PostgreSQL 並列トランザクションによる性能低下について考えてみる
はじめに
- PostgreSQL に限らないかもしれないが、RDBMS で多数の並列トランザクションを伴う処理を行う場合は、単純なサーバリソースのボトルネック以外の要因として、エンジン内部的に取られる Light Weight Lock により待機が発生し、性能が低下する可能性がある。
- RDBMS のロックという仕組みがある以上、並列処理で性能が低下する点は仕方ない。逆に仕組みがないと書き込みの整合性が取れなくなる可能性。とはいえ、性能改善するようにチューニングはしたいもの。
- 主に PostgreSQL でどのような Light Weight Lock が考えられ、どのような対応で改善が図れるのかといった点を考察したい。
- 「並列度自体を下げればいいじゃん」と言うのは当然あるものの、アプリやユーザ要件の制約でそう簡単にいかない場合は往々にしてあると思うので RDBMS 自体の機能で改善できる方法をこの記事では考える。
WALWriteLock
wal_buffer 内の wal をディスクにflushするときに取得されるロック。1時点で1つのプロセスしかこのロックを取得できないため、同時多発的にコミット処理等ディスクにwalをflushするワークロード傾向の場合には発生しやすい。
PostgreSQL サーバ側での対応方法としては、、、
- full_page_writes を off に設定してディスクにflushするwalの量を削減する。(でも full page write が無効化されると Durability が下がる...)
- commit_delay を設定して複数トランザクションある程度まとまった単位でディスクに一括flush
- ディスクの性能を向上することでディスクにflushする時間を短くする。
- synchronous_commit を無効化してコミットのディスクへの同期を待たない。ただし、ミッションクリティカルなシステムでは非推奨だと思われる
WALInsertLock
wal レコード作成時には、その分の領域を wal buffer に割り当てる必要がある。
割り当て後に、その wal buffer 上の領域に対して wal レコードをコピー(追加)する際に取得されるのがWALInsertLock。
なお、WALInsertLockは同時に複数のバックエンドから取得可能で値は NUM_XLOGINSERT_LOCKS=8 らしい。恐らくソースはここなのではないでしょうか。
2種類撮り方があって
-
単発で取るのは WALInsertLockAquire
https://github.com/postgres/postgres/blob/d2e150831af85fd30742f551a497db6639d91d0b/src/backend/access/transam/xlog.c#L1300-L1338 -
全て(NUM_XLOGINSERT_LOCKS)取るのは WALInsertLockAcquireExclusive
https://github.com/postgres/postgres/blob/d2e150831af85fd30742f551a497db6639d91d0b/src/backend/access/transam/xlog.c#L1345-L1365
要するに、並列書き込みトランザクション数が多ければ多いほど当該ロックに対する待機は発生しやすいと考えることができる。
PostgreSQL サーバ側での対応方法としては、、、
- full_page_writes を off に設定してそもそものwal量を削減する。(でも、full page writeが無効化されると Durability が下がる...)
- wal_buffers を増やす。
ProcArrayLock
ProcArray=主な目的として、全アクティブトランザクションのバックエンドプロセスを管理する共有メモリ上に確保された配列。
ProcArray に対して Shared lockを取得することで各トランザクションはsnapshotを取得し、また、トランザクション終了時(commit や rollback や abort 時)には ProcArray に対して Exclusive lock を取得して配列の情報を更新する必要がある。ProcArrayLock が発生しやすい状況は、並列トランザクションが同時多発的にトランザクション終了するような場合。トランザクション終了時ということで主には ProcArrayEndTransaction が実行されている最中に ProcArray に対して Exclusive lock が取得されることでこの待機イベントが発生しやすいように見える。
PostgreSQL サーバ側での対応方法としては、、、
あまり思いつかないから誰か教えて欲しいです。。。
仕組み上仕方がなさそうなので、同時コミット数を減らすとか、並列度を減らすとかかな。
他にもあるので追記検討中。。。
参考資料
Discussion