2016年12月15日木曜日

PostgreSQLのテーブルの内部と更新時の動作について

このエントリはバルテックアドベントカレンダー2016に向けた投稿です。
http://www.adventar.org/calendars/1637

PostgreSQLはオープンソースのデータベースの中でもメジャーです。私と同じ会社に所属する皆さまもPostgreSQLを触る機会がある人は多いのではないでしょうか。
私も今年の前半にPostgreSQLの導入検討の案件があり、触ってみました。

PostgreSQLは他のデータベースと比較して特徴的な個所として挙げられるのは、追記型であるということが言えます。

ログ先行書き込み(write ahead logging, WAL)というものをPostgreSQLでは採用しています。
簡単に言いますと、トランザクションをコミットする前にログを書き出し、それからデータファイルに書き込むことで、たとえクラッシュが起きてもデータベースをリカバリできるという手法です。

さて、PostgreSQLではどのように実装されているのでしょうか?
PostgreSQLのテーブルの中身を見てみますと、データファイルの中にテーブルの行の情報が格納されていますが、行ごとにヘッダがあります。
行ヘッダの中身は28バイトあり、その中にはt_xminとt_xmaxがあります。
行が挿入されると、t_xminにはトランザクションIDが入ります。
それから行が削除・更新されると、t_xmaxにトランザクションIDが入り、このデータは無効となります。
PostgreSQLはデータを追記していきますので、削除・更新をする際には実際に消したりするわけではなく、論理削除のようにフラグを立てていくだけで、
他のトランザクションは、自分のトランザクションのIDと照らし合わせて、読むべきかどうかを判断して処理していきます。

これらの動きを確認したい場合は、pageinspectという追加モジュールがあります。
http://www.postgresql.jp/document/9.6/html/pageinspect.html

ちょっと手持ち環境でpageinspectを入れられなかったのですが、イメージとしてはこんな感じです。
私は安飲み屋を巡るのを趣味の一つとして持っているので、「居酒屋巡礼」テーブルを考えてみました。

izakaya_name         place                last_visit       
==================== ==================== ====================
oosakaya             monzennakacho        2014-11-24
fujiyahonten         shibuya              2015-08-24
mantarou             shinjuku             2016-09-12
torimasa             inadazutsumi         2016-10-07

このテーブルのt_xminとt_xmaxを、仮にこう考えてみました。
xmin xmax izakaya_name         place                last_visit
==== ==== ==================== ==================== ==============
X100      oosakaya             monzennakacho        2014-11-24
X101      fujiyahonten         shibuya              2015-08-24
X102      mantarou             shinjuku             2016-09-12
X103      torimasa             inadazutsumi         2016-10-07

テーブルを追加します。
insert into izakaya values('ma-chan','shinagawa','2016-10-21');

このテーブルのt_xminとt_xmaxは、こんな感じになるでしょう。追記されています。
xmin xmax izakaya_name         place                last_visit
==== ==== ==================== ==================== ==============
X100      oosakaya             monzennakacho        2014-11-24
X101      fujiyahonten         shibuya              2015-08-24
X102      mantarou             shinjuku             2016-09-12
X103      torimasa             inadazutsumi         2016-10-07
X104      ma-chan              shinagawa            2016-10-21

更新はどうでしょうか。torimasaに2016-12-04に訪れたので、X103のレコードを更新します。

update izakaya set last_visit='2016-12-04' where izakaya_name='torimasa';

xmaxに値を入れることで、torimasaの2016-10-07の行は(最新のポイントでは)参照されなくなり、見えなくなり、代わりに2016-12-04の行が見えるようになります。
xmin xmax izakaya_name         place                last_visit 
==== ==== ==================== ==================== ==============
X100      oosakaya             monzennakacho        2014-11-24
X101      fujiyahonten         shibuya              2015-08-24
X102      mantarou             shinjuku             2016-09-12
X103 X105 torimasa             inadazutsumi         2016-10-07
X104      ma-chan              shinagawa            2016-10-21
X105      torimasa             inadazutsumi         2016-12-04

ちなみに、削除の場合は、xmaxに値が入るのみとなります。


それでは、テーブルのデータは溜まっていく一方ではないかと思われるかもしれません。
しかしながら、この問題は不必要な行を回収するバキュームという機能で解消されます。
バキュームについては、またの機会に書いてみようと思います。


<参照資料>
「WEB+DB PRESS Vol.24 徒然PostgreSQL散策」
http://www2b.biglobe.ne.jp/~caco/webdb-pdfs/vol24_214-221.pdf

「PostgreSQL Deep Dive PostgreSQLのストレージアーキテクチャ(基本編)」
http://pgsqldeepdive.blogspot.jp/2012/12/postgresql_16.html

4 件のコメント:

  1. RDBは、難しいところがあっても順を追って話し合えば必ず分かり合えるって信頼感があって好きです。
    NoSQLはもう異世界な感じで怖いです。

    返信削除
  2. katoさん
    コメントありがとうございます。
    NoSQLは私もほとんどわかっておらず、異世界な感じがしています。
    折を見て、カウチベースとか勉強したいと思ってます。

    返信削除
  3. 私はRDBの方がACIDのために色々な制御が必要で、NoSQLはトランザクションなどが余り厳密ではなくその分シンプルだと思っていますが、実際どうなんでしょうかね

    返信削除
  4. カンノさん
    確かにアプリ側からみると、RDBのほうが制約があるのでしょうね。
    RDBを中心にやっていると、考え方を切り替えるのが難しいです。

    返信削除