SELECT FOR UPDATE statement is used to order transactions by controlling concurrent access to one or more rows of a table.
It works by locking the rows returned by a selection query, such that other transactions trying to access those rows are forced to wait for the transaction that locked the rows to finish. These other transactions are effectively put into a queue based on when they tried to read the value of the locked rows.
Because this queueing happens during the read operation, the thrashing that would otherwise occur if multiple concurrently executing transactions attempt to SELECT the same data and then UPDATE the results of that selection is prevented. By preventing thrashing, CockroachDB also prevents transaction retries that would otherwise occur due to contention.
As a result, using SELECT FOR UPDATE leads to increased throughput and decreased tail latency for contended operations.
Note that using SELECT FOR UPDATE does not completely eliminate the chance of serialization errors, which use the SQLSTATE error code 40001, and emit error messages with the string restart transaction. These errors can also arise due to time uncertainty. To eliminate the need for application-level retry logic, in addition to SELECT FOR UPDATE your application also needs to use a driver that implements automatic retry handling.
CockroachDB does not support the FOR SHARE or FOR KEY SHARE locking strengths.
By default, CockroachDB uses the
SELECT FOR UPDATE locking mechanism during the initial row scan performed in UPDATE and UPSERT statement execution. To turn off implicit SELECT FOR UPDATE locking for UPDATE and UPSERT statements, set the enable_implicit_select_for_update session variable to false.Syntax
The following diagram shows the supported syntax for the optionalFOR locking clause of a SELECT statement.
SELECT statement syntax documentation, see Selection Queries.
Parameters
Locking strengths
Locking strength dictates the row-level locking behavior on rows retrieved by aSELECT statement.
Wait policies
Wait policies determine how aSELECT FOR UPDATE statement handles conflicts with locks held by other active transactions. By default, SELECT FOR UPDATE queries on rows that are already locked by an active transaction must wait for the transaction to finish.
For documentation on all other parameters of a
SELECT statement, see Selection Queries.
Required privileges
The user must have theSELECT and UPDATE privileges on the tables used as operands.
Known limitations
Locks acquired usingSELECT ... FOR UPDATE SELECT ... FOR UPDATE are dropped on lease transfers and range splits and merges. SELECT ... FOR UPDATE locks should be thought of as best-effort, and should not be relied upon for correctness, as they are implemented as fast, in-memory unreplicated locks.
If a lease transfer or range split/merge occurs on a range held by an unreplicated lock, the lock is dropped, and the following behaviors can occur:
- The desired ordering of concurrent accesses to one or more rows of a table expressed by your use of
SELECT ... FOR UPDATEmay not be preserved (that is, a transaction B against some table T that was supposed to wait behind another transaction A operating on T may not wait for transaction A). - The transaction that acquired the (now dropped) unreplicated lock may fail to commit, leading to transaction retry errors with code
40001and therestart transactionerror message.
Examples
Enforce transaction order when updating the same rows
This example usesSELECT FOR UPDATE to lock a row inside a transaction, forcing other transactions that want to update the same row to wait for the first transaction to complete. The other transactions that want to update the same row are effectively put into a queue based on when they first try to read the value of the row.
This example assumes you are running a local unsecured cluster.
First, connect to the running cluster (call this Terminal 1):
SELECT FOR UPDATE statement from Terminal 2 will appear to “wait”.
Back in Terminal 1, update the row and commit the transaction:

