バージョンごとのドキュメント一覧

13.2. トランザクションの分離 #

<title>Transaction Isolation</title>

The <acronym>SQL</acronym> standard defines four levels of transaction isolation. The most strict is Serializable, which is defined by the standard in a paragraph which says that any concurrent execution of a set of Serializable transactions is guaranteed to produce the same effect as running them one at a time in some order. The other three levels are defined in terms of phenomena, resulting from interaction between concurrent transactions, which must not occur at each level. The standard notes that due to the definition of Serializable, none of these phenomena are possible at that level. (This is hardly surprising &#45;- if the effect of the transactions must be consistent with having been run one at a time, how could you see any phenomena caused by interactions?) SQLの標準規格では、トランザクションの分離について4つのレベルを定義しています。 標準規格で定義されているもののうち最も厳密なものはシリアライザブルです。 1セットのシリアライザブルなトランザクションを同時実行した場合には、ある順番でひとつずつそれらを実行した場合と同じ結果となることが保証されるものです。本文で詳しく述べます。 他の3レベルは、同時実行しているトランザクション間の相互作用に起因する、各レベルで発生してはならない現象面に基づき定義されます。 標準規格のシリアライザブルの定義では、このレベルではこれらの現象が起こりえないと述べています。 (これは驚くことではありません。トランザクションの効果がひとつずつ実行された場合と一貫性を持たなければならないとしたら、相互作用によって発生した現象はどうやっても見つけ出すことはできないでしょう。)

The phenomena which are prohibited at various levels are: 各種レベルにおける禁止される現象を以下に示します。

dirty read <indexterm><primary>dirty read</primary></indexterm> ダーティリード

A transaction reads data written by a concurrent uncommitted transaction. 同時に実行されている他のトランザクションが書き込んで未だコミットしていないデータを読み込んでしまう。

nonrepeatable read <indexterm><primary>nonrepeatable read</primary></indexterm> 反復不能読み取り

A transaction re-reads data it has previously read and finds that data has been modified by another transaction (that committed since the initial read). トランザクションが、以前読み込んだデータを再度読み込み、そのデータが(最初の読み込みの後にコミットした)別のトランザクションによって更新されたことを見出す。

phantom read <indexterm><primary>phantom read</primary></indexterm> ファントムリード

A transaction re-executes a query returning a set of rows that satisfy a search condition and finds that the set of rows satisfying the condition has changed due to another recently-committed transaction. トランザクションが、複数行のある集合を返す検索条件で問い合わせを再実行した時、別のトランザクションがコミットしてしまったために、同じ検索条件で問い合わせを実行しても異なる結果を得てしまう。

serialization anomaly <indexterm><primary>serialization anomaly</primary></indexterm> 直列化異常

The result of successfully committing a group of transactions is inconsistent with all possible orderings of running those transactions one at a time. 複数のトランザクションを正常にコミットした結果が、それらのトランザクションを1つずつあらゆる可能な順序で実行する場合とは一貫性がない。

The SQL standard and PostgreSQL-implemented transaction isolation levels are described in <xref linkend="mvcc-isolevel-table"/>. 標準SQLおよびPostgreSQLで実装されているトランザクション分離レベルを表 13.1に示します。

表13.1 トランザクション分離レベル

<title>Transaction Isolation Levels</title>
Isolation Level 分離レベル Dirty Read ダーティリード Nonrepeatable Read 反復不能読み取り Phantom Read ファントムリード Serialization Anomaly 直列化異常
Read uncommitted リードアンコミッティド Allowed, but not in PG 許容されるが、PostgreSQLでは発生しない Possible 可能性あり Possible 可能性あり Possible 可能性あり
Read committed リードコミッティド Not possible 安全 Possible 可能性あり Possible 可能性あり Possible 可能性あり
Repeatable read リピータブルリード Not possible 安全 Not possible 安全 Allowed, but not in PG 許容されるが、PostgreSQLでは発生しない Possible 可能性あり
Serializable シリアライザブル Not possible 安全 Not possible 安全 Not possible 安全 Not possible 安全

In <productname>PostgreSQL</productname>, you can request any of the four standard transaction isolation levels, but internally only three distinct isolation levels are implemented, i.e., PostgreSQL's Read Uncommitted mode behaves like Read Committed. This is because it is the only sensible way to map the standard isolation levels to PostgreSQL's multiversion concurrency control architecture. PostgreSQLでは、4つの標準トランザクション分離レベルを全て要求することができます。 しかし、内部的には3つの分離レベルしか実装されていません。 つまり、PostgreSQLのリードアンコミッティドモードは、リードコミッティドのように動作します。 これは、PostgreSQLの多版型同時実行制御という仕組みに標準の分離レベルを関連付ける実際的な方法がこれしかないからです。

The table also shows that PostgreSQL's Repeatable Read implementation does not allow phantom reads. This is acceptable under the SQL standard because the standard specifies which anomalies must <emphasis>not</emphasis> occur at certain isolation levels; higher guarantees are acceptable. The behavior of the available isolation levels is detailed in the following subsections. このテーブルはまた、PostgreSQLのリピータブルリードの実装ではファントムリードが起こらないことを示しています。 SQL標準では、ある分離レベルで発生してはならない異常が指定されているので、これは許されています。 より高度な保障は許容されます。 利用可能な分離レベルでの動作については後で詳細に説明します。

To set the transaction isolation level of a transaction, use the command <xref linkend="sql-set-transaction"/>. トランザクションのトランザクション分離レベルを設定するにはSET TRANSACTIONコマンドを使用してください。

重要

Some <productname>PostgreSQL</productname> data types and functions have special rules regarding transactional behavior. In particular, changes made to a sequence (and therefore the counter of a column declared using <type>serial</type>) are immediately visible to all other transactions and are not rolled back if the transaction that made the changes aborts. See <xref linkend="functions-sequence"/> and <xref linkend="datatype-serial"/>. いくつかのPostgreSQLデータ型と関数はトランザクションの振る舞いに関して特別の規則があります。 特に、シーケンスに対しての変更は(従い、serialを使用して宣言された列のカウンタ)は直後に全ての他のトランザクションで可視となり、変更を行ったトランザクションが中止されるとロールバックはできません。 9.17および8.1.4を参照してください。

13.2.1. リードコミッティド分離レベル #

<title>Read Committed Isolation Level</title>

<firstterm>Read Committed</firstterm> is the default isolation level in <productname>PostgreSQL</productname>. When a transaction uses this isolation level, a <command>SELECT</command> query (without a <literal>FOR UPDATE/SHARE</literal> clause) sees only data committed before the query began; it never sees either uncommitted data or changes committed by concurrent transactions during the query's execution. In effect, a <command>SELECT</command> query sees a snapshot of the database as of the instant the query begins to run. However, <command>SELECT</command> does see the effects of previous updates executed within its own transaction, even though they are not yet committed. Also note that two successive <command>SELECT</command> commands can see different data, even though they are within a single transaction, if other transactions commit changes after the first <command>SELECT</command> starts and before the second <command>SELECT</command> starts. PostgreSQLではリードコミッティドがデフォルトの分離レベルです。 トランザクションがこの分離レベルを使用すると、SELECT問い合わせ(FOR UPDATE/SHARE句を伴わない)はその問い合わせが実行される直前までにコミットされたデータのみを参照し、クエリの実行中にまだコミットされていないデータや、その問い合わせの実行中に別の同時実行トランザクションがコミットした更新は参照しません。 結果として、SELECT問い合わせはその問い合わせが実行を開始した時点のデータベースのスナップショットを参照することになります。 しかしSELECT文は、自分自身のトランザクション内で実行され更新された結果はたとえまだコミットされていなくても参照します。 単一のトランザクション内であっても、SELECT文を2回連続して発行した場合、最初のSELECT文が開始した後で2番目のSELECT文が開始する前に他のトランザクションが更新をコミットすると、最初とその次に発行したSELECT問い合わせは異なるデータを参照してしまうことにも注意してください。

<command>UPDATE</command>, <command>DELETE</command>, <command>SELECT FOR UPDATE</command>, and <command>SELECT FOR SHARE</command> commands behave the same as <command>SELECT</command> in terms of searching for target rows: they will only find target rows that were committed as of the command start time. However, such a target row might have already been updated (or deleted or locked) by another concurrent transaction by the time it is found. In this case, the would-be updater will wait for the first updating transaction to commit or roll back (if it is still in progress). If the first updater rolls back, then its effects are negated and the second updater can proceed with updating the originally found row. If the first updater commits, the second updater will ignore the row if the first updater deleted it, otherwise it will attempt to apply its operation to the updated version of the row. The search condition of the command (the <literal>WHERE</literal> clause) is re-evaluated to see if the updated version of the row still matches the search condition. If so, the second updater proceeds with its operation using the updated version of the row. In the case of <command>SELECT FOR UPDATE</command> and <command>SELECT FOR SHARE</command>, this means it is the updated version of the row that is locked and returned to the client. UPDATEDELETESELECT FOR UPDATE、およびSELECT FOR SHAREコマンドは対象行を検索する際にSELECTコマンドと同じように振舞います。 これらのコマンドは、問い合わせが開始された時点で既にコミットされた対象行のみを検出します。 しかし、その対象行は、検出されるまでに、同時実行中の他のトランザクションによって、既に更新(もしくは削除あるいはロック)されてしまっているかもしれません。 このような場合更新されるべき処理は、最初の更新トランザクションが(それがまだ進行中の場合)コミットもしくはロールバックするのを待ちます。 最初の更新処理がロールバックされるとその結果は無視されて、2番目の更新処理で元々検出した行の更新を続行できます。 最初の更新処理がコミットされると、2番目の更新処理では、最初の更新処理により行が削除された場合はその行を無視します。 行が削除されなかった時の更新処理は、最初のコミットで更新された行に適用されます。 コマンドの検索条件(WHERE句)は、更新された行がまだその検索条件に一致するかどうかの確認のため再評価されます。 検索条件と一致している場合、2番目の更新処理は、更新された行を使用して処理を開始します。 SELECT FOR UPDATEおよびSELECT FOR SHAREの場合、ロックされクライアントに返されるのは、更新されるバージョンの行であることを意味します。

<command>INSERT</command> with an <literal>ON CONFLICT DO UPDATE</literal> clause behaves similarly. In Read Committed mode, each row proposed for insertion will either insert or update. Unless there are unrelated errors, one of those two outcomes is guaranteed. If a conflict originates in another transaction whose effects are not yet visible to the <command>INSERT</command>, the <command>UPDATE</command> clause will affect that row, even though possibly <emphasis>no</emphasis> version of that row is conventionally visible to the command. ON CONFLICT DO UPDATE句のあるINSERTは同じように動作します。 リードコミッティドモードでは、挿入を提案された各行が挿入または更新されます。 無関係なエラーが発生しなければ、それら2つの結果のうち1つが保証されます。 まだその結果がINSERTに対して可視になっていない他のトランザクションに起因する競合では、慣習的な意味でそのコマンドに対して可視のバージョンの行が存在しないにも関わらず、UPDATE句がその行に対して動作します。

<command>INSERT</command> with an <literal>ON CONFLICT DO NOTHING</literal> clause may have insertion not proceed for a row due to the outcome of another transaction whose effects are not visible to the <command>INSERT</command> snapshot. Again, this is only the case in Read Committed mode. ON CONFLICT DO NOTHING句のあるINSERTでは、INSERTのスナップショットに対してその結果が可視になっていない他のトランザクションの結果のために、行の挿入が処理されないかもしれません。 ここでも、問題になるのはリードコミッティドモードのときだけです。

<command>MERGE</command> allows the user to specify various combinations of <command>INSERT</command>, <command>UPDATE</command> and <command>DELETE</command> subcommands. A <command>MERGE</command> command with both <command>INSERT</command> and <command>UPDATE</command> subcommands looks similar to <command>INSERT</command> with an <literal>ON CONFLICT DO UPDATE</literal> clause but does not guarantee that either <command>INSERT</command> or <command>UPDATE</command> will occur. If <command>MERGE</command> attempts an <command>UPDATE</command> or <command>DELETE</command> and the row is concurrently updated but the join condition still passes for the current target and the current source tuple, then <command>MERGE</command> will behave the same as the <command>UPDATE</command> or <command>DELETE</command> commands and perform its action on the updated version of the row. However, because <command>MERGE</command> can specify several actions and they can be conditional, the conditions for each action are re-evaluated on the updated version of the row, starting from the first action, even if the action that had originally matched appears later in the list of actions. On the other hand, if the row is concurrently updated or deleted so that the join condition fails, then <command>MERGE</command> will evaluate the condition's <literal>NOT MATCHED</literal> actions next, and execute the first one that succeeds. If <command>MERGE</command> attempts an <command>INSERT</command> and a unique index is present and a duplicate row is concurrently inserted, then a uniqueness violation error is raised; <command>MERGE</command> does not attempt to avoid such errors by restarting evaluation of <literal>MATCHED</literal> conditions. MERGEを使用すると、ユーザーはINSERTUPDATEおよびDELETEサブコマンドの様々な組み合わせを指定できます。 INSERTおよびUPDATEサブコマンドを指定したMERGEコマンドは、ON CONFLICT DO UPDATE句を指定したINSERTに似ていますが、INSERTまたはUPDATEのいずれかが発生することを保証するものではありません。 MERGEUPDATEまたはDELETEを試行し、行が同時に更新されるものの、結合条件が現在のターゲットタプルと現在のソースタプルに適用される場合、MERGEUPDATEまたはDELETEコマンドと同じように動作し、更新された行バージョンに対してアクションを実行します。 ただし、MERGEは複数のアクションを指定でき、それらは条件付きにすることができるため、各アクションの条件は更新されたバージョンの行に対して最初のアクションから再評価されます。 最初に一致したアクションがアクションリストの後の方に現れる場合でも同様です。 一方、行が同時に更新または削除されて結合条件が失敗した場合、MERGEは条件のNOT MATCHEDアクションを次に評価し、成功した最初のアクションを実行します。 MERGEINSERTを試行し、一意なインデックスが存在し、重複した行が同時に挿入された場合、一意性違反エラーが発生します。MERGEMATCHED条件の評価を再開してこのようなエラーを回避しようとはしません。

Because of the above rules, it is possible for an updating command to see an inconsistent snapshot: it can see the effects of concurrent updating commands on the same rows it is trying to update, but it does not see effects of those commands on other rows in the database. This behavior makes Read Committed mode unsuitable for commands that involve complex search conditions; however, it is just right for simpler cases. For example, consider updating bank balances with transactions like: このような仕組みにより、更新コマンドが、一貫しないスナップショットを参照する可能性があります。 つまり、自分が更新を試みているのと同じ行に対して同時に更新するコマンドの結果は参照できますが、それらのコマンドがデータベース中の他の行に対して更新した結果は参照しません。 このような動作をするために複雑な検索条件を含む問い合わせにリードコミッティドモードを使用することは適切ではありません。 しかし、より単純な検索条件の場合、このモードの使用が適しています。 例えば、銀行の残高を更新する以下のようなトランザクションを考えてみます。

BEGIN;
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 12345;
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 7534;
COMMIT;

If two such transactions concurrently try to change the balance of account 12345, we clearly want the second transaction to start with the updated version of the account's row. Because each command is affecting only a predetermined row, letting it see the updated version of the row does not create any troublesome inconsistency. 2つのこのようなトランザクションが同時に口座番号12345の残高を変更しようとした場合、口座の行の更新されたバージョンに対して2番目のトランザクションが開始されることは明らかに望まれるところです。 各コマンドは事前に決定していた行に対してのみ処理を行うため、行の更新されたバージョンを見せることによって、何の問題となる不整合も引き起こしません。

More complex usage can produce undesirable results in Read Committed mode. For example, consider a <command>DELETE</command> command operating on data that is being both added and removed from its restriction criteria by another command, e.g., assume <literal>website</literal> is a two-row table with <literal>website.hits</literal> equaling <literal>9</literal> and <literal>10</literal>: より複雑な使用法により、リードコミッティドモードでは好ましくない結果を生成する場合があります。 例えば、別のコマンドによってDELETEの制約条件からデータが同時に追加・削除される場合を考えます。 例えば、websiteは2行のテーブルで、website.hitsの値には910があるとします。

BEGIN;
UPDATE website SET hits = hits + 1;

&#45;- run from another session:  DELETE FROM website WHERE hits = 10;

-- 別のセッションから DELETE FROM website WHERE hits = 10; を実行します
COMMIT;

The <command>DELETE</command> will have no effect even though there is a <literal>website.hits = 10</literal> row before and after the <command>UPDATE</command>. This occurs because the pre-update row value <literal>9</literal> is skipped, and when the <command>UPDATE</command> completes and <command>DELETE</command> obtains a lock, the new row value is no longer <literal>10</literal> but <literal>11</literal>, which no longer matches the criteria. UPDATEの前後の両方でwebsite.hits = 10の行があるにも関わらず、DELETEは何もしません。 なぜこうなるのかと言うと、更新前の行値9は読み飛ばされ、またUPDATEが完了してDELETEがロックを獲得した時点では、新しい行値は10ではなく11となり、判定条件にもはやマッチしなくなっているからです。

Because Read Committed mode starts each command with a new snapshot that includes all transactions committed up to that instant, subsequent commands in the same transaction will see the effects of the committed concurrent transaction in any case. The point at issue above is whether or not a <emphasis>single</emphasis> command sees an absolutely consistent view of the database. リードコミッティドモードは、それぞれのコマンドをその時点までにコミットされた全てのトランザクションを含む新規スナップショットを伴って開始するので、同一のトランザクション内でそれに続くコマンドは、いかなる場合でもコミットされた同時実行トランザクションの結果を参照します。 上記問題の要点は単一のコマンドがデータベースの厳密に一貫性のある見え方を見るか否かです。

The partial transaction isolation provided by Read Committed mode is adequate for many applications, and this mode is fast and simple to use; however, it is not sufficient for all cases. Applications that do complex queries and updates might require a more rigorously consistent view of the database than Read Committed mode provides. リードコミッティドモードで提供されている部分的なトランザクション分離は、多くのアプリケーションでは適切です。 またこのモードは高速で、使い方も簡単ですが、全ての場合に対して十分ではありません。 複雑な問い合わせや更新を行うアプリケーションは、リードコミッティドモードが提供する以上のより厳正なデータベースの厳密に一貫性のある見え方を必要とします。

13.2.2. リピータブルリード分離レベル #

<title>Repeatable Read Isolation Level</title>

The <firstterm>Repeatable Read</firstterm> isolation level only sees data committed before the transaction began; it never sees either uncommitted data or changes committed by concurrent transactions during the transaction's execution. (However, each query does see the effects of previous updates executed within its own transaction, even though they are not yet committed.) This is a stronger guarantee than is required by the <acronym>SQL</acronym> standard for this isolation level, and prevents all of the phenomena described in <xref linkend="mvcc-isolevel-table"/> except for serialization anomalies. As mentioned above, this is specifically allowed by the standard, which only describes the <emphasis>minimum</emphasis> protections each isolation level must provide. リピータブルリード分離レベルは、トランザクションが開始される前までにコミットされたデータのみを参照します。 コミットされていないデータや、そのトランザクションの実行中に別のトランザクションでコミットされた変更を参照しません。 (しかし、その問い合わせと同じトランザクション内で行われた過去の更新は、まだコミットされていませんが、参照します。) これはSQLの標準規格で求められるものよりもより強く保証するもので、直列化異常を除いて、表 13.1で述べている現象をすべて防ぎます。 上で述べたように、これは標準規格によって明示的に許容されているもので、標準ではそれぞれの分離レベルが提供しなくてはならない最小の保護のみが示されています。

This level is different from Read Committed in that a query in a repeatable read transaction sees a snapshot as of the start of the first non-transaction-control statement in the <emphasis>transaction</emphasis>, not as of the start of the current statement within the transaction. Thus, successive <command>SELECT</command> commands within a <emphasis>single</emphasis> transaction see the same data, i.e., they do not see changes made by other transactions that committed after their own transaction started. リピータブルリードのトランザクション内の問い合わせは、トランザクション内の現在の文の開始時点ではなく、トランザクションの最初のトランザクション制御以外の文の開始時点のスナップショットを見る、という点でこのレベルはリードコミッティドと異なります。 従って、単一トランザクション内の連続するSELECT文は、同じデータを参照します。つまり、自身のトランザクションが開始した後にコミットされた他のトランザクションによる変更を参照しません。

Applications using this level must be prepared to retry transactions due to serialization failures. このレベルを使ったアプリケーションでは、直列化の失敗によるトランザクションの再実行に備えておく必要があります。

<command>UPDATE</command>, <command>DELETE</command>, <command>MERGE</command>, <command>SELECT FOR UPDATE</command>, and <command>SELECT FOR SHARE</command> commands behave the same as <command>SELECT</command> in terms of searching for target rows: they will only find target rows that were committed as of the transaction start time. However, such a target row might have already been updated (or deleted or locked) by another concurrent transaction by the time it is found. In this case, the repeatable read transaction will wait for the first updating transaction to commit or roll back (if it is still in progress). If the first updater rolls back, then its effects are negated and the repeatable read transaction can proceed with updating the originally found row. But if the first updater commits (and actually updated or deleted the row, not just locked it) then the repeatable read transaction will be rolled back with the message UPDATEDELETEMERGESELECT FOR UPDATE、およびSELECT FOR SHAREコマンドでは、SELECTと同じように対象行を検索します。 これらのコマンドでは、トランザクションが開始された時点で既にコミットされている対象行のみを検出します。 しかし、その対象行は、検出されるまでに、同時実行中の他のトランザクションによって、既に更新(もしくは削除あるいはロック)されている可能性があります。 このような場合、リピータブルリードトランザクションは、最初の更新トランザクションが(それらがまだ進行中の場合)コミットもしくはロールバックするのを待ちます。 最初の更新処理がロールバックされると、その結果は無視され、リピータブルリードトランザクションでは元々検出した行の更新を続行できます。 しかし、最初の更新処理がコミット(かつ、単にロックされるだけでなく、実際に行が更新または削除)されると、リピータブルリードトランザクションでは、以下のようなメッセージを出力してロールバックを行います。

ERROR:  could not serialize access due to concurrent update

because a repeatable read transaction cannot modify or lock rows changed by other transactions after the repeatable read transaction began. これは、リピータブルリードトランザクションでは、トランザクションが開始された後に別のトランザクションによって更新されたデータを変更またはロックすることができないためです。

When an application receives this error message, it should abort the current transaction and retry the whole transaction from the beginning. The second time through, the transaction will see the previously-committed change as part of its initial view of the database, so there is no logical conflict in using the new version of the row as the starting point for the new transaction's update. アプリケーションがこのエラーメッセージを受け取った場合、現在のトランザクションを中止して、トランザクション全体を始めからやり直されなければなりません。 2回目では、トランザクションはコミットされた変更を含めてデータベースの最初の状態とみなすので、新しいバージョンの行を新しいトランザクションにおける更新の始点としても、論理的矛盾は起こりません。

Note that only updating transactions might need to be retried; read-only transactions will never have serialization conflicts. 再実行する必要があるかもしれないのは、更新トランザクションのみです。 読み込み専用トランザクションでは直列化の衝突は決して起こりません。

The Repeatable Read mode provides a rigorous guarantee that each transaction sees a completely stable view of the database. However, this view will not necessarily always be consistent with some serial (one at a time) execution of concurrent transactions of the same level. For example, even a read-only transaction at this level may see a control record updated to show that a batch has been completed but <emphasis>not</emphasis> see one of the detail records which is logically part of the batch because it read an earlier revision of the control record. Attempts to enforce business rules by transactions running at this isolation level are not likely to work correctly without careful use of explicit locks to block conflicting transactions. リピータブルリードモードでは、全てのトランザクションがデータベースの一貫した不変のビューの状態を参照することが保証されます。 しかし、このビューは常にいくつかの同じレベルの同時実行トランザクションの直列(一度に一つずつの)実行と一貫性を持つとは限りません。 例えば、このレベルの読み取りのみのトランザクションは、バッチが完了したことを示すために更新された制御レコードを参照できますが、 制御レコードのより以前のバージョンを読み取るため、論理的にそのバッチの一部となる詳細なレコードの1つを参照することはできません。 この分離レベルで実行するトランザクションによりビジネスルールを強制しようとすることは、競合するトランザクションをブロックするために注意深く明示的なロックを持たないと、正確に動作しないことが多くあります。

The Repeatable Read isolation level is implemented using a technique known in academic database literature and in some other database products as <firstterm>Snapshot Isolation</firstterm>. Differences in behavior and performance may be observed when compared with systems that use a traditional locking technique that reduces concurrency. Some other systems may even offer Repeatable Read and Snapshot Isolation as distinct isolation levels with different behavior. The permitted phenomena that distinguish the two techniques were not formalized by database researchers until after the SQL standard was developed, and are outside the scope of this manual. For a full treatment, please see <xref linkend="berenson95"/>. リピータブルリード分離レベルは、学術的なデータベースの文献や他のデータベース製品のいくつかではスナップショット分離として知られる技術を用いて実装されています。 同時実行性の面で劣る伝統的なロック技術を使うシステムと比較すると振舞いや性能の違いが観察されるかもしれません。 他のシステムでは、リピータブルリードとスナップショット分離を異なる振舞いをする別の分離レベルとして提供しているかもしれません。 2つの技術を区別する許容される現象は、標準SQLが制定されるまではデータベース研究者により定式化されておらず、この文書の範囲を超えます。 詳細な取り扱いについては[berenson95]を参照してください。

注記

Prior to <productname>PostgreSQL</productname> version 9.1, a request for the Serializable transaction isolation level provided exactly the same behavior described here. To retain the legacy Serializable behavior, Repeatable Read should now be requested. PostgreSQL version 9.1より前までは、シリアライザブル分離レベルの要求はここで説明した通りの動作をそのまま提供していました。 以前のシリアライザブルの動作を維持するためには、リピータブルリードを要求しなければならなくなりました。

13.2.3. シリアライザブル分離レベル #

<title>Serializable Isolation Level</title>

The <firstterm>Serializable</firstterm> isolation level provides the strictest transaction isolation. This level emulates serial transaction execution for all committed transactions; as if transactions had been executed one after another, serially, rather than concurrently. However, like the Repeatable Read level, applications using this level must be prepared to retry transactions due to serialization failures. In fact, this isolation level works exactly the same as Repeatable Read except that it also monitors for conditions which could make execution of a concurrent set of serializable transactions behave in a manner inconsistent with all possible serial (one at a time) executions of those transactions. This monitoring does not introduce any blocking beyond that present in repeatable read, but there is some overhead to the monitoring, and detection of the conditions which could cause a <firstterm>serialization anomaly</firstterm> will trigger a <firstterm>serialization failure</firstterm>. シリアライザブル分離レベルは、最も厳しいトランザクションの分離性を提供します。 このレベルではトランザクションが同時にではなく、次から次へと、あたかも順に実行されているように逐次的なトランザクションの実行を全てのコミットされたトランザクションに対しエミュレートします。 しかし、このレベルを使ったアプリケーションでは、リピータブルリードレベルと同様に、直列化の失敗によるトランザクションの再実行に備えておく必要があります。 実際、この分離レベルは、(ある時点で)逐次実行可能なすべてのトランザクションにおいて、シリアライザブルトランザクションの同時実行の組が一貫性のないような振る舞いをしていないかも監視することを除き、リピータブルリードと全く同じ動きをします。 この監視では、リピータブルリードが示すものを越えてブロックすることはありませんが、監視によりいくらかのオーバーヘッドがあり、直列化異常を引き起こすような状態の検知は、直列化の失敗を引き起こすでしょう。

As an example, consider a table <structname>mytab</structname>, initially containing: 例えば、以下の初期データを持つmytabというテーブルを考えてみます。

 class | value
-------+-------
     1 |    10
     1 |    20
     2 |   100
     2 |   200

Suppose that serializable transaction A computes: ここでシリアライザブルトランザクションAが以下を計算し、

SELECT SUM(value) FROM mytab WHERE class = 1;

and then inserts the result (30) as the <structfield>value</structfield> in a new row with <structfield>class</structfield><literal> = 2</literal>. Concurrently, serializable transaction B computes: そして、valueにその結果(30)を、class = 2の行として新たに挿入したとします。 同時にシリアライザブルトランザクションBが以下を計算し、

SELECT SUM(value) FROM mytab WHERE class = 2;

and obtains the result 300, which it inserts in a new row with <structfield>class</structfield><literal> = 1</literal>. Then both transactions try to commit. If either transaction were running at the Repeatable Read isolation level, both would be allowed to commit; but since there is no serial order of execution consistent with the result, using Serializable transactions will allow one transaction to commit and will roll the other back with this message: その結果300を得、そして、この結果をclass = 1の新たな行として挿入したとします。 その後、両方のトランザクションがコミットを試みます。 もし一方の処理がリピータブルリード分離レベルで実行していれば、両方のコミットが許されるでしょう。 しかし、この結果と一貫する実行順序が存在しないため、シリアライザブルトランザクションを使用した場合は、ひとつのトランザクションがコミットを許され、他方は次のメッセージとともにロールバックされることになります。

ERROR:  could not serialize access due to read/write dependencies among transactions

This is because if A had executed before B, B would have computed the sum 330, not 300, and similarly the other order would have resulted in a different sum computed by A. この理由は、もしAがBよりも前に実行されていた場合、Bの総和は300ではなく330と計算され、また同様に逆の順序で実行されたとすればAで計算される総和が異なる結果になるからです。

When relying on Serializable transactions to prevent anomalies, it is important that any data read from a permanent user table not be considered valid until the transaction which read it has successfully committed. This is true even for read-only transactions, except that data read within a <firstterm>deferrable</firstterm> read-only transaction is known to be valid as soon as it is read, because such a transaction waits until it can acquire a snapshot guaranteed to be free from such problems before starting to read any data. In all other cases applications must not depend on results read during a transaction that later aborted; instead, they should retry the transaction until it succeeds. 異常を防止するためにシリアライザブルトランザクションを使用するのであれば、恒久的なユーザテーブルから読み取られたいかなるデータも、それを読んだトランザクションがコミットされるまで有効とは認められない点は重要です。 このことは読み取り専用トランザクションにも当てはまりますが、遅延可能な読み取り専用トランザクション内で読み込まれたデータは例外で、読み込まれてすぐに有効とみなされます。 なぜなら、遅延可能なトランザクションはすべてのデータを読み込む前にこのような問題がないことを保証されているスナップショットを取得できるまで待機するからです。 それ以外の全ての場合において、後に中止されたトランザクション内で読み込まれた結果をアプリケーションは信用してはならず、アプリケーションはトランザクションが成功するまで再試行すべきです。

To guarantee true serializability <productname>PostgreSQL</productname> uses <firstterm>predicate locking</firstterm>, which means that it keeps locks which allow it to determine when a write would have had an impact on the result of a previous read from a concurrent transaction, had it run first. In <productname>PostgreSQL</productname> these locks do not cause any blocking and therefore can <emphasis>not</emphasis> play any part in causing a deadlock. They are used to identify and flag dependencies among concurrent Serializable transactions which in certain combinations can lead to serialization anomalies. In contrast, a Read Committed or Repeatable Read transaction which wants to ensure data consistency may need to take out a lock on an entire table, which could block other users attempting to use that table, or it may use <literal>SELECT FOR UPDATE</literal> or <literal>SELECT FOR SHARE</literal> which not only can block other transactions but cause disk access. 真の直列性を保証するためにPostgreSQLでは、述語ロックを使います。 述語ロックでは、トランザクションが最初に実行されたとしたら、それによる書き込みが同時実行トランザクションによる読み取り結果にいつ影響を及ぼしたかの決定を可能にするロックを保持します。 PostgreSQLでは、これらのロックはブロッキングを引き起こさないため、デッドロックの要因とならないものです。 それらは、同時実行中のシリアライザブルトランザクションが、直列化異常につながる組み合わせであることを識別しフラグを立てることに使用されます。 それとは対照的に、データの一貫性を保証したいリードコミッティドあるいはリピータブルリードトランザクションでは、テーブル全体のロック(そのテーブルを使用しようとしている他のユーザをブロックするかもしれません)を必要とするかもしれませんし、あるいは、他のトランザクションをブロックするだけでなくディスク・アクセスを引き起こすSELECT FOR UPDATEあるいはSELECT FOR SHAREを使用するかもしれません。

Predicate locks in <productname>PostgreSQL</productname>, like in most other database systems, are based on data actually accessed by a transaction. These will show up in the <link linkend="view-pg-locks"><structname>pg_locks</structname></link> system view with a <literal>mode</literal> of <literal>SIReadLock</literal>. The particular locks acquired during execution of a query will depend on the plan used by the query, and multiple finer-grained locks (e.g., tuple locks) may be combined into fewer coarser-grained locks (e.g., page locks) during the course of the transaction to prevent exhaustion of the memory used to track the locks. A <literal>READ ONLY</literal> transaction may be able to release its SIRead locks before completion, if it detects that no conflicts can still occur which could lead to a serialization anomaly. In fact, <literal>READ ONLY</literal> transactions will often be able to establish that fact at startup and avoid taking any predicate locks. If you explicitly request a <literal>SERIALIZABLE READ ONLY DEFERRABLE</literal> transaction, it will block until it can establish this fact. (This is the <emphasis>only</emphasis> case where Serializable transactions block but Repeatable Read transactions don't.) On the other hand, SIRead locks often need to be kept past transaction commit, until overlapping read write transactions complete. PostgreSQLの述語ロックは、他のほとんどのデータベースシステムと同様、トランザクションによって実際にアクセスされたデータを元にしています。 これらは、pg_locksシステムビューにmodeSIReadLockのデータとして現れます。 問い合わせの実行期間中に獲得される個別のロックは、問い合わせが使用した計画に依存するでしょう。 また、ロックを追跡するために使用されるメモリの消耗を防ぐために、トランザクションの過程において、多数のよりきめの細かいロック(例えばタプル・ロック)が結合されて、より少数のよりきめの粗いロック(例えばページ・ロック)になるかもしれません。 直列化異常につながるような競合が継続して生じないことを検知すると、READ ONLYトランザクションは、それが完了する前にSIReadロックを解放できるかもしれません。 実際、READ ONLYトランザクションは、よく開始時点でその事実を確証し、どんな述語ロックもとらないこともあります。 SERIALIZABLE READ ONLY DEFERRABLEトランザクションを明示的に要求した場合には、この事実を確証できるまでブロックします。 (これは、シリアライザブルトランザクションはブロックするけれども、リピータブルリードトランザクションはブロックしない唯一のケースです。) 他方で、SIReadロックは、しばしば読み取りと書き込みが重なっているトランザクションが完了するまで、トランザクションのコミットが終わっても保持される必要があります。

Consistent use of Serializable transactions can simplify development. The guarantee that any set of successfully committed concurrent Serializable transactions will have the same effect as if they were run one at a time means that if you can demonstrate that a single transaction, as written, will do the right thing when run by itself, you can have confidence that it will do the right thing in any mix of Serializable transactions, even without any information about what those other transactions might do, or it will not successfully commit. It is important that an environment which uses this technique have a generalized way of handling serialization failures (which always return with an SQLSTATE value of '40001'), because it will be very hard to predict exactly which transactions might contribute to the read/write dependencies and need to be rolled back to prevent serialization anomalies. The monitoring of read/write dependencies has a cost, as does the restart of transactions which are terminated with a serialization failure, but balanced against the cost and blocking involved in use of explicit locks and <literal>SELECT FOR UPDATE</literal> or <literal>SELECT FOR SHARE</literal>, Serializable transactions are the best performance choice for some environments. シリアライザブルトランザクションの一貫した使用は開発を単純化することができます。 正常にコミットされた同時実行のシリアライザブルトランザクションのどんな集合も、あたかもそれらが一度に一つずつ実行されたのと同じ結果になることが保証されるので、単独で実行されたときに単一トランザクションが正しく動作するよう書かれていると実証できるなら、他のトランザクションが何をしているかの情報が全く無くとも、複数シリアライザブルトランザクションが混在する中で正しく動作するかコミットに成功しないかであると確証を持つことができます。 この技術を使用する環境では、直列化の失敗(常にSQLSTATE値が'40001'で返る)を扱うための、汎用的な手段を持っていることが重要です。 なぜなら、どのトランザクションが読み取り/書き込みの依存性に影響し、直列化異常を防ぐためにロールバックさせる必要があるかということを、正確に予測することは非常に困難だからです。 読み取り/書き込みの依存性を監視したり、直列化異常で終了したトランザクションを再起動することはコストがかかります。 しかしながら、このコストと、明示的なロックとSELECT FOR UPDATEまたはSELECT FOR SHAREを使用したブロッキングとで比較検討すると、シリアライザブルトランザクションはいくつかの環境において最良な実行を選択することになります。

While <productname>PostgreSQL</productname>'s Serializable transaction isolation level only allows concurrent transactions to commit if it can prove there is a serial order of execution that would produce the same effect, it doesn't always prevent errors from being raised that would not occur in true serial execution. In particular, it is possible to see unique constraint violations caused by conflicts with overlapping Serializable transactions even after explicitly checking that the key isn't present before attempting to insert it. This can be avoided by making sure that <emphasis>all</emphasis> Serializable transactions that insert potentially conflicting keys explicitly check if they can do so first. For example, imagine an application that asks the user for a new key and then checks that it doesn't exist already by trying to select it first, or generates a new key by selecting the maximum existing key and adding one. If some Serializable transactions insert new keys directly without following this protocol, unique constraints violations might be reported even in cases where they could not occur in a serial execution of the concurrent transactions. PostgreSQLのシリアライザブルトランザクション分離レベルが同じ結果を生む実行順序があることを証明できるときだけ、同時のトランザクションのコミットを許すとはいえ、本当のシリアル実行では起こらないエラーが常に防げるわけではありません。 特に、たとえそのキーが生成されていないことを挿入しようとする前に明示的に調査した後でも重複しているシリアライザブルトランザクションとの競合が原因で一意性制約違反を見ることになる可能性があります。 これは潜在的に競合しているキーを挿入する全てのシリアライザブルトランザクションで確実に挿入できるかどうか最初に明示的に調査することで防ぐことができます。 例えば、ユーザに新しいキーを聞いてからまずselectでそれがすでに存在しているか確かめるアプリケーション、もしくは存在している中で一番大きなキーを選択しそれに1を足すことで新しいキーを生成するアプリケーションを想像してみてください。 もしいくつかのシリアライザブルトランザクションがこのプロトコルに沿わずに直接新しいキーを挿入すれば、たとえそれがシリアル実行の同時トランザクションでは起こりえないケースでも一意性制約違反が報告されることになります。

For optimal performance when relying on Serializable transactions for concurrency control, these issues should be considered: 同時実行制御のためにシリアライザブルトランザクションを使用する場合、最適な性能のためには、以下の問題を考慮すべきです。

  • Declare transactions as <literal>READ ONLY</literal> when possible. 可能であればトランザクションをREAD ONLYとして宣言してください。

  • Control the number of active connections, using a connection pool if needed. This is always an important performance consideration, but it can be particularly important in a busy system using Serializable transactions. もし必要ならばコネクションプールを使用して、活動中の接続数を制御してください。 これは常に重要な性能上の考慮点ですが、シリアライザブルトランザクションを使用した多忙なシステムにおいては、特に重要になる可能性があります。

  • Don't put more into a single transaction than needed for integrity purposes. 完全性のために必要とされる以上のものを1つのトランザクションに入れないようにしてください。

  • Don't leave connections dangling <quote>idle in transaction</quote> longer than necessary. The configuration parameter <xref linkend="guc-idle-in-transaction-session-timeout"/> may be used to automatically disconnect lingering sessions. 必要以上に長くトランザクション内で待機状態で接続したまま放置しておかないようにしてください。 長引くセッションを自動的に切断するために、設定パラメータidle_in_transaction_session_timeoutを使うことができます。

  • Eliminate explicit locks, <literal>SELECT FOR UPDATE</literal>, and <literal>SELECT FOR SHARE</literal> where no longer needed due to the protections automatically provided by Serializable transactions. シリアライザブルトランザクションにより自動的に提供される保護により、不必要な、明示的なロック、SELECT FOR UPDATEおよびSELECT FOR SHAREを取り除いてください。

  • When the system is forced to combine multiple page-level predicate locks into a single relation-level predicate lock because the predicate lock table is short of memory, an increase in the rate of serialization failures may occur. You can avoid this by increasing <xref linkend="guc-max-pred-locks-per-transaction"/>, <xref linkend="guc-max-pred-locks-per-relation"/>, and/or <xref linkend="guc-max-pred-locks-per-page"/>. 述語ロックのテーブルがメモリ不足になると、複数のページレベルの述語ロックを単一のリレーションレベルの述語ロックへと結合するようシステムが強いられ、直列化失敗の発生割合が増加する恐れがあります。 これは、max_pred_locks_per_transactionmax_pred_locks_per_relationmax_pred_locks_per_pageのいずれか、あるいは、すべてを増やすことにより回避できます。

  • A sequential scan will always necessitate a relation-level predicate lock. This can result in an increased rate of serialization failures. It may be helpful to encourage the use of index scans by reducing <xref linkend="guc-random-page-cost"/> and/or increasing <xref linkend="guc-cpu-tuple-cost"/>. Be sure to weigh any decrease in transaction rollbacks and restarts against any overall change in query execution time. シーケンシャルスキャンは常にリレーションレベルでの述語ロックを必要とします。 これによって、直列化失敗の頻度が増える可能性があります。 random_page_costを縮小および(または)cpu_tuple_costを増加することによりインデックススキャンの使用を促進することは有用かもしれません。 トランザクションのロールバックや再実行の減少を、問い合わせ実行時間の全体的な変化と比較検討するようにしてください。

The Serializable isolation level is implemented using a technique known in academic database literature as Serializable Snapshot Isolation, which builds on Snapshot Isolation by adding checks for serialization anomalies. Some differences in behavior and performance may be observed when compared with other systems that use a traditional locking technique. Please see <xref linkend="ports12"/> for detailed information. シリアライザブル分離レベルは、学術的なデータベースの文献ではシリアライザブルスナップショット分離として知られる技術を使って実装されています。シリアライザブルスナップショット分離は、スナップショット分離の上に直列化異常の確認を追加することで構築されています。 伝統的なロック技術を使う他のシステムと比較すると振舞いや性能の違いが観察されるかもしれません。 詳細な情報は[ports12]を参照してください。