DB 스터디 1주차를 진행하며 Reviewer 역할을 맡았습니다. 공부했던 내용을 정리하고 팀원들이 적어준 이슈에 답변하며 스터디를 진행했는데 DBMS에서 로그 버퍼와 로그 파일을 통해 성능을 향상하는 방법이 기억에 남았습니다.
"스터디에서 발생한 이슈"
https://github.com/code-appender/sql-level-up/issues/4
[ 로그버퍼란 무엇일까 ]
로그버퍼는 DBMS가 사용자로부터 SQL 구문을 입력받아 얻은 변경 정보를 잠시 저장하는 메모리 영역입니다.
임시로 저장된 변경 정보들은 트랜잭션이 커밋되기 전까지 로그버퍼에서 머물게 됩니다. 로그버퍼가 가득차거나 트랜잭션이 커밋되기 전까지는 이어서 트랜잭션이 수행될 수 있습니다. 즉, 사용자의 SQL 구문을 추가적으로 실행하고 기록할 수 있습니다.
[ 로그버퍼는 로그파일로 데이터를 보낸다 ]
로그버퍼가 가득차거나 진행중인 트랜잭션의 커밋을 시도하면 다음으로 이어질 변경사항을 추적하기 위해서 로그버퍼를 비워야 합니다. 로그버퍼를 비운다는 것은 디스크 I/O를 발생시켜 디스크 내의 Redo 로그 파일에 로그 버퍼 내용을 옮기는 것을 의미하며 이 과정을 "Flush"라고 합니다.
참고로, "트랜잭션이 커밋을 완료했다"라고 판단이 되는 시점은 디스크에 변경사항이 반영될 때가 아닌 "로그버퍼의 내용이 로그파일에 완벽히 기록이 되었다"로 결정됩니다. 이는 커밋이 완료가 되었더라도 저장소에는 아직 변경사항이 반영되지 않았을수도 있음을 의미합니다.
[ 로그버퍼의 크기는 얼마가 적당할까 ]
디스크 I/O는 시간이 오래 걸리는 작업이기 때문에 최대한 적게 발생하는 것이 좋습니다. 그렇기 위해서 로그 버퍼의 크기를 키워서 큰 트랜잭션을 한 번의 디스크 I/O 작업을 통해 플러시하는 것으로 DBMS의 성능을 향상시킬 수 있습니다.
하지만 이 로그 버퍼의 크기를 크게 키움으로써 꼭 좋은 성능 향상을 기대할 수는 없습니다. 트랜잭션의 크기가 로그 버퍼보다 작은 경우가 대부분이라면 데이터베이스의 다른 작업을 처리하는 데에 필요한 메모리를 굳이 끌어올 이유가 없습니다.
또한, 로그 버퍼는 영속성이 없기 때문에 트랜잭션이 커밋되기 전에 장애가 발생하여 큰 로그 버퍼의 데이터가 사라진다면 복구하지 못하는 데이터 또한 크기가 커질 수 있기 때문에 위험성이 증가합니다. 이러한 이유로 MySQL의 기본 엔진인 InnoDB 같은 경우 로그 버퍼의 크기로 4mb ~ 16mb를 권장하고 있습니다.
[ MySQL InnoDB는 로그 버퍼를 어떻게 다룰까? ]
그럼 MySQL에서 로그 버퍼의 크기를 키우는 성능 튜닝을 진행해야 하는지, 또는 그대로 둬도 되는지, 튜닝은 어떻게 하는지 알아보겠습니다.
MySQL에는 innodb_log_waits라는 상태변수가 존재합니다. 이 상태변수는 로그 버퍼가 가득 차서 공간이 가득 찰 때까지 기다려야 하는 횟수를 나타냅니다. 로그 버퍼가 가득 차게 되면, 새로운 변경 사항을 로그 버퍼에 기록하기 위해서는 먼저 로그 버퍼의 일부 내용을 디스크에 flush해야 합니다.
이때, 새로운 변경 사항의 기록이 잠시 대기 상태가 되어야 하는데, 이런 대기 상태가 발생한 횟수를 innodb_log_waits 상태 변수가 나타냅니다.
따라서, innodb_log_waits 값이 크면 로그 버퍼의 크기가 너무 작아 자주 디스크에 flush해야 하는 상황이 발생하고 있다는 것을 의미할 수 있습니다. 이 경우, 로그 버퍼의 크기를 증가시켜 성능을 개선하는 것을 고려해 볼 수 있습니다.
innoDB에서 로그 버퍼 사이즈를 키우는 방법은 시스템 환경 변수를 조작하는 것입니다.
mysqld --innodb_log_buffer_size=67108864
또는
[mysqld]
innodb_log_buffer_size = 64M
위의 두 방법으로 로그 버퍼의 사이즈를 조절할 수 있습니다. 변수를 설정한 후에는 mysql을 재시작해야 적용됩니다.
[ 결론 ]
SQL 구문이 실행되어 만들어진 변경사항은 로그버퍼에 임시로 저장되었다가 로그버퍼가 가득차거나 트랜잭션 커밋이 시도되면 디스크 내의 Redo 로그파일로 이동합니다. 로그버퍼에서 로그파일로 데이터를 옮기는 과정인 커밋은 disk i/o가 발생하기 때문에 비용이 큰 작업이며 데이터 정합성을 위해 동기 접근이 발생합니다. 따라서 잦은 커밋은 성능저하로 이어질 수 있습니다.
이 성능저하를 줄이기 위해 큰 트랜잭션을 자주 수행하는 서비스인 경우 로그 버퍼의 크기를 키우는 것이 성능 저하를 막는 방법이 될 수 있습니다. 하지만 다른 작업에 사용될 메모리를 끌어오는 것이기 때문에 적절한 트레이드오프를 고려해야 합니다.
MySQL 또한 서비스 별로, 기능 별로 많은 조건을 고려하여 로그 버퍼의 크기를 조정하는 것을 권장하고 있습니다.
참고 자료
- https://m.blog.naver.com/buti/140105422885
- https://cloud.google.com/mysql/optimization?hl=ko
- https://velog.io/@inhwa1025/MySQL-InnoDB%EB%9E%80
- https://gonmossi.tistory.com/90
- https://dus815.tistory.com/entry/Mysql-Redo-Log-%EB%9E%80