개발놀이터

PostgreSQL이 WAL (Write Ahead Log) 를 활용하는 방법 본문

CS 지식/데이터베이스

PostgreSQL이 WAL (Write Ahead Log) 를 활용하는 방법

마늘냄새폴폴 2024. 7. 24. 20:45

이번 포스팅은 PostgreSQL의 로드맵인 아래의 링크에서 영감을 받았습니다. 

 

https://roadmap.sh/postgresql-dba

 

DBA Roadmap: Learn to become a database administrator with PostgreSQL

Community driven, articles, resources, guides, interview questions, quizzes for DevOps. Learn to become a modern DevOps engineer by following the steps, skills, resources and guides listed in this roadmap.

roadmap.sh

 

DBA를 위한 로드맵이지만 요즘 데이터베이스를 공부하고 있는 김에 공부하게 되었습니다. 위의 로드맵을 순서대로 쭉 따라가고 있는데 그중에 WAL이라는 개념이 나옵니다. 

 

이번 포스팅에선 WAL에 대해서 깊이있게 공부한 내용을 공유하고자합니다.

 

WAL (Write Ahead Log)

WAL은 PostgreSQL에서 중요한 역할을합니다. WAL의 다른 이름은 트랜잭션 로그인데요, 말 그대로 트랜잭션 처리에 중요한 역할을 합니다. 

 

그리고 트랜잭션을 진행할 때 사용하는 로그 뿐만아니라 백업을 위한 바이너리 로그이기도합니다. MySQL에선 binlog라는 바이너리 로그와 트랜잭션 로그가 분리되어 있지만 PostgreSQL에선 WAL이라는 파일 하나로 관리됩니다. 

 

WAL은 기본 세팅으로 16MB의 크기를 부여받습니다. PostgreSQL은 WAL에 데이터베이스의 쓰기 연산과 관련된 모든 내용을 기록합니다. 

 

만약 쓰기 연산이 일어나면 WAL에 먼저 쓰기 작업을 진행합니다. 이 작업들은 각각 식별자가 존재하는데 Log Sequence Number (이하 LSN) 으로 이를 관리합니다. 

 

WAL의 진정한 파워는 충돌 때 발생한다.

WAL은 트랜잭션과 관련된 모든 로그를 기록하기 때문에 각기 다른 스레드가 트랜잭션을 부여받고 쓰기 연산을 시작할 때 충돌이 일어날 수 있습니다. 

 

이 때 서로 WAL에 쓰기 작업을 하려고할 때 충돌하게 되고 조금이라도 늦게 쓴 트랜잭션이 잠시 롤백했다가 이전 트랜잭션이 완전히 커밋되는 것을 확인하고 WAL에 쓰게됩니다. 

 

이는 PostgreSQL의 일관성을 보장해줍니다. RDBMS의 트랜잭션 ACID 중 C에 해당하는 작업을 이렇게 진행하고 있었군요... 처음 알게 되었습니다. 

 

 

WAL의 설정

Shared Buffers

PostgreSQL은 데이터베이스의 Buffer를 공유합니다. Buffer는 데이터베이스의 요소중 하나이고 자주 사용되는 데이터를 메모리에 적재하여 읽기 연산을 최적화하는 것을 말합니다. 

 

Shared Buffers는 말 그대로 이 Buffer들을 각각 스레드들이 공유한다는 것입니다. RDBMS에는 이러한 Buffer를 관리하는 Buffer Manager가 존재하는데 이 매니저들이 Buffer에 접근하는 스레드들을 관리해줍니다. 

 

 

Dirty Pages

Page는 데이터베이스가 데이터를 저장하는 최소단위입니다. 데이터베이스는 한줄 한줄 데이터를 가져오는 것이 아니라 이 Page를 이용해서 뭉탱이로 데이터를 가져와 읽기 연산에 필요한 디스크 I/O를 최적화해 오버헤드를 줄여줍니다.

 

데이터베이스가 쓰기 연산을 진행하고 디스크에 저장되기까지 중간 단계인 WAL에 존재하는 페이지들을 Dirty Page라고 부르고 Checkpointer와 Background Writer 이 두 요소를 이용해 WAL에 있는 페이지들을 디스크에 밀어넣음으로써 데이터를 디스크에 저장하게 됩니다. 

 

 

