Last Updated on 12월 21, 2022 by Jade(정현호)
안녕하세요
이번에는 MySQL 의 InnoDB 스토리지 엔진의 Double Write Buffer(이중 쓰기 버퍼) 기능에 대해서 확인 해보도록 하겠습니다.
Real MySQL 8.0 책을 정리 하였으며 MySQL Document 를 참조 하였으며 아래 포스팅에서 어이지는 글 입니다.
Double Write Buffer
InnoDB 스토리지 엔진의 Redo Log(리두 로그)는 리두 로그 공간의 낭비를 막고 빠르게 Redo Log 에 쓰기 위해 페이지의 변경된 내용만 기록 하게 됩니다
(Oracle 에서는 이런 내용을 Redo 에 Change Vector 를 생성으로 표현하고 있으며 블록 변경의 최소 단위를 의미)
이로 인해 InnoDB의 스토리지 엔진에서 Dirty Page를 디스크 파일로 Flush 할때 페이지중 일부만 Disk에 기록된 상태에서 장애 발생시 그 페이지의 내용은 복구 할 수 없을 수도 있습니다.
이렇게 페이지가 일부만 기록 되는 현상을 파셜 페이지(Partial Page) 또는 톤 페이지(Torn Page) 라고 하는데, 이런 현상은 하드웨어 오작동이나 시스템의 비정상 종료 등으로 발생할 수 있습니다.
InnoDB 스토리지 엔진에서는 이 같은 문제를 막기 위해 Double-Write 기법을 이용하게 됩니다.
Double Write Buffer(이중 쓰기 버퍼)는 InnoDB가 InnoDB 데이터 파일의 적절한 위치에 페이지를 쓰기 전에 버퍼 풀에서 플러시된 페이지를 쓰는 저장 영역 입니다
페이지 쓰기 도중에 운영 체제나 스토리지 하위 시스템 장애나 또는 예기치 않은 mysqld 프로세스 종료가 있는 경우 InnoDB 는 Crash Recovery 를 하게 되며 복구 중에 필요한 페이지를 Double Write Buffer(이중 쓰기 버퍼)에서 복사본을 찾을 수 있습니다.
데이터가 두 번 기록 되지만 Double Write Buffer(이중 쓰기 버퍼)는 두 배의 I/O 오버헤드나 두 배의 I/O 작업을 필요로 하지 않습니다.
내려쓸 때 순차적, Sequential I/O로 처리 되므로 두배가 되지는 않습니다.
다만, 테스트 해보면 파라미터 값에 따라서 수행 시간 차이는 있으나 데이터의 입력 건수나 방식에 따라서 차이점을 느끼지 못할 수도 있으며, 해당 내용은 아래 innodb_doublewrite 시스템 변수 설명에서 내용을 기록해 두었습니다.
MySQL 8.0.20 이전에는 이중 쓰기 버퍼 저장 영역이 InnoDB 시스템 테이블스페이스에 위치해 있었습니다. MySQL 8.0.20부터 별도의 파일(테이블스페이스)에 저장되고 저장되는 위치도 별도로 설정 할 수 있습니다.
-rw-r-----. 1 mysql mysql 196608 Nov 8 01:26 '#ib_16384_0.dblwr' -rw-r-----. 1 mysql mysql 8585216 Nov 8 00:11 '#ib_16384_1.dblwr'
관련 파라미터 : innodb_data_home_dir (8.0.20)
아래 그림은 Double Write Buffer(이중 쓰기 버퍼) 작동 하는 방식을 표현한 것 입니다.
A~D까지의 더티 페이지(Dirty Page) 를 디스크로 플러시 한다고 할때 InnoDB 스토리지 엔진은 실제 데이터 파일에 변경 내용을 기록하기 전에 A~D까지의 더티 페이지를 우선 묶어서 한번의 디스크 쓰기로 시스템 테이블스페이스(8.0.20 부터는 별개의 파일) 의 DoubleWrite 버퍼에 기록하게 됩니다.
그리고 InnoDB 스토리지 엔진은 각 더티 페이지를 각각 파일의 적당한 위치에 하나씩 랜덤으로 쓰기를 실행하게 됩니다.
이렇게 시스템 테이블스페이스의 DoubleWrite 버퍼 공간에 기록된 변경 내용은 실제 데이터 파일 'A'~'D' 더티 페이지가 정상적으로 기록되면 더이상 필요가 없어지게 됩니다.
Double Write Buffer(이중 쓰기 버퍼) 의 목적은 실제 데이터 파일의 쓰기가 중간에 실패할 때 복구에 목적이 있습니다.
만약 'A' 와 'B' 페이지는 정상적으로 기록 되었지만 'C' 페이지가 기록되는 도중에 운영체제가 비정상적으로 종료 되었다고 가정해보도록 하겠습니다.
그러면 InnoDB 스토리지 엔진은 재시작 될 때 항상 DoubleWrite 버퍼의 내용과 데이터 파일의 페이지들을 모두 비교해서 다른 내용을 담고 있는 페이지가 있으면 Double Write Buffer(이중 쓰기 버퍼) 내용을 데이터 파일의 페이지로 복사를 하게 됩니다.
[참고] AWS RDS for MySQL 8.0.30 버전부터 Double Write Buffer(이중 쓰기 버퍼) 기능을 미사용 하는 것과 연관하여, 쓰기 처리량 최대 2배 증가 가능한 RDS Optimized Writes 기능이 도입 되었습니다.(현재는 일부 리전만 지원)
관련 MySQL 변수
Double Write Buffer(이중 쓰기 버퍼) 구성을 위해 다음 변수가 제공됩니다.
innodb_doublewrite
Double Write 버퍼를 사용 할지 여부를 결정 하는 파라미터로 기본적으로 활성화되어 있습니다.
8.0.29 버전까지 선택할 수 있는 파라미터 값은 ON 또는 OFF 이며, Dynamic 파라미터가 아니기 때문에 파라미터 변경시 재시작이 필요 합니다.(Dynamic=No)
8.0.30 버전 부터는 선택 할 수 있는 파라미터 값이 DETECT_AND_RECOVER 와 DETECT_ONLY 2가지 더 추가 되었습니다.
DETECT_AND_RECOVER 설정은 ON 설정과 동일 하며, DETECT_ONLY 설정을 사용하면 메타 데이터만 이중 쓰기 버퍼에 기록 합니다.
데이터베이스 페이지 콘텐츠는 이중 쓰기 버퍼에 기록되지 않으며, 불완전한 페이지 의 수정을 위한 doublewrite buffe는 복구에 사용되지 않으며 이 설정은 불완전한(incomplete) 페이지 쓰기를 감지하기 위한 것입니다.
또한 파라미터 값 일부에서는 Dynamic(동적 변경) 으로 변경이 가능해 졌습니다.
ON, DETECT_AND_RECOVER 및 DETECT_ONLY 사이 에서는 동적 변경을 할 수 있습니다.
다만 innodb_doublewrite 사용에 대한 값과 OFF 로 변경 간에는 동적 변경이 지원하지 않습니다.
즉, innodb_doublewrite 를 ON, DETECT_AND_RECOVER , DETECT_ONLY 사용하던 중에 OFF 로 동적 변경이 불가하며, 반대로 OFF 로 사용하다가 기능의 활성화 값인 ON, DETECT_AND_RECOVER , DETECT_ONLY 으로 변경은 동적으로 변경이 불가능 합니다.
8.0.30 버전 부터 동적으로 변경이 가능한 값은 ON, DETECT_AND_RECOVER , DETECT_ONLY 3개 사이에서만 가능 합니다.
(8.0.29 버전까지 기준으로) Double Write Buffer를 비활성화하려면 innodb_doublewrite=0 으로 my.cnf 에 설정 하거나 --skip-innodb-doublewrite 를 사용하여 MySQL 서버를 시작하면 됩나다.
Double Write Buffer 는 원자(Atomic) 쓰기를 지원하는 Fusion-io 장치에 있는 경우 Double Write Buffer는 자동으로 비활성화되고 데이터 파일 쓰기에 Fusion-io 원자(Atomic) writes 가 대신 사용하여 수행됩니다.
innodb_doublewrite 설정은 전역적으로 적용 됩니다 그래서 이중 쓰기 버퍼가 비활성화되면 Fusion-io 하드웨어에 없는 파일을 포함하여 모든 데이터 파일에 대해 비활성화됩니다.
이 기능은 Fusion-io 하드웨어에서만 지원되며 Linux의 Fusion-io NVMFS에서만 활성화됩니다. 이 기능을 최대한 활용하려면 innodb_flush_method 설정을 O_DIRECT 로 권장합니다.
또는 ZFS와 같이 File System 차원에서 원자(Atomic) 단위의 Write를 지원하게 됩니다.
파라미터 별 수행 시간 테스트
• 테스트 진행 버전 : 8.0.31
• 입력 건수 : 20,000,000
• 입력 방식 : sysbench 이용
파라미터 | DETECT_AND_RECOVER | DETECT_ONLY | OFF |
수행시간(Sec) | 380 | 369 | 359 |
테스트 환경에서는 2천만건 입력에 관한 수행 시간(소요시간) 으로 DETECT_AND_RECOVER 파라미터 수행 시간 대비 DETECT_ONLY 는 약 2.89% 정도 감소 하였고, DETECT_AND_RECOVER 파라미터 수행 시간 대비 OFF 는 약 5.53% 정도 감소 하였습니다.
테스트 환경(Spec 등) 과 데이터 건수 와 입력 방식에 따라서 수행 시간의 차이는 있으므로, 참고 정보 정도로 봐주시면 됩니다.
innodb_doublewrite_dir
MySQL 8.0.20 부터 도입된 파라미터이며, InnoDB가 Double Write Buffer(이중 쓰기 버퍼) 파일을 생성하는 디렉토리를 정의합니다.
(innodb_doublewrite_dir=/경로명)
디렉터리를 지정하지 않으면 innodb_data_home_dir 디렉터리에 이중 쓰기 파일이 생성되며, 지정하지 않으면 기본적으로 데이터 디렉터리가 사용됩니다.
스키마 명과의 충돌을 피하기 위해 해시 기호 '#'이 지정된 디렉토리 이름 앞에 자동으로 붙습니다(접두사). 단, '.', '#'. 또는 '/' 접두사가 디렉토리 이름에 명시적으로 지정되어 있으면 해시 기호 '#'이 디렉토리 이름에 접두어가 붙지 않습니다.
Double Write Buffer(이중 쓰기 버퍼)는 빠른 저장 매체에 저장하는 것이 좋으며, SSD 또는 NVMe SSD에 저장하는 것이 좋을 것 입니다.
innodb_doublewrite_files
Double Write Buffer 파일의 수를 정의합니다.
기본 값으로 innodb_buffer_pool_instances X 2 개로 각 buffer pool instance 마다 2개의 Double Write Buffer 를 생성하게 됩니다(최소값 2)
(flush list doublewrite file 와 LRU list doublewrite file)
"flush list doublewrite file" 은 buffer pool 의 flush list 에 있는 플러시된 페이지를 저장하며, "flush list doublewrite file"의 기본 크기는 InnoDB page size * doublewrite page bytes 입니다.
"LRU list doublewrite file" 은 buffer pool 의 LRU 목록에서 플러시된 페이지를 저장 합니다 단일 페이지 플러시를 위한 슬롯도 포함합니다
기본 크기는 InnoDB page size * (doublewrite pages + (512 / buffer pool instances 수)) 이며, 여기서 512는 단일 페이지 플러시를 위해 예약된 총 슬롯 수입니다.
최소한 두 개의 Double Write 파일이 있있으며, Double Write 파일의 최대 수는 buffer pool instances 수의 2배입니다.
(buffer pool instances의 수는 innodb_buffer_pool_instances 파라미터에 의해 제어됩니다)
위에서 확인 한 내용과 같이 Double Write 파일 이름의 형식은 #ib_page_size_file_number.dblwr 가 됩니다
InnoDB 페이지 크기가 16KB이고 단일 버퍼 풀이 있는 MySQL 인스턴스에 대해 다음 이중 쓰기 파일이 생성됩니다.
포스팅 테스트 시스템에서는 버퍼 풀 수는 1개 입니다.
mysql> show variables like '%innodb_buffer_pool_instances%'; +------------------------------+-------+ | Variable_name | Value | +------------------------------+-------+ | innodb_buffer_pool_instances | 1 | +------------------------------+-------+
포스팅 테스트 시스템에서는 innodb_doublewrite_files 는 기본값(2) 으로 설정 중이며 그에 따라 파일이 2개가 생성되어 있는 상태 입니다.
-rw-r-----. 1 mysql mysql 196608 Nov 8 01:26 '#ib_16384_0.dblwr' -rw-r-----. 1 mysql mysql 8585216 Nov 8 00:11 '#ib_16384_1.dblwr'
innodb_doublewrite_pages
MySQL 8.0.20 에 소개되는 innodb_doublewrite_pages 는 스레드당 최대 이중 쓰기 페이지 수를 제어합니다.
값을 지정하지 않으면 기본값으로 innodb_write_io_threads 값으로 설정 되며, innodb_write_io_threads 기본값은 4 입니다.
innodb_doublewrite_batch_size
innodb_doublewrite_batch_size 는 MySQL 8.0.20 에 도입 되었으며,기본값은 0 이고 배치에서 사용할 Double Write 페이지 수를 제어합니다. 이 변수는 고급 성능 조정을 위한 것입니다
Conclusion
Double Write Buffer(이중 쓰기 버퍼) 는 데이터의 안정성을 위해 자주 사용 됩니다.
HDD(하드디스크)처럼 자기 원판(Platter)이 회전하는 저장 시스템에서는 어자피 한번의 순차 디스크 쓰기를 하는 것이기 때문에 별로 부담이 되지 않지만 SSD처럼 랜덤 IO나 순차 IO의 비용이 비슷한 저장 시스템에서는 부담스러운 작업일 수 있습니다.
하지만 데이터의 무결성이 중요한 서비스에서는 Double Write Buffer(이중 쓰기 버퍼)의 활성화를 고려 하는 것이 좋을 것 같습니다.
MySQL 에서는 대부분이 복제(Replication) 을 하여 사용하기 때문에 데이터의 무결성을 위해서 Master 인스턴스에서는 Double Write Buffer 를 활성화하고, Slave(Replica) 인스턴스에서는 Double Write Buffer를 비활성화하는 구성도 고려 해 볼 수 있을 것 같습니다.
데이터의 무결성이 덜 중요한 시스템이면서 성능이 우선시 되어야 한다면 InnoDB 리두 로그 동기화 설정(innodb_flush_log_at_trx_commit) 을 1이 아닌 값으로 설정하면서 Double Write Buffer 도 비활성하는 것도 좋을것 같습니다.
해당 포스팅은 Real MySQL 8.0 책의 많은 내용 중에서 일부분의 내용만 함축적으로 정리한 것으로 모든 내용 확인 및 이해를 위해서 직접 책을 통해 모든 내용을 확인하시는 것을 권해 드립니다
이어지는 다음 글
Reference
Reference Book
• Real MySQL 8.0
Reference Link
• mysql.com/innodb-redo-log.html
• mysql.com/5.7/nnodb-doublewrite-buffer
• mysql.com/8.0/nnodb-doublewrite-buffer
• intomysql.blogspot.com
• percona.com/zfs-from-a-mysql-perspective
관련된 다른 글
Principal DBA(MySQL, AWS Aurora, Oracle)
핀테크 서비스인 핀다에서 데이터베이스를 운영하고 있어요(at finda.co.kr)
Previous - 당근마켓, 위메프, Oracle Korea ACS / Fedora Kor UserGroup 운영중
Database 외에도 NoSQL , Linux , Python, Cloud, Http/PHP CGI 등에도 관심이 있습니다
purityboy83@gmail.com / admin@hoing.io