2016-08-20 5 views
11

免責事項:理論的な質問。PostgreSQL Upsertは、システム列XMIN、XMAXなどを使用して、挿入され更新された行を区別します。

PostgreSQL upsertステートメントで、挿入された行と更新された行を区別する方法についていくつかの質問がありました。ここ

は単純な例である:

[email protected]=# create table t(i int primary key, x int); 
[email protected]=# insert into t values(1,1); 
[email protected]=# insert into t values(1,11),(2,22) 
    on conflict(i) do update set x = excluded.i*11 
    returning *, xmin, xmax; 
╔═══╤════╤══════╤══════╗ 
║ i │ x │ xmin │ xmax ║ 
╠═══╪════╪══════╪══════╣ 
║ 1 │ 11 │ 7696 │ 7696 ║ 
║ 2 │ 22 │ 7696 │ 0 ║ 
╚═══╧════╧══════╧══════╝ 

したがって、xmax> 0(又はxmax = xmin) - 行が更新されました。 xmax = 0 - 行が挿入されました。

IMO xminおよびxmaxの列hereの意味をあまりにも明確に説明していません。

ロジックをこれらの列に基づいてベースにすることはできますか?システム列(ソースコードを除く)に関する重要な説明はありますか?

最後に、更新/挿入された行についての私の推測は正しいですか?

+0

更新中に設定したメタデータの別の列を作成しないでください。 – vol7ron

+0

@ vol7ronクエリ全体が遅くなっているためです。私は、既存の列(システム列を含む)が十分であると感じています。興味深いもの: – Abelisto

+0

私の直感はそれがうまくいくと言いますが、それは文書化されていない行動であり、いつか変わらないという保証はありません。私はプロのプロジェクトでこれを使うのではないでしょう。 – klin

答えて

16

これは深い答えが必要な面白い質問だと思います。それは少し長い場合は私と一緒に耐えてください。

要するに

:あなたの推測が正しい、とあなたは、行が挿入され、更新されていないかどうかを判断するために、次のRETURNING句を使用することができます。

RETURNING (xmax = 0) AS inserted 

詳細な説明:

行PostgreSQLはデータを変更しませんが、行の新しいバージョンを作成します。古いバージョンは、不要になったときにのautovacuumによって削除されます。行のバージョンはタプルと呼ばれます。したがって、PostgreSQLでは、1行に複数のタプルが存在する可能性があります。

xmaxは、二つの異なる目的を果たす:

  1. ドキュメントで述べたように、それは削除(または更新)トランザクションのトランザクションIDであることができるタプル(“タプル”が“行のための別の言葉であります”)。 xminxmaxの間のトランザクションIDを持つトランザクションだけがタプルを見ることができます。 xmaxより小さいトランザクションIDを持つトランザクションがない場合、古いタプルを安全に削除できます。

  2. xmax行ロックを格納するためにも使用されます。 PostgreSQLでは、行ロックはロックテーブルに格納されず、ロックテーブルのオーバーフローを避けるためにタプルに格納されます。
    行に1つのトランザクションのみがロックされている場合、xmaxにはロックトランザクションのトランザクションIDが格納されます。複数のトランザクションで行がロックされている場合、xmaxには、いわゆるmultixactの番号が格納されます。このデータ構造には、ロックトランザクションのトランザクションIDが含まれています。このフィールドの正確な意味は、実装の詳細とみなされ、SQLを介して直ちに表示されていないタプルのt_infomaskを知らなくても理解できないので

xmaxのドキュメントは、完全ではありません。

あなたはこのタプルの他のフィールドを表示するためのcontribモジュールpageinspectをインストールすることができます。

私はあなたの例を走り、これは私が(トランザクションID番号が私の場合には当然異なっている)の詳細を調べるためにheap_page_items機能を使用するときに私が見たものである:t_infomask

SELECT *, ctid, xmin, xmax FROM t; 

┌───┬────┬───────┬────────┬────────┐ 
│ i │ x │ ctid │ xmin │ xmax │ 
├───┼────┼───────┼────────┼────────┤ 
│ 1 │ 11 │ (0,2) │ 102508 │ 102508 │ 
│ 2 │ 22 │ (0,3) │ 102508 │  0 │ 
└───┴────┴───────┴────────┴────────┘ 
(2 rows) 

SELECT lp, lp_off, t_xmin, t_xmax, t_ctid, 
     to_hex(t_infomask) AS t_infomask, to_hex(t_infomask2) AS t_infomask2 
FROM heap_page_items(get_raw_page('laurenz.t', 0)); 

┌────┬────────┬────────┬────────┬────────┬────────────┬─────────────┐ 
│ lp │ lp_off │ t_xmin │ t_xmax │ t_ctid │ t_infomask │ t_infomask2 │ 
├────┼────────┼────────┼────────┼────────┼────────────┼─────────────┤ 
│ 1 │ 8160 │ 102507 │ 102508 │ (0,2) │ 500  │ 4002  │ 
│ 2 │ 8128 │ 102508 │ 102508 │ (0,2) │ 2190  │ 8002  │ 
│ 3 │ 8096 │ 102508 │  0 │ (0,3) │ 900  │ 2   │ 
└────┴────────┴────────┴────────┴────────┴────────────┴─────────────┘ 
(3 rows) 

の意味とt_infomask2src/include/access/htup_details.hにあります。 lp_offは、ページ内のタプルデータのオフセット、及びt_ctidは、ページ番号と、ページ内のタプル数で構成さ現在のタプルIDです。テーブルが新しく作成されたので、すべてのデータがページ0である

は私がheap_page_itemsによって返された3行を議論してみましょう。 ラインポインタlp)1時

  1. 我々は古い、更新されたタプルを見つけます。元々はctid = (0,1)でしたが、更新中に現在のバージョンのタプルIDを含むように変更されました。タプルはトランザクション102507によって作成され、トランザクション102508(INSERT ... ON CONFLICTを発行したトランザクション)によって無効化されました。このタプルは表示されなくなり、VACUUMの間に削除されます。

    t_infomaskは、xminxmaxの両方がコミットされたトランザクションに属し、その結果、タプルが作成および削除されたときを示しています。 t_infomask2は、タプルがHOT(ヒープのみのタプル)の更新で更新されたことを示します。つまり、更新されたタプルは元のタプルと同じページにあり、インデックス付きの列は変更されませんでした(src/backend/access/heap/README.HOT参照)。

  2. 行ポインタ2では、トランザクションINSERT ... ON CONFLICT(トランザクション102508)によって作成された新しい更新済みタプルが表示されます。

    t_infomaskこのタプルは、更新の結果であるxminが有効であり、そしてxmaxは(トランザクションが完了したので、もはや適切である)KEY SHARE行ロックが含まれていることを示しています。この行ロックは、INSERT ... ON CONFLICT処理中に取得されました。 t_infomask2は、これがHOTタプルであることを示しています。

  3. 行ポインタ3では、新たに挿入された行が表示されます。

    t_infomaskは、xminが有効であり、xmaxが無効であることを示しています。 xmaxは、この値が常に新しく挿入されたタプルに使用されるため、0に設定されます。

したがって、更新された行の0以外のxmaxは、行ロックによって引き起こされる実装成果物です。INSERT ... ON CONFLICTが1日再実装され、この動作が変更されることが考えられますが、そうは思わないでしょう。

+0

システムの列についての詳細な説明をありがとう。内部的な仕組みを理解することは本当に便利です。 – Abelisto

+2

これはより多くのupvotesに値する素晴らしいと面白い答えです。 –

関連する問題