Checkpointer

PostgreSQL은 WAL에 체크포인트를 설정하고 이 체크포인트를 이용해 충돌이 일어났을 때 회복을 진행하게 됩니다. 

 

만약 서로 다른 스레드 중 이전 스레드가 WAL에 기록하고 이를 디스크에 밀어넣기 전에 체크포인트를 설정합니다. 이후 다음 스레드가 WAL에 접근할 때 충돌이 일어나고 이 체크포인트를 기준으로 이후에 접근한 스레드의 트랜잭션이 롤백됩니다. 

 

PostgreSQL은 checkpoint-segment와 checkpoint-timeout이라는 두가지 속성으로 Checkpointer를 튜닝할 수 있습니다. 

 

checkpoint-segment는 체크포인트간 얼마나 많은 데이터가 적재될 수 있는지를 설정합니다. 기본값은 3개이고 원한다면 개발자가 이를 자유롭게 설정할 수 있습니다. 반면 checkpoint-timeout은 체크포인트를 얼마나 자주 기록할지 정하는 설정입니다. 

 

checkpoint-segment와 checkpoint-timeout은 모두 작게 설정하면 설정할수록 더욱 자주 Dirty Page를 디스크에 밀어넣어 데이터의 일관성을 높일 수 있습니다. 하지만 그만큼 성능이 떨어지게 되죠. 

 

 

Bakcground Writer

Background Writer는 Checkpointer를 도와주는 하위시스템으로 체크포인트 사이에 적재된 데이터를 디스크에 밀어넣는 역할을 수행합니다. 

 

Background Writer가 작동할 때, 디스크 I/O가 발생하고 이 때 큰 오버헤드가 발생합니다. 때문에, 앞서 설명했듯이 Checkpointer를 어떻게 설정하느냐에 따라서 일관성과 성능 사이의 트레이드오프를 결정해야합니다. 

 

 

PostgreSQL이 WAL를 활용하는 방법

이제 본문 내용이 나오네요. 이번 챕터에서 PostgreSQL이 WAL를 이용해 다양한 기능을 구현하는데 이를 한번 알아보도록 하겠습니다. 

 

1. 트랜잭션 일관성

이는 앞서 언급했던 내용이니 짧게 짚고 넘어가겠습니다. 

 

PostgreSQL은 데이터를 디스크에 적재하기 전 WAL에 기록하고 이를 Checkpointer와 Background Writer를 이용해서 적재합니다. 

 

이 때, 트랜잭션간 충돌이 일어났을 때 WAL를 기반으로 롤백을 진행하고 트랜잭션의 일관성을 유지시켜줍니다. 

 

 

2. 레플리케이션 데이터 동기화

만약 Master, Slave기반의 레플리케이션을 구현했다면 마스터노드에 쓰기연산이 일어나면 이를 복제본에도 똑같이 적용시켜줘야합니다. 

 

이때 마스터 노드에 쓰기 연산이 일어나면 레플리카에게 WAL를 전달합니다. 그러면 레플리카는 WAL를 보고 어떤 데이터가 쓰였는지 알 수 있고, 이를 디스크에 반영합니다. 

 

이런 특징 때문에 보통 RDBMS에서는 레플리케이션을 비동기만 지원하지만 PostgreSQL은 동기 레플리케이션이 가능합니다. 

 

이를 통해 더욱 높은 일관성을 보장할 수 있게 되겠죠. 높은 일관성이 필요한 서비스에서는 꼭 필요한 기능이라고 할 수 있습니다. 

 

여담으로 MySQL은 동기, 비동기 말고도 반동기 (semi-synchronous)를 지원합니다. 반도기는 일단 마스터 노드에 작성하고 하나라도 레플리카에서 작성이 완료되었다는 신호를 받으면 그 다음으로 프로세스를 진행시키는 방법입니다. 

 

이에 대한 자세한 내용은 아래의 링크에 설명되어있습니다!

 

https://coding-review.tistory.com/522

 

RDBMS의 레플리케이션 전략

