Backend/Database

[MySQL] Direct I/O

chocoji 2024. 2. 29. 20:58
Real MySQL 8.0 - 11.8 쿼리 성능 테스트 부분에서 Direct I/O라는 용어가 나왔는데, MySQL 튜닝의 중요한 요소인 것 같아서 정리하게 되었다.

Direct I/O 란?

운영체제의 파일 시스템 캐시를 사용하지 않고 데이터를 직접 디스크에 읽거나 쓰는 방법

  • 일반적으로 운영체제는 파일을 읽을 때 파일 시스템 캐시를 사용하여 읽기 속도를 향상시킨다. (`Buffered I/O`)
    • 파일이 한 번 읽힌 후, 해당 데이터는 캐시에 저장되어 다음에 동일한 데이터에 액세스할 때 디스크로부터 읽는 대신 캐시에서 데이터를 제공한다.
    • 즉, 캐시 히트했을 경우의 데이터 읽기 속도가 빨라진다.
  • 그런데 InnoDB는 이미 `버퍼 풀`이라는 훨씬 고도화된 메모리 영역을 가지고 있기 때문에 동일한 데이터를 버퍼 풀과 운영체제의 캐시에 이중으로 저장해서 메모리를 낭비할 필요가 없다. ➡️ `Double Buffering`
    • InnoDB의 버퍼 풀 캐시는 운영체제의 캐시보다 더 체계적이고 효율적이기 때문에 운영체제의 캐시가 별로 도움이 되지 않는다.
    • 운영체제의 캐시는 DB 작업 외에 작업 시 언제든지 지워질 수 있다.
  • 파일 캐시를 경유하지 않고 버퍼 풀에서 디스크로 직접 액세스해서 불필요한 지연(2중 캐시 처리)이 발생하지 않도록 하는 방식이 바로 `Direct I/O`이다.
    • 메모리 효율적 사용: 복사 과정 감소
    • 캐시 관리 단순화: 파일 시스템 캐시를 사용하지 않기 때문에 캐시 관리의 복잡성을 피할 수 있음
🐈 참고
- `Double Buffering`: 메모리상에서 buffer pool과 OS Cache에 중복으로 데이터를 저장하는 것
- `Double Write Buffer`: 데이터의 안전성을 위해 디스크상에 중복된 데이터를 이중으로 저장하는 것

 

innodb_flush_method

InnoDB 스토리지 엔진에서 Flush 작업을 어떻게 수행할 것인지를 결정하는 설정 옵션

  • `Flush`: 데이터를 일시적인 저장소(캐시나 버퍼)에서 영구적인 저장소(디스크)로 옮기는 작업 (`In-Memory` ➡️ `On-Disk`)
  • 운영체제는 디스크에 데이터를 쓰는 작업을 운영체제의 버퍼로 기록하는 작업버퍼의 내용을 디스크로 복사하는 두 단계의 작업을 어떻게 조합하느냐에 따라 3가지 관점으로 쓰기 방식을 분류한다.
    • 동기 vs 비동기
      • 동기(Sync): 두 단계의 작업을 동시에 같이 실행하는 방식
      • 비동기(Async): 두 단계 작업을 각각 다른 시점에 실행하는 방식 ➡️ 직접적으로 사용되지는 않음
    • 메타 데이터(파일의 속성, 권한 등) 반영 여부
      • `fsync`: 데이터와 파일의 메타데이터를 한꺼번에 변경하는 방식
      • `fdatasync`: 파일의 메타정보는 무시하고 순수하게 사용자의 데이터만 변경하는 방식
    • Direct I/O 사용 여부

 

option 종류

⚠️ 더 많은 옵션이 있지만 문제가 있거나 잘 사용하지 않는 옵션은 제외함

fsync

기본값. 데이터나 로그 파일을 열고 쓸 때 `fsync()`를 사용함

  • 파일의 데이터와 메타데이터를 모두 디스크에 동기화(sync)

 

+) fdatasync

`fdatasync()` 시스템 콜을 지원하는 플랫폼에서 `fsync()` 대신에 `fdatasync()`을 사용하도록 허용함

  • 파일의 데이터만 디스크에 동기화하고, 메타 데이터는 필요한 경우에만 디스크에 write
  • `fdatasync`는 데이터 안정성을 보장하는데 필요한 최소한의 동기화를 수행하기 때문에 `fsync`보다 약간의 성능 향상을 얻을 수 있음

 

