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 -- 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: 各種レベルにおける禁止される現象を以下に示します。
A transaction reads data written by a concurrent uncommitted transaction. 同時に実行されている他のトランザクションが書き込んで未だコミットしていないデータを読み込んでしまう。
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). トランザクションが、以前読み込んだデータを再度読み込み、そのデータが(最初の読み込みの後にコミットした)別のトランザクションによって更新されたことを見出す。
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. トランザクションが、複数行のある集合を返す検索条件で問い合わせを再実行した時、別のトランザクションがコミットしてしまったために、同じ検索条件で問い合わせを実行しても異なる結果を得てしまう。
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 トランザクション分離レベル
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を参照してください。
<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.
UPDATE
、DELETE
、SELECT 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 so that the join
condition fails, then <command>MERGE</command> will evaluate the
command's <literal>NOT MATCHED BY SOURCE</literal> and
<literal>NOT MATCHED [BY TARGET]</literal> actions next, and execute
the first one of each kind that succeeds.
If the row is concurrently deleted, then <command>MERGE</command>
will evaluate the command's <literal>NOT MATCHED [BY TARGET]</literal>
actions, 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.
《マッチ度[80.904523]》MERGE
を使用すると、ユーザはINSERT
、UPDATE
およびDELETE
サブコマンドの様々な組み合わせを指定できます。
INSERT
およびUPDATE
サブコマンドを指定したMERGE
コマンドは、ON CONFLICT DO UPDATE
句を指定したINSERT
に似ていますが、INSERT
またはUPDATE
のいずれかが発生することを保証するものではありません。
MERGE
がUPDATE
またはDELETE
を試行し、行が同時に更新されるものの、結合条件が現在のターゲットタプルと現在のソースタプルに適用される場合、MERGE
はUPDATE
またはDELETE
コマンドと同じように動作し、更新された行バージョンに対してアクションを実行します。
ただし、MERGE
は複数のアクションを指定でき、それらは条件付きにすることができるため、各アクションの条件は更新されたバージョンの行に対して最初のアクションから再評価されます。
最初に一致したアクションがアクションリストの後の方に現れる場合でも同様です。
一方、行が同時に更新または削除されて結合条件が失敗した場合、MERGE
は条件のNOT MATCHED
アクションを次に評価し、成功した最初のアクションを実行します。
MERGE
がINSERT
を試行し、一意なインデックスが存在し、重複した行が同時に挿入された場合、一意性違反エラーが発生します。MERGE
はMATCHED
条件の評価を再開してこのようなエラーを回避しようとはしません。
《機械翻訳》MERGE
を使用すると、ユーザはINSERT
、UPDATE
、DELETE
サブコマンドのさまざまな組み合わせを指定できます。
INSERT
とMERGE
の両方のサブコマンドを持つUPDATE
コマンドは、ONコンフリクトDO更新
句を持つINSERT
と似ていますが、INSERT
またはUPDATE
のいずれかが実行されることを保証するものではありません。
MERGE
がUPDATE
またはDELETE
を試行し、行が同時に更新されても、結合条件が現在ターゲットと現在ソースタプルを通過する場合、MERGE
はUPDATE
またはDELETE
コマンドと同じように動作し、行の更新されたバージョンに対してアクションを実行します。
ただし、MERGE
は複数のアクションを指定でき、条件付きにすることができるため、最初に一致したアクションが後で行のバージョンに表示された場合でも、各アクションの条件は、最初のアクションから開始して、更新されたアクションのリストで再評価されます。
一方、行が同時に更新されて結合条件が失敗した場合、MERGE
は次にコマンドのNOT MATCHED BY SOURCE
と NOT MATCHED [BY TARGET]
アクションを評価し、実行は成功したそれぞれの種類の最初のものを評価する。
行が同時に削除された場合、MERGE
はコマンドの NOT MATCHED [BY TARGET]
アクションを評価し、実行が最初に成功した地域を評価します。
MERGE
がINSERT
を試行し、一意インデックスが存在し、重複行が同時に挿入される場合、一意性違反エラーが発生する。
MERGE
は、MATCHED
条件の評価を再開することによってそのようなエラーを回避しようとはしない。
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
の値には9
と10
があるとします。
BEGIN;
UPDATE website SET hits = hits + 1;
-- 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. リードコミッティドモードで提供されている部分的なトランザクション分離は、多くのアプリケーションでは適切です。 またこのモードは高速で、使い方も簡単ですが、全ての場合に対して十分ではありません。 複雑な問い合わせや更新を行うアプリケーションは、リードコミッティドモードが提供する以上のより厳正なデータベースの厳密に一貫性のある見え方を必要とします。
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
UPDATE
、DELETE
、MERGE
、SELECT 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より前までは、シリアライザブル分離レベルの要求はここで説明した通りの動作をそのまま提供していました。 以前のシリアライザブルの動作を維持するためには、リピータブルリードを要求しなければならなくなりました。
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
システムビューにmode
がSIReadLock
のデータとして現れます。
問い合わせの実行期間中に獲得される個別のロックは、問い合わせが使用した計画に依存するでしょう。
また、ロックを追跡するために使用されるメモリの消耗を防ぐために、トランザクションの過程において、多数のよりきめの細かいロック(例えばタプル・ロック)が結合されて、より少数のよりきめの粗いロック(例えばページ・ロック)になるかもしれません。
直列化異常につながるような競合が継続して生じないことを検知すると、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_transaction、max_pred_locks_per_relation、max_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]を参照してください。