이번 포스팅은 PostgreSQL을 공부하다가 나온 Streaming Replication이 흥미로워서 이참에 다른 RDBMS의 레플리케이션 전략에 대해서 공부해보고 정리해보려고합니다.  이 전략들을 공부하기 전엔 단순

coding-review.tistory.com

 

3. RDBMS가 NoSQL의 자리를 넘보다

PostgreSQL은 훌륭한 오픈소스 커뮤니티와 라이센스 덕분에 RDBMS를 NoSQL의 특징을 갖게 만들어주는 마개조 버전 라이브러리가 존재합니다. 

 

그 예로 Citus가 존재하는데 이는 RDBMS에서 불가능에 가까웠던 Sharding을 지원하여 RDBMS의 수평확장을 가능케 해주는 라이브러리입니다. 물론 MySQL에도 Sharding을 부분적으로 지원하지만 많이 사용하지는 않는 것 같았습니다.

 

하지만 이런 라이브러리 말고도 WAL를 이용해서 NoSQL의 BASE 속성을 흉내낼 수 있다는 것이 흥미롭습니다. 

 

바로 비동기 커밋입니다. 

 

WAL의 설정 중에는 동기 / 비동기를 껐다 켰다할 수 있는 설정이 존재하는데 이를 이용해서 커밋을 비동기로 처리할 수 있습니다. 

 

비동기 커밋은 말 그대로 커밋이 된 것을 확인하지 않고 WAL에 데이터를 쓸 수 있게 설정하는 것인데 이게 성능 차이가 은근 있더라구요.

 

동기 커밋은 TPS (Transaction Per Second) 가 900인 반면 비동기 커밋은 TPS가 1500에 가깝게 나옵니다. 이는 166퍼센트 성능향상이 있는 것으로 꽤나 큰 차이라는 것을 알 수 있죠. 

 

 

마치며

PostgreSQL은 WAL를 이용해 RDBMS의 기본 소양인 ACID를 지키면서 동시에 NoSQL의 특징을 가질 수도 있다는 것이 정말 놀라웠습니다. 

 

이번에 공부하고 어떻게 RDBMS가 일관성을 지킬 수 있었는지 알게되었고 WAL를 이용해 어떤 전략을 사용하고 있는지 알 수 있었습니다. 

 

PostgreSQL은 후발주자인만큼 RDBMS계의 거장들인 Oracle, MySQL, MariaDB 에게도 뒤지지 않는 특징들이 있어야 살아남을 수 있었을 것 같네요. 

 

많은 개발자들이 RDBMS를 사용하면서 생기는 다양한 고민들 (NoSQL에 비해 낮은 성능, 낮은 확장성) 을 조금씩 해결해주면서 사용자를 점점 늘려가고 있는 것 같습니다. 

 

이때문인지 PostgreSQL은 NoSQL에서만 볼 수 있었던 특징들이 몇가지 보입니다. JSON, BSON 등 유연한 데이터 타입, 엄청나게 많은 인덱스 전략들, Sharding과 비동기 커밋으로 BASE 속성까지말이죠. 

 

처음엔 흥미로 시작했던 PostgreSQL의 매력들에 더 깊이있게 공부해보고 싶어지더군요. 앞으로 맨처음 언급했던 로드맵을 따라가면서 포스팅도 천천히 써볼 생각입니다. 

 

긴 글 읽어주셔서 감사합니다. 오늘도 즐거운 하루 되세요!

 

 

출처

https://vipulvyas.medium.com/understanding-database-internals-how-tables-and-indexes-are-stored-on-disk-and-queried-7cf09a6a48a4

https://www.javatpoint.com/database-buffer

 

Database Buffer - javatpoint

Database Buffer with DBMS Overview, DBMS vs Files System, Architecture, Three schema Architecture, Language, Keys, Generalization, Specialization, Relational Model concept etc.

www.javatpoint.com

https://hevodata.com/learn/working-with-postgres-wal/

 

Working With Postgres WAL Made Easy 101 | Hevo

Discover the significance of Postgres WAL (Write-Ahead Logging) in maintaining data integrity. Explore their role in robust data backups and crucial WAL configuration concepts.

hevodata.com

https://www.youtube.com/watch?v=haz2h7_xFDk&t=240s