diff options
author | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2013-01-23 12:04:59 -0300 |
---|---|---|
committer | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2013-01-23 12:04:59 -0300 |
commit | 0ac5ad5134f2769ccbaefec73844f8504c4d6182 (patch) | |
tree | d9b0ba4a1b65a52030820efe68a9c937c46aad1f /doc/src | |
parent | f925c79b9f36c54b67053ade5ad225a75b8dc803 (diff) | |
download | postgresql-0ac5ad5134f2769ccbaefec73844f8504c4d6182.tar.gz postgresql-0ac5ad5134f2769ccbaefec73844f8504c4d6182.zip |
Improve concurrency of foreign key locking
This patch introduces two additional lock modes for tuples: "SELECT FOR
KEY SHARE" and "SELECT FOR NO KEY UPDATE". These don't block each
other, in contrast with already existing "SELECT FOR SHARE" and "SELECT
FOR UPDATE". UPDATE commands that do not modify the values stored in
the columns that are part of the key of the tuple now grab a SELECT FOR
NO KEY UPDATE lock on the tuple, allowing them to proceed concurrently
with tuple locks of the FOR KEY SHARE variety.
Foreign key triggers now use FOR KEY SHARE instead of FOR SHARE; this
means the concurrency improvement applies to them, which is the whole
point of this patch.
The added tuple lock semantics require some rejiggering of the multixact
module, so that the locking level that each transaction is holding can
be stored alongside its Xid. Also, multixacts now need to persist
across server restarts and crashes, because they can now represent not
only tuple locks, but also tuple updates. This means we need more
careful tracking of lifetime of pg_multixact SLRU files; since they now
persist longer, we require more infrastructure to figure out when they
can be removed. pg_upgrade also needs to be careful to copy
pg_multixact files over from the old server to the new, or at least part
of multixact.c state, depending on the versions of the old and new
servers.
Tuple time qualification rules (HeapTupleSatisfies routines) need to be
careful not to consider tuples with the "is multi" infomask bit set as
being only locked; they might need to look up MultiXact values (i.e.
possibly do pg_multixact I/O) to find out the Xid that updated a tuple,
whereas they previously were assured to only use information readily
available from the tuple header. This is considered acceptable, because
the extra I/O would involve cases that would previously cause some
commands to block waiting for concurrent transactions to finish.
Another important change is the fact that locking tuples that have
previously been updated causes the future versions to be marked as
locked, too; this is essential for correctness of foreign key checks.
This causes additional WAL-logging, also (there was previously a single
WAL record for a locked tuple; now there are as many as updated copies
of the tuple there exist.)
With all this in place, contention related to tuples being checked by
foreign key rules should be much reduced.
As a bonus, the old behavior that a subtransaction grabbing a stronger
tuple lock than the parent (sub)transaction held on a given tuple and
later aborting caused the weaker lock to be lost, has been fixed.
Many new spec files were added for isolation tester framework, to ensure
overall behavior is sane. There's probably room for several more tests.
There were several reviewers of this patch; in particular, Noah Misch
and Andres Freund spent considerable time in it. Original idea for the
patch came from Simon Riggs, after a problem report by Joel Jacobson.
Most code is from me, with contributions from Marti Raudsepp, Alexander
Shulgin, Noah Misch and Andres Freund.
This patch was discussed in several pgsql-hackers threads; the most
important start at the following message-ids:
AANLkTimo9XVcEzfiBR-ut3KVNDkjm2Vxh+t8kAmWjPuv@mail.gmail.com
1290721684-sup-3951@alvh.no-ip.org
1294953201-sup-2099@alvh.no-ip.org
1320343602-sup-2290@alvh.no-ip.org
1339690386-sup-8927@alvh.no-ip.org
4FE5FF020200002500048A3D@gw.wicourts.gov
4FEAB90A0200002500048B7D@gw.wicourts.gov
Diffstat (limited to 'doc/src')
-rw-r--r-- | doc/src/sgml/pgrowlocks.sgml | 15 | ||||
-rw-r--r-- | doc/src/sgml/ref/select.sgml | 146 |
2 files changed, 105 insertions, 56 deletions
diff --git a/doc/src/sgml/pgrowlocks.sgml b/doc/src/sgml/pgrowlocks.sgml index 390fa236d31..c7714d88774 100644 --- a/doc/src/sgml/pgrowlocks.sgml +++ b/doc/src/sgml/pgrowlocks.sgml @@ -44,12 +44,6 @@ pgrowlocks(text) returns setof record <entry>Tuple ID (TID) of locked row</entry> </row> <row> - <entry><structfield>lock_type</structfield></entry> - <entry><type>text</type></entry> - <entry><literal>Shared</> for shared lock, or - <literal>Exclusive</> for exclusive lock</entry> - </row> - <row> <entry><structfield>locker</structfield></entry> <entry><type>xid</type></entry> <entry>Transaction ID of locker, or multixact ID if multitransaction</entry> @@ -65,6 +59,15 @@ pgrowlocks(text) returns setof record <entry>Transaction IDs of lockers (more than one if multitransaction)</entry> </row> <row> + <entry><structfield>lock_type</structfield></entry> + <entry><type>text[]</type></entry> + <entry>Lock mode of lockers (more than one if multitransaction), + an array of <literal>Key Share</>, <literal>Share</>, + <literal>For No Key Update</>, <literal>No Key Update</>, + <literal>For Update</>, <literal>Update</>.</entry> + </row> + + <row> <entry><structfield>pids</structfield></entry> <entry><type>integer[]</type></entry> <entry>Process IDs of locking backends (more than one if multitransaction)</entry> diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index 9963780c313..26d511fad8c 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -45,7 +45,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="parameter">expression</replac [ LIMIT { <replaceable class="parameter">count</replaceable> | ALL } ] [ OFFSET <replaceable class="parameter">start</replaceable> [ ROW | ROWS ] ] [ FETCH { FIRST | NEXT } [ <replaceable class="parameter">count</replaceable> ] { ROW | ROWS } ONLY ] - [ FOR { UPDATE | SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] [...] ] + [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] [...] ] <phrase>where <replaceable class="parameter">from_item</replaceable> can be one of:</phrase> @@ -178,7 +178,8 @@ TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] <listitem> <para> - If <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> + If <literal>FOR UPDATE</>, <literal>FOR NO KEY UPDATE</literal>, <literal>FOR SHARE</literal> + or <literal>FOR KEY SHARE</literal> is specified, the <command>SELECT</command> statement locks the selected rows against concurrent updates. (See <xref linkend="sql-for-update-share" @@ -190,8 +191,9 @@ TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] <para> You must have <literal>SELECT</literal> privilege on each column used - in a <command>SELECT</> command. The use of <literal>FOR UPDATE</literal> - or <literal>FOR SHARE</literal> requires + in a <command>SELECT</> command. The use of <literal>FOR NO KEY UPDATE</>, + <literal>FOR UPDATE</literal>, + <literal>FOR SHARE</literal> or <literal>FOR KEY SHARE</literal> requires <literal>UPDATE</literal> privilege as well (for at least one column of each table so selected). </para> @@ -873,8 +875,8 @@ SELECT DISTINCT ON (location) location, time, report <replaceable class="parameter">select_statement</replaceable> UNION [ ALL | DISTINCT ] <replaceable class="parameter">select_statement</replaceable> </synopsis><replaceable class="parameter">select_statement</replaceable> is any <command>SELECT</command> statement without an <literal>ORDER - BY</>, <literal>LIMIT</>, <literal>FOR UPDATE</literal>, or - <literal>FOR SHARE</literal> clause. + BY</>, <literal>LIMIT</>, <literal>FOR NO KEY UPDATE</>, <literal>FOR UPDATE</literal>, + <literal>FOR SHARE</literal>, or <literal>FOR KEY SHARE</literal> clause. (<literal>ORDER BY</> and <literal>LIMIT</> can be attached to a subexpression if it is enclosed in parentheses. Without parentheses, these clauses will be taken to apply to the result of @@ -910,7 +912,8 @@ SELECT DISTINCT ON (location) location, time, report </para> <para> - Currently, <literal>FOR UPDATE</> and <literal>FOR SHARE</> cannot be + Currently, <literal>FOR NO KEY UPDATE</>, <literal>FOR UPDATE</>, <literal>FOR SHARE</> and + <literal>FOR KEY SHARE</> cannot be specified either for a <literal>UNION</> result or for any input of a <literal>UNION</>. </para> @@ -925,8 +928,8 @@ SELECT DISTINCT ON (location) location, time, report <replaceable class="parameter">select_statement</replaceable> INTERSECT [ ALL | DISTINCT ] <replaceable class="parameter">select_statement</replaceable> </synopsis><replaceable class="parameter">select_statement</replaceable> is any <command>SELECT</command> statement without an <literal>ORDER - BY</>, <literal>LIMIT</>, <literal>FOR UPDATE</literal>, or - <literal>FOR SHARE</literal> clause. + BY</>, <literal>LIMIT</>, <literal>FOR NO KEY UPDATE</>, <literal>FOR UPDATE</literal>, + <literal>FOR SHARE</literal>, or <literal>FOR KEY SHARE</> clause. </para> <para> @@ -957,7 +960,8 @@ SELECT DISTINCT ON (location) location, time, report </para> <para> - Currently, <literal>FOR UPDATE</> and <literal>FOR SHARE</> cannot be + Currently, <literal>FOR NO KEY UPDATE</>, <literal>FOR UPDATE</>, <literal>FOR SHARE</> and + <literal>FOR KEY SHARE</> cannot be specified either for an <literal>INTERSECT</> result or for any input of an <literal>INTERSECT</>. </para> @@ -972,8 +976,8 @@ SELECT DISTINCT ON (location) location, time, report <replaceable class="parameter">select_statement</replaceable> EXCEPT [ ALL | DISTINCT ] <replaceable class="parameter">select_statement</replaceable> </synopsis><replaceable class="parameter">select_statement</replaceable> is any <command>SELECT</command> statement without an <literal>ORDER - BY</>, <literal>LIMIT</>, <literal>FOR UPDATE</literal>, or - <literal>FOR SHARE</literal> clause. + BY</>, <literal>LIMIT</>, <literal>FOR NO KEY UPDATE</>, <literal>FOR UPDATE</literal>, + <literal>FOR SHARE</literal>, or <literal>FOR KEY SHARE</> clause. </para> <para> @@ -1000,7 +1004,8 @@ SELECT DISTINCT ON (location) location, time, report </para> <para> - Currently, <literal>FOR UPDATE</> and <literal>FOR SHARE</> cannot be + Currently, <literal>FOR NO KEY UPDATE</>, <literal>FOR UPDATE</>, <literal>FOR SHARE</> and + <literal>FOR KEY SHARE</> cannot be specified either for an <literal>EXCEPT</> result or for any input of an <literal>EXCEPT</>. </para> @@ -1185,7 +1190,14 @@ FETCH { FIRST | NEXT } [ <replaceable class="parameter">count</replaceable> ] { </refsect2> <refsect2 id="SQL-FOR-UPDATE-SHARE"> - <title id="sql-for-update-share-title"><literal>FOR UPDATE</literal>/<literal>FOR SHARE</literal> Clause</title> + <title id="sql-for-update-share-title"><literal>FOR UPDATE</>, <literal>FOR NO KEY UPDATE</>/<literal>FOR SHARE</>/<literal>FOR KEY SHARE</> Clauses</title> + + <para> + <literal>FOR UPDATE</>, <literal>FOR NO KEY UPDATE</>, <literal>FOR SHARE</> + and <literal>FOR KEY SHARE</> + are <firstterm>locking clauses</>; they affect how <literal>SELECT</> + locks rows as they are obtained from the table. + </para> <para> The <literal>FOR UPDATE</literal> clause has this form: @@ -1195,6 +1207,13 @@ FOR UPDATE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] </para> <para> + The <literal>FOR NO KEY UPDATE</literal> clause has this form: +<synopsis> +FOR NO KEY UPDATE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] +</synopsis> + </para> + + <para> The closely related <literal>FOR SHARE</literal> clause has this form: <synopsis> FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] @@ -1202,13 +1221,30 @@ FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] </para> <para> + Similarly, the <literal>FOR KEY SHARE</> clause has this form: +<synopsis> +FOR KEY SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] [ NOWAIT ] +</synopsis> + </para> + + <para> <literal>FOR UPDATE</literal> causes the rows retrieved by the <command>SELECT</command> statement to be locked as though for update. This prevents them from being modified or deleted by other transactions until the current transaction ends. That is, other transactions that attempt <command>UPDATE</command>, - <command>DELETE</command>, or <command>SELECT FOR UPDATE</command> + <command>DELETE</command>, + <command>SELECT FOR UPDATE</command>, + <command>SELECT FOR SHARE</command> or + <command>SELECT FOR KEY SHARE</command> of these rows will be blocked until the current transaction ends. + The <literal>FOR UPDATE</> lock mode + is also acquired by any <command>DELETE</> on a row, and also by an + <command>UPDATE</> that modifies the values on certain columns. Currently, + the set of columns considered for the <command>UPDATE</> case are those that + have an unique index on them that can be used in a foreign key (so partial + indexes and expressional indexes are not considered), but this may change + in the future. Also, if an <command>UPDATE</command>, <command>DELETE</command>, or <command>SELECT FOR UPDATE</command> from another transaction has already locked a selected row or rows, <command>SELECT FOR @@ -1221,12 +1257,32 @@ FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] </para> <para> + <literal>FOR NO KEY UPDATE</> behaves similarly, except that the lock + acquired is weaker: this lock will not block + <literal>SELECT FOR KEY SHARE</> commands that attempt to acquire + a lock on the same rows. + </para> + + <para> <literal>FOR SHARE</literal> behaves similarly, except that it acquires a shared rather than exclusive lock on each retrieved row. A shared lock blocks other transactions from performing <command>UPDATE</command>, <command>DELETE</command>, or <command>SELECT FOR UPDATE</command> on these rows, but it does not prevent them - from performing <command>SELECT FOR SHARE</command>. + from performing <command>SELECT FOR SHARE</command> or + <command>SELECT FOR KEY SHARE</command>. + </para> + + <para> + <literal>FOR KEY SHARE</> behaves similarly to <literal>FOR SHARE</literal>, + except that the lock + is weaker: <literal>SELECT FOR UPDATE</> is blocked, but + not <literal>SELECT FOR NO KEY UPDATE</>. A key-shared + lock blocks other transactions from performing <command>DELETE</command> + or any <command>UPDATE</command> that changes the key values, but not + other <command>UPDATE</>, and neither it does prevent + <command>SELECT FOR UPDATE</>, <command>SELECT FOR SHARE</>, or + <command>SELECT FOR KEY SHARE</>. </para> <para> @@ -1243,41 +1299,39 @@ FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] </para> <para> - If specific tables are named in <literal>FOR UPDATE</literal> - or <literal>FOR SHARE</literal>, + If specific tables are named in a locking clause, then only rows coming from those tables are locked; any other tables used in the <command>SELECT</command> are simply read as - usual. A <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> + usual. A locking clause without a table list affects all tables used in the statement. - If <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> is + If a locking clause is applied to a view or sub-query, it affects all tables used in the view or sub-query. - However, <literal>FOR UPDATE</literal>/<literal>FOR SHARE</literal> + However, these clauses do not apply to <literal>WITH</> queries referenced by the primary query. If you want row locking to occur within a <literal>WITH</> query, specify - <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> within the - <literal>WITH</> query. + a locking clause within the <literal>WITH</> query. </para> <para> - Multiple <literal>FOR UPDATE</literal> and <literal>FOR SHARE</literal> + Multiple locking clauses can be written if it is necessary to specify different locking behavior for different tables. If the same table is mentioned (or - implicitly affected) by both <literal>FOR UPDATE</literal> and - <literal>FOR SHARE</literal> clauses, then it is processed as - <literal>FOR UPDATE</literal>. Similarly, a table is processed + implicitly affected) by more than one locking clause, + then it is processed as if it was only specified by the strongest one. + Similarly, a table is processed as <literal>NOWAIT</> if that is specified in any of the clauses affecting it. </para> <para> - <literal>FOR UPDATE</literal> and <literal>FOR SHARE</literal> cannot be + The locking clauses cannot be used in contexts where returned rows cannot be clearly identified with individual table rows; for example they cannot be used with aggregation. </para> <para> - When <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> + When a locking clause appears at the top level of a <command>SELECT</> query, the rows that are locked are exactly those that are returned by the query; in the case of a join query, the rows locked are those that contribute to @@ -1288,13 +1342,13 @@ FOR SHARE [ OF <replaceable class="parameter">table_name</replaceable> [, ...] ] <literal>LIMIT</> is used, locking stops once enough rows have been returned to satisfy the limit (but note that rows skipped over by <literal>OFFSET</> will get locked). Similarly, - if <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> + if a locking clause is used in a cursor's query, only rows actually fetched or stepped past by the cursor will be locked. </para> <para> - When <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal> + When a locking clause appears in a sub-<command>SELECT</>, the rows locked are those returned to the outer query by the sub-query. This might involve fewer rows than inspection of the sub-query alone would suggest, @@ -1307,11 +1361,9 @@ SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss WHERE col1 = 5; condition is not textually within the sub-query. </para> - <caution> - <para> - Avoid locking a row and then modifying it within a later savepoint or - <application>PL/pgSQL</application> exception block. A subsequent - rollback would cause the lock to be lost. For example: + <para> + Previous releases failed to preserve a lock which is upgraded by a later + savepoint. For example, this code: <programlisting> BEGIN; SELECT * FROM mytable WHERE key = 1 FOR UPDATE; @@ -1319,23 +1371,15 @@ SAVEPOINT s; UPDATE mytable SET ... WHERE key = 1; ROLLBACK TO s; </programlisting> - After the <command>ROLLBACK</>, the row is effectively unlocked, rather - than returned to its pre-savepoint state of being locked but not modified. - This hazard occurs if a row locked in the current transaction is updated - or deleted, or if a shared lock is upgraded to exclusive: in all these - cases, the former lock state is forgotten. If the transaction is then - rolled back to a state between the original locking command and the - subsequent change, the row will appear not to be locked at all. This is - an implementation deficiency which will be addressed in a future release - of <productname>PostgreSQL</productname>. - </para> - </caution> + would fail to preserve the <literal>FOR UPDATE</> lock after the + <command>ROLLBACK</>. This has been fixed in release 9.2. + </para> <caution> <para> It is possible for a <command>SELECT</> command running at the <literal>READ COMMITTED</literal> transaction isolation level and using <literal>ORDER - BY</literal> and <literal>FOR UPDATE/SHARE</literal> to return rows out of + BY</literal> and a locking clause to return rows out of order. This is because <literal>ORDER BY</> is applied first. The command sorts the result, but might then block trying to obtain a lock on one or more of the rows. Once the <literal>SELECT</> unblocks, some @@ -1765,14 +1809,16 @@ SELECT distributors.* WHERE distributors.name = 'Westward'; </refsect2> <refsect2> - <title><literal>FOR UPDATE</> and <literal>FOR SHARE</></title> + <title><literal>FOR NO KEY UPDATE</>, <literal>FOR UPDATE</>, <literal>FOR SHARE</>, <literal>FOR KEY SHARE</></title> <para> Although <literal>FOR UPDATE</> appears in the SQL standard, the standard allows it only as an option of <command>DECLARE CURSOR</>. <productname>PostgreSQL</productname> allows it in any <command>SELECT</> query as well as in sub-<command>SELECT</>s, but this is an extension. - The <literal>FOR SHARE</> variant, and the <literal>NOWAIT</> option, + The <literal>FOR NO KEY UPDATE</>, <literal>FOR SHARE</> and + <literal>FOR KEY SHARE</> variants, + as well as the <literal>NOWAIT</> option, do not appear in the standard. </para> </refsect2> |