You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Assume that there is a table which controls stock amount information.
CREATETABLEproducts(
id INTEGERPRIMARY KEY,
remaining_amount INTEGERNOT NULL
);
INSERT INTO products(id, remaining_amount) VALUES (1, 1);
Now, user A and B try to take the last stock at the same time.
A/B: UPDATE products
SET remaining_amount = remaining_amount -1WHERE id =1and remaining_amount >0;
I've found that for UPDATE statements which include the field to be updated in the WHERE condition, a pessimistic lock is implicitly obtained regardless of transaction isolation levels. So we don't have to execute explicitly SELECT ... FOR UPDATE before simple UPDATE statements.
A
B
SET TRANSACTION ISOLATIONLEVEL READ UNCOMMITTED; BEGIN;
SET TRANSACTION ISOLATIONLEVEL READ UNCOMMITTED; BEGIN;
UPDATE products SET remaining_amount = remaining_amount - 1 WHERE remaining_amount > 0;
However, it is not guaranteed to be conflict-free whenever a SELECT subquery is included. Our investigation revealed that the results vary depending on the transaction isolation level, as follows:
UPDATE t SET v=v-1 WHERE id=1 AND v>0
UPDATE t SET v=v-1 WHERE EXISTS(SUBQUERY)
UPDATE t SET v=(SUBQUERY)-1 WHERE id=1 AND v>0
Postgres
For simple updates or subquery SET, use READ COMMITTED.
For complex subquery WHERE, use REPEATABLE READ and retry on serialization errors.
Simple
Subquery WHERE
Subquery SET
READ COMMITTED
✅
❌ 5/6 Broken
✅
REPEATABLE READ
❗ Serialization Error
❗ Serialization Error
❗ Serialization Error
SERIALIZABLE
❗ Serialization Error
❗ Serialization Error
❗ Serialization Error
Subquery WHERE with READ COMMITTED will be broken:
[B] Latter \ [A] Former
Before-Read Delay
Pre-Write Delay
Post-Write Delay
Act before A's commit
❌ Broken
❌ Broken
❌ Broken
Act after A's commit
✅
❌ Broken
❌ Broken
MySQL
For simple updates or subquery SET, any transaction isolation level works well.READ UNCOMMITTED or READ COMMITTED are recommended.
For complex subquery WHERE, use REPEATABLE READ and retry on deadlock errors.
Simple
Subquery WHERE
Subquery SET
READ UNCOMMITTED
✅
❌ 4/6 Broken
✅
READ COMMITTED
✅
❌ 5/6 Broken
✅
REPEATABLE READ
✅
❗ 1/6 Deadlock
✅
SERIALIZABLE
✅
❗ 1/6 Deadlock
✅
Subquery WHERE with READ UNCOMMITTED will be broken:
[B] Latter \ [A] Former
Before-Read Delay
Pre-Write Delay
Post-Write Delay
Act before A's commit
✅
❌ Broken
❌ Broken
Act after A's commit
✅
❌ Broken
❌ Broken
Subquery WHERE with READ COMMITTED will be broken:
[B] Latter \ [A] Former
Before-Read Delay
Pre-Write Delay
Post-Write Delay
Act before A's commit
❌ Broken
❌ Broken
❌ Broken
Act after A's commit
✅
❌ Broken
❌ Broken
Subquery WHERE with REPEATABLE READ will get deadlocks:
[B] Latter \ [A] Former
Before-Read Delay
Pre-Write Delay
Post-Write Delay
Act before A's commit
✅
✅
✅
Act after A's commit
✅
✅
❗ Deadlock
Subquery WHERE with SERIALIZABLE will get deadlocks:
[B] Latter \ [A] Former
Before-Read Delay
Pre-Write Delay
Post-Write Delay
Act before A's commit
✅
✅
❗ Deadlock
Act after A's commit
✅
✅
✅
About
WHERE 条件のフィールドを UPDATE するのって,明示的にロックしてなくても安全?全パターン調べてみました! - Qiita