O_DIRECT

데이터 파일을 열 때는 `O_DIRECT`, 데이터 파일과 로그 파일을 쓸 때는 `fsync()`를 사용함

 

https://nomadlee.com/mysql-database-commit-architecture/

  • 대용량 데이터를 읽을 때 디스크에서 직접 가져오면 읽기 성능을 향상시킬 수 있음
  • 데이터를 기록할 때는 안전하게 디스크에 반영하기 위해 `fsync()`를 사용함 

 

그런데 왜 Direct I/O가 기본값이 아닐까?

  • Buffered I/O 방식과 비교했을 때 메모리도 효율적으로 사용할 수 있고
  • 교재(Real MySQL 8.0) 에서도 InnoDB가 일반적으로 Direct I/O 방식을 사용한다고 해서

`innodb_flush_method`의 기본값도 Direct I/O 방식일 줄 알았다

더보기

InnoDB는 일반적으로 파일 시스템이나 캐시나 버퍼를 거치지 않는 Direct I/O 방식을 사용하므로 운영체제의 캐시가 그다지 큰 영향을 미치지 않는다. - Real MySQL 8.0 - 11.8.1.1

 +) MySQL의 기본 스토리지 엔진은 InnoDB

 

하지만 (위에서 언급했듯이) 기본값은 `fsync`이다.

https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_flush_method

 

아마 다음의 이유가 아닐까 생각한다.

  • `fsync()`를 사용했을 때의 데이터 안정성과 일관성
  • Direct I/O를 사용했을 때 오히려 성능이 떨어질 수 있음

 

fdatasync vs O_DIRECT  ❓

https://www.mimul.com/blog/sysvar_innodb_flush_method/

 

MySQL innodb_flush_method 튜닝 포인트 | Mimul Tech log

MySQL InnoDB 스토리지 엔진을 사용하면 매개 변수 innodb_flush_method가 있는데 이 설정 값의 의미와 테스트를 통해 튜닝 포인트를 검토.

www.mimul.com

https://www.mimul.com/blog/sysvar_innodb_flush_method/

- 128MB 이전에는 `fdatasync` 방식이 성능이 더 높고
- 128MB 이후부터는 `O_DIRECT` 방식의 성능이 더 높게 나왔다.
  • 앞쪽으로 `fdatasync` 방식의 성능이 더 높은 것은 InnoDB 버퍼 풀에 없는 데이터가 OS 파일 캐시에 있어 디스크 I/O를 줄일 수 있기 때문에 성능 이점을 얻은 것 같다. ➡️ 즉, OS 파일 캐시가 InnoDB 버퍼 풀에 대한 보조 캐시 역할 수행했기 때문!
  • `O_DIRECT`가 `fdatasync`에 비해 동등한 성능 이상을 발휘하기 위해서는 다음 조건 중 하나를 충족해야 한다.
    • 모든 데이터가 InnoDB 버퍼 풀에 들어가거나
    • 물리 메모리의 절반 정도를 InnoDB 버퍼 풀에 할당하는 경우
    • RAID 컨트롤러에 Write Cache가 붙어있고, 그 Write Cache가 Battery Backed Unit(BBU)이 있을 경우

➡️ 즉, 디스크 I/O 횟수가 많지 않은 경우 성능 향상 효과가 있다.

 

정리

디스크 I/O가 MySQL 성능에 큰 영향을 주는 만큼, 상황에 맞게 적절한 파일 I/O 방식을 선택하는 것이 중요해 보인다.

버퍼 풀의 크기와 데이터의 안정성의 중요도 등 다양한 요소가 선택에 영향을 주는 만큼, 각 방식의 특징을 잘 알아두고 직접 테스트를 해서 설정값을 조절하는 방법을 잘 익혀둬야겠다!

 

Ref

https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_flush_method

https://nomadlee.com/mysql-database-commit-architecture/

https://systemv.tistory.com/49

https://dus815.tistory.com/entry/Mysql-InnoDB-%EC%97%90%EC%84%9C-OS-Cache-%EC%99%80%EC%9D%98-%EA%B4%80%EA%B3%84

https://www.mimul.com/blog/sysvar_innodb_flush_method/

https://minsql.com/mysql/mysql-innodb_flush_method/