Files
nexus/sreweekly/markdown/454/05-solutions-to-the-lost-update-problem.md
2026-09-12 17:23:01 +08:00

50 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Solutions to the Lost Update Problem
- **期号**: SRE Weekly Issue #454(2024-12-08)
- **作者**: Sönke Ruempler
- **链接**: https://ruempler.eu/2024/11/25/solutions-to-the-lost-update-problem/
## 简介
This article has a couple of strategies for handling concurrent updates to the same row in MySQL, with and without locking.
## 正文
The **Lost Update Problem** is a common issue in concurrent systems, where two transactions read the same data, modify it, and write it back to the database. The second transaction will overwrite the changes made by the first transaction, causing the first transaction’s changes to be lost.
This is especially problematic in cases where money in a bank account is involved, as it can lead to inconsistencies in the account balance. A rather dramatic example of this is the [Flexcoin bankruptcy](https://www.reuters.com/article/technology/bitcoin-bank-flexcoin-shuts-after-hacking-theft-idUSBREA2329B/).
For example, in MySQL the default isolation level is `REPEATABLE READ`, which means that a transaction will read the same data multiple times and get the same result. However, this does not prevent the Lost Update Problem, as shown in this sequence diagram:
Fortunately, there are several ways to solve the Lost Update Problem:
## `SERIALIZABLE`
`SERIALIZABLE` is the strictest isolation level, which prevents the Lost Update Problem by bailing out transactions that would cause conflicts:
In MySQL, `SERIALIZABLE` is the same as `SELECT ... FOR SHARE`, which means reads are not blocked, but writes are. This can lead to deadlocks, as shown in the sequence diagram above.
##
`SELECT ... FOR UPDATE` is a way to lock rows for writing, which prevents other transactions from reading or writing the same rows. This is useful when you want to prevent the Lost Update Problem, but do not want to use the `SERIALIZABLE` isolation level:
##
This approach involves adding a version column to the table, which is incremented every time a row is updated. When a transaction updates a row, it checks whether the version has changed since it was read. If the version has changed, the transaction will fail and the changes will not be applied. This approach is useful when conflicts are rare, as it avoids the overhead of locking and blocking transactions:
As an alternative, you can use a `WHERE` clause with the old value, instead of a version column. This is useful when the table does not have a version column:
##
If locking via the database is not enough for your use case, you can implement your own locking mechanism, e.g. with Redis or Memcached.
##
- [A beginner’s guide to database locking and the lost update phenomena](https://vladmihalcea.com/a-beginners-guide-to-database-locking-and-the-lost-update-phenomena/)
- [Isolation Levels in MySQL](https://dev.mysql.com/doc/refman/8.4/en/innodb-transaction-isolation-levels.html)
- [The race condition that led to Flexcoin bankruptcy](https://vladmihalcea.com/race-condition/)
### Like what you read?
You can [hire me](https://cv.ruempler.eu/) or [make a donation via PayPal](https://www.paypal.me/s0enke)!