fsync는 왜 서버를 느리게 만들까? 디스크 쓰기 보장의 원리
리눅스 서버에서 디스크 쓰기 속도가 충분히 빠른데도 데이터베이스 응답이 느려지거나 파일 저장 작업이 지연되는 경우가 있다. CPU 사용률은 높지 않고 메모리에도 여유가 있지만, 특정 요청만 유독 오래 걸리는 현상이다.
이러한 문제는 단순한 디스크 성능 부족이 아니라 fsync 시스템 호출로 인한 동기식 쓰기 지연에서 발생할 수 있다.
리눅스는 일반적으로 데이터를 저장할 때 디스크에 즉시 기록하지 않고 메모리의 Page Cache를 활용한다. 하지만 데이터베이스 트랜잭션이나 중요한 파일 저장 작업에서는 갑작스러운 전원 장애에도 데이터를 보존해야 하므로 실제 저장장치에 기록이 완료되도록 요구한다.
이때 사용하는 대표적인 시스템 호출이 fsync다.
fsync는 데이터의 안정성을 높이는 중요한 기능이지만, 호출 빈도가 높거나 저장장치의 지연 시간이 길어지면 서버 전체의 응답 성능에 영향을 줄 수 있다.
이번 글에서는 fsync가 서버 성능을 저하시키는 원인부터 리눅스 커널의 동작 원리, 실제 장애 증상, 분석 명령어와 해결 방법까지 살펴본다.
fsync가 서버 성능 저하를 발생시키는 원인
일반적인 파일 쓰기 작업은 애플리케이션이 write() 시스템 호출을 실행하면서 시작된다.
리눅스의 Buffered I/O 환경에서는 write()가 호출되더라도 데이터가 반드시 물리적인 저장장치에 즉시 기록되는 것은 아니다.
운영체제는 데이터를 Page Cache에 저장하고, 이후 커널의 Writeback 작업을 통해 실제 디스크로 전달할 수 있다.
이 구조는 여러 쓰기 작업을 모아 처리할 수 있어 성능 측면에서 유리하다.
문제는 데이터의 영구 저장을 보장해야 하는 상황이다.
예를 들어 데이터베이스에서 결제 정보가 저장되었다고 응답했는데, 직후 서버 전원이 차단되어 해당 데이터가 사라진다면 심각한 문제가 발생한다.
이러한 위험을 줄이기 위해 애플리케이션은 fsync를 호출한다.
fsync는 파일의 변경된 데이터와 필요한 메타데이터가 저장장치에 안전하게 반영되도록 동기화하며, 장치가 완료를 보고할 때까지 호출한 스레드를 대기시킨다.
따라서 fsync에 소요되는 시간이 길어지면 해당 작업이 끝나기를 기다리는 애플리케이션의 처리 시간도 증가한다.
특히 다음과 같은 환경에서 성능 문제가 나타나기 쉽다.
- 데이터베이스가 트랜잭션마다 로그를 동기화하는 환경
- 작은 파일을 자주 생성하고 저장하는 애플리케이션
- 디스크 쓰기 지연 시간이 불규칙하게 증가하는 서버
- 다수의 프로세스가 동일한 저장장치에 쓰기 작업을 집중시키는 환경
중요한 점은 fsync 자체가 비정상적인 기능은 아니라는 것이다.
데이터를 안전하게 저장하기 위해 필요한 대기 시간이 서버의 응답 지연으로 나타나는 것이 핵심이다.
fsync의 동작 원리와 Linux Kernel의 쓰기 처리 과정
fsync를 이해하려면 일반적인 write()와 실제 디스크 기록 사이의 차이를 알아야 한다.
다음은 Buffered I/O를 사용하는 파일 쓰기 과정의 단순화된 구조다.
Linux 파일 쓰기와 fsync 동작 흐름
Application
write() 시스템 호출
Page Cache
Dirty Page에 변경 데이터 보관
비동기 Writeback
커널이 쓰기 시점 결정
fsync()
필요한 기록 완료까지 대기
Filesystem / Block Layer
쓰기 요청 및 필요 시 캐시 Flush
Storage Device (SSD / HDD)
영속적 저장 완료 보고
개념도: 파일시스템과 저장장치에 따라 내부 처리 순서는 달라질 수 있다.
Page Cache와 Dirty Page
애플리케이션이 파일에 데이터를 기록하면 변경된 메모리 페이지는 Dirty Page 상태가 될 수 있다.
Dirty Page는 메모리의 데이터가 변경되었지만 해당 변경 내용이 아직 영속적 저장소에 완전히 반영되지 않은 상태를 의미한다.
커널은 Writeback 작업으로 이러한 데이터를 저장장치에 기록한다.
The Linux Kernel documentation
일반적인 비동기 쓰기에서는 애플리케이션이 이 과정을 매번 기다릴 필요가 없다.
반면 fsync는 해당 파일의 변경 내용이 저장장치에 반영되도록 요구한다.
저장장치 Cache Flush
SSD나 HDD에도 쓰기 캐시가 존재할 수 있다.
따라서 운영체제에서 저장장치로 데이터를 전달했다는 사실만으로 전원 장애에 대비한 영속성이 확보되었다고 볼 수는 없다.
리눅스는 저장장치의 특성에 따라 Cache Flush 또는 FUA(Force Unit Access) 등의 기능을 사용해 필요한 데이터가 비휘발성 저장소에 도달하도록 처리한다.
이 과정에서 저장장치의 응답 시간이 길어지면 fsync 호출도 늦게 종료될 수 있다.
fsync와 fdatasync의 차이
| 구분 | fsync() | fdatasync() |
|---|---|---|
| 파일 데이터 | 동기화 | 동기화 |
| 메타데이터 | 변경 내용 동기화 | 데이터 복구에 필요한 정보 중심 |
| 목적 | 파일 데이터와 메타데이터의 영속성 | 필요한 데이터의 영속성 |
| 성능 | 메타데이터 작업이 추가될 수 있음 | 일부 환경에서 디스크 작업 감소 가능 |
fdatasync가 항상 빠른 것은 아니다. 파일 크기 변경처럼 데이터를 다시 읽기 위해 필요한 메타데이터는 fdatasync에서도 동기화해야 한다.
fsync 지연이 발생할 때 나타나는 실제 서버 증상
fsync로 인한 성능 문제는 단순히 디스크 사용률이 높아지는 형태로만 나타나지 않는다.
대표적인 증상은 다음과 같다.
데이터베이스 트랜잭션 응답 지연
데이터베이스가 트랜잭션 로그를 영속적으로 저장한 뒤 완료를 반환하도록 설정되어 있다면, 로그 동기화 지연이 트랜잭션 처리 시간에 영향을 준다.
특히 짧은 트랜잭션이 빈번하게 발생하는 서비스에서 이러한 대기 시간이 누적될 수 있다.
CPU 사용률은 낮지만 요청이 느림
애플리케이션 스레드가 fsync 완료를 기다리는 동안 CPU에서 계산 작업을 수행하지 않을 수 있다.
따라서 CPU 사용률이 낮다는 이유만으로 서버에 병목이 없다고 판단해서는 안 된다.
디스크 평균 처리량은 낮지만 응답 시간이 불규칙함
fsync 지연은 처리량보다 개별 쓰기 작업의 지연 시간에 민감하다.
대량의 데이터를 연속으로 쓰는 작업은 빠르게 처리되더라도 작은 동기식 쓰기가 반복되면 요청 처리 성능이 떨어질 수 있다.
서버 응답 시간의 순간적인 증가
평소 정상적으로 동작하던 API에서 간헐적으로 응답 시간이 크게 증가할 수 있다.
이때 애플리케이션 로그, 데이터베이스 로그, 디스크 지표를 같은 시간대에 비교하면 병목의 발생 위치를 찾는 데 도움이 된다.
Linux에서 fsync 지연을 분석하는 방법
fsync 문제가 의심된다면 시스템 전체의 I/O 지표를 확인한 뒤 실제 시스템 호출 시간을 측정하는 순서로 접근하는 것이 좋다.
iostat으로 디스크 지연 시간 확인
iostat -xz 1
iostat은 저장장치의 I/O 처리 상태를 확인하는 명령어다.
-x는 확장 통계를 표시하고 -z는 활동이 없는 장치를 숨기며, 1은 1초 간격으로 통계를 출력한다.
주요 지표는 다음과 같다.
| 지표 | 의미 | 분석 포인트 |
|---|---|---|
| w_await | 쓰기 요청의 평균 대기 및 처리 시간(ms) | 평소보다 크게 증가하는지 |
| aqu-sz | 평균 I/O 대기열 길이 | 요청이 누적되는지 |
| %util | 관측 구간 중 장치가 I/O를 처리한 시간 비율 | 장치 특성을 고려해 해석 |
| wkB/s | 초당 쓰기 데이터량 | 처리량과 지연 시간 비교 |
예를 들어 wkB/s는 크게 증가하지 않았는데 w_await가 급격히 상승한다면 쓰기 지연 문제를 의심할 수 있다.
다만 w_await는 fsync 호출 시간 자체가 아니며, %util 역시 최신 병렬 SSD에서 단독으로 포화 여부를 판정하기에는 한계가 있다.
strace로 fsync 호출 시간 측정
sudo strace -tt -T \
-e trace=fsync,fdatasync \
-p <PID>
실행 중인 프로세스가 fsync와 fdatasync를 얼마나 자주 호출하며, 각 호출에 얼마의 시간이 소요되는지 확인하는 명령어다.
-tt: 호출 시각 표시-T: 시스템 호출 수행 시간 표시-e trace: 추적할 시스템 호출 제한-p: 분석할 프로세스 PID 지정
다음은 형식을 설명하기 위한 가상 출력 예시다.
21:10:01.123456 fsync(8) = 0 <0.004521>
21:10:01.145678 fsync(8) = 0 <0.037814>
21:10:01.223456 fdatasync(9) = 0 <0.006103>
두 번째 fsync 호출의 소요 시간은 약 37.8ms다.
같은 프로세스에서 이러한 지연이 반복되는지, 서비스 응답 지연 시간과 일치하는지를 확인해야 한다.
단일 호출이 오래 걸렸다고 해서 fsync가 전체 성능 문제의 원인이라고 확정할 수는 없다.
또한 strace는 프로세스 동작에 영향을 줄 수 있으므로 운영 환경에서는 짧은 시간 동안 제한적으로 사용해야 한다.
/proc/meminfo에서 Dirty Page 확인
grep -E 'Dirty|Writeback' /proc/meminfo
이 명령어는 메모리에서 기록 대기 중이거나 Writeback 진행 중인 데이터의 규모를 확인한다.
예시 출력은 다음과 같다.
Dirty: 245760 kB
Writeback: 16384 kB
Dirty는 아직 기록이 완료되지 않은 변경 데이터의 규모를, Writeback은 현재 기록 작업이 진행 중인 데이터의 규모를 나타낸다.
이 수치가 지속적으로 증가한다면 저장장치가 쓰기 작업을 충분히 소화하지 못하는지 조사할 필요가 있다.
하지만 Dirty Page가 많다는 사실만으로 fsync 지연이 확정되는 것은 아니다.
반드시 시스템 호출 시간과 디스크 지표를 함께 분석해야 한다.
pidstat으로 프로세스별 I/O 확인
pidstat -d 1
프로세스별 읽기·쓰기 활동을 확인하는 명령어다.
특정 데이터베이스나 애플리케이션이 많은 쓰기 요청을 발생시키는지 파악할 수 있다.
다만 이 명령어는 fsync 호출 횟수나 지연 시간을 직접 보여주지는 않는다.
실제 운영 환경에서의 장애 판단
실제 분석에서는 다음 흐름으로 원인을 좁혀갈 수 있다.
애플리케이션 API 응답 시간이 증가한 시점에 iostat의 w_await가 함께 상승했다고 가정하자.
이후 해당 애플리케이션에서 fsync 호출 시간이 증가한 사실까지 확인했다면 동기식 쓰기 대기가 응답 지연에 기여했을 가능성이 커진다.
반대로 디스크 지연은 증가했지만 애플리케이션에서 fsync를 거의 호출하지 않는다면 다른 I/O 작업이나 파일시스템 병목을 조사해야 한다.
분석의 핵심은 fsync 호출 여부가 아니라, 호출 지연과 서비스 응답 지연이 실제로 연결되어 있는지 확인하는 것이다.
fsync로 인한 서버 성능 저하 해결 방법
fsync 문제를 해결할 때 가장 주의해야 할 부분은 데이터 안정성과 처리 성능 사이의 관계다.
단순히 fsync 호출을 제거하면 일시적으로 성능이 개선될 수 있지만, 장애 발생 시 데이터 손실 위험이 증가할 수 있다.
따라서 원인에 맞는 해결 방법을 선택해야 한다.
불필요하게 반복되는 동기식 쓰기 줄이기
애플리케이션이 작은 데이터를 기록할 때마다 fsync를 호출하면 저장장치 동기화 비용이 반복된다.
반드시 개별 동기화가 필요하지 않은 데이터라면 여러 쓰기 작업을 하나의 배치로 묶는 방식을 검토할 수 있다.
예를 들어 중요도가 낮은 통계성 데이터나 임시 로그는 배치 처리를 적용할 수 있다.
반면 결제 정보, 주문 상태, 중요한 트랜잭션 로그는 성능만을 위해 저장 보장 수준을 낮춰서는 안 된다.
데이터베이스의 로그 동기화 설정 점검
데이터베이스는 트랜잭션 로그의 기록 및 동기화 정책에 따라 성능 특성이 달라질 수 있다.
PostgreSQL에서는 synchronous_commit과 같은 설정이 트랜잭션 완료 시 로그의 영속성 보장 수준에 영향을 준다.
다만 해당 옵션을 비활성화하면 장애 상황에서 최근 완료된 트랜잭션이 손실될 가능성이 생긴다.
따라서 설정 변경 전에는 데이터 복구 요구사항과 장애 시 허용 가능한 손실 범위를 반드시 검토해야 한다.
저장장치의 지연 시간 개선
fsync는 실제 저장장치의 응답 특성에 영향을 받기 때문에 저장장치의 쓰기 지연을 줄이는 것도 중요한 해결 방법이다.
특히 동기식 쓰기가 많은 환경에서는 다음 항목을 비교할 필요가 있다.
- 저장장치의 동기식 쓰기 지연 시간
- 쓰기 부하가 증가했을 때 지연 시간의 변화
- 저장장치의 캐시 및 전원 보호 기능
- 가상화 환경의 스토리지 성능 제한
- 여러 서비스가 동일한 장치를 공유하는지 여부
SSD라고 해서 모든 동기식 쓰기 작업이 동일하게 빠른 것은 아니다.
일반적인 순차 쓰기 처리량이 높더라도 fsync 지연 시간은 장치와 파일시스템, 운영 환경에 따라 달라질 수 있다.
파일 저장과 디렉터리 동기화 구분
fsync는 해당 파일의 데이터를 동기화하지만 새로 생성하거나 이름을 변경한 파일의 디렉터리 엔트리까지 항상 보장하지는 않는다.
파일 생성 및 이름 변경의 영속성이 중요한 애플리케이션에서는 디렉터리에 대한 별도의 fsync가 필요할 수 있다.
특히 임시 파일을 만든 뒤 rename()으로 교체하는 방식에서는 파일 내용과 디렉터리 변경의 저장 보장을 구분해야 한다.
이는 단순한 성능 튜닝이 아니라 장애 발생 시 파일의 일관성과 복구 가능성에 영향을 주는 설계 문제다.
fsync 최적화 전후의 성능 비교
최적화 후에는 단순한 처리량 증가만 확인해서는 안 된다.
API 응답 시간, 트랜잭션 처리량, 쓰기 지연 시간, 오류 발생 여부를 함께 비교해야 한다.
특히 평균 응답 시간뿐 아니라 p95, p99 응답 시간의 변화도 중요하다.
평균 성능이 개선되었더라도 일부 요청의 지연 시간이 여전히 크게 증가한다면 실제 서비스 품질은 충분히 개선되지 않았을 수 있다.
정리
fsync는 리눅스에서 파일 데이터를 안전하게 저장하기 위한 중요한 시스템 호출이다.
일반적인 write()는 Page Cache를 이용해 빠르게 완료될 수 있지만, fsync는 저장장치에 대한 동기화가 완료되기를 기다리기 때문에 추가적인 지연이 발생할 수 있다.
이러한 지연은 데이터베이스 트랜잭션, 파일 저장 작업, 동기식 로그 기록이 많은 서버에서 응답 성능 저하로 이어질 수 있다.
따라서 fsync 관련 장애를 분석할 때는 iostat으로 디스크 지연을 확인하고, strace로 실제 시스템 호출 시간을 측정하며, Dirty Page와 프로세스별 I/O 활동까지 함께 조사해야 한다.
또한 fsync는 데이터 안정성과 직접 연결되므로 단순히 호출 횟수를 줄이거나 동기화 설정을 해제하는 방법은 신중해야 한다.
서버 성능을 개선하는 핵심은 fsync를 무조건 제거하는 것이 아니라, 데이터의 영속성 요구사항을 유지하면서 불필요한 동기화 비용과 저장장치 지연을 줄이는 데 있다.




댓글 0
첫 댓글을 남겨보세요.