Hatena::ブログ(Diary)

モメないためのメモ

2011-12-07

Synchronized Snapshot お試し:PostgreSQL Advent Calendar #7

このエントリはPostgreSQL Advent Calendarの12/7分です。

現在開発中のPostgreSQL 9.2でSynchronized Snapshot(同期スナップショット?)なる機能が導入されます。開発の動機はpg_dumpallで複数のデータベースバックアップを一貫性のある内容で取りたいということのようで、あまり派手な機能ではないのですが、開発中の pgsql_fdw と組み合わせて面白いことできないかな、と思って調べたので内容を整理してみます。

そもそも「Snapshot(スナップショット)」って何?

PostgreSQLにおける「スナップショット」とは、MVCC で使われる情報の一つで、大雑把に言えばあるトランザクションからどの時点でのデータが可視であるかを判定する基準です。Synchronized Snapshotでは、このスナップショットを複数のトランザクションで共有することによって同一内容のデータを参照できるようになります。

スナップショットを同期する流れ

参照するデータの準備

まず、参照するデータを用意します。デフォルトの分離レベルはREAD COMMITTEDなので、この時点で他のトランザクションから1,000件が参照できます。

<Session 1-データ操作>
postgres=# CREATE TABLE foo (
postgres-# 	id int,
postgres-# 	name text,
postgres-# 	CONSTRAINT foo_pkey PRIMARY KEY (id)
postgres-# );
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"
CREATE TABLE
postgres=# INSERT INTO foo SELECT id, to_char(id, '00000000') FROM generate_series(1, 1000) id;
INSERT 0 1000
同期元トランザクションの開始

まず、基準となるトランザクションがいないことには話が始まりませんので開始します。今回はデータを作成したトランザクションとは別に同期元トランザクションを開始してみます。この時点で参照できるのは1,000件です。

<Session 2-同期元>
postgres=# BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN
postgres=# SELECT count(*) FROM foo;
 count
-------
  1000
(1 row)
スナップショットエクスポート

同期元のトランザクションでpg_export_snapshot()関数を実行してスナップショットエクスポートします。関数戻り値スナップショット識別子なので、後で使うまで覚えておきましょう。なお、READ COMMITTED分離レベルの場合は、トランザクション進行中に複数回実行すると異なるスナップショット識別子が返り、複数の別トランザクションでそれぞれの時点のデータを参照することもできます。

<Session 2-同期元>
postgres=# select pg_export_snapshot();
 pg_export_snapshot
--------------------
 000007CE-1
(1 row)
データ追加

同期元とは別のセッションで、先ほどのテーブルにデータを1,000件追加してコミットしておきます。同期元トランザクションはREAD COMMITTED分離レベルなので、参照できるデータは2,000件になります。

<Session 1-データ操作>
postgres=# INSERT INTO foo SELECT id, to_char(id, '00000000') FROM generate_series(1001, 2000) id;
INSERT 0 1000
<Session 2-同期元>
postgres=# SELECT count(*) FROM foo;
 count
-------
  2000
(1 row)
同期先トランザクションの開始

別のセッショントランザクションを開始します。が、同期元との間に分離レベルの制約があります。

同期元同期先
SERIALIZABLEREPEATABLE READまたはSERIALIZABLE
REPEATABLE READREPEATABLE READ
READ COMMITTEDREPEATABLE READ

ここでは同期元がREAD COMMITTEDなので、REPEATABLE READを使います。

<Session 3-同期先1>
postgres=# BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN
スナップショットインポート

同期先のトランザクションで先ほど取得したスナップショット識別子を指定してpg_import_snapshot()関数を実行します…と言いたいところですが、インポートにはSET TRANSACTION SNAPSHOT文を使うことになっています。エクスポートした時点と同じく参照できるのは1,000件が参照ですね。

<Session 3-同期先1>
postgres=# SET TRANSACTION SNAPSHOT '000007CE-1';
SET
postgres=# SELECT count(*) FROM foo;
 count
-------
  1000
(1 row)

なお、SET TRANSACTION SNAPSHOT文はそのトランザクションの最初のクエリでなければならない、という制約があります。私は最初にpsqlでSET TRANSACTION文を打つときに補完機能を使ってしまい、裏でSELECT文が発行されてトランザクション開始からやりなおすはめになりました…

別のスナップショットエクスポート/インポート

同期元トランザクションから2,000件参照できる状態でエクスポートしたスナップショットを使うと、同じデータが参照できます。

<Session 2-同期元>
postgres=# select pg_export_snapshot();
 pg_export_snapshot
--------------------
 000007BB-1
(1 row)
<Session 4-同期先2>
postgres=# BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN
postgres=# SET TRANSACTION SNAPSHOT '000007CE-1';
SET
postgres=# SELECT count(*) FROM foo;
 count
-------
  2000
(1 row)

まとまってないまとめ

このエントリ読んだ方で何か面白い使い道を思いついた方がいたら、ぜひ教えてください。m(_ _)m

明日のPostgreSQL Advent Calendarは〜♪

fujii_masaoさんです。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証