Transactions and Concurrency Control Patterns by Vlad Mihalcea
Transactions and Concurrency Control are very of paramount importance when it comes to enterprise systems data integrity. However, this topic is very tough since you have to understand the inner workings of the database system, its concurrency control design choices (e.g. 2PL, MVCC), transaction isolation levels and locking schemes.
In this presentation, I’m going to explain what data anomalies can happen depending on the transaction isolation level, with references to Oracle, SQL Server, PostgreSQL, and MySQL.
I will also demonstrate that database transactions are not enough, especially for multi-request web flows. For this reason, I’m going to present multiple application-level transaction patterns based on both optimistic and pessimistic locking mechanisms.
Last, I’m going to talk about Concurrency Control strategies used in the Hibernate second-level caching mechanism, which can boost performance without compromising strong consistency.
5. Poloniex got hacked – 12.3% of BTC stolen
https://bitcointalk.org/index.php?topic=499580
6. Poloniex got hacked – 12.3% of BTC stolen
“The hacker discovered that if you place several
withdrawals all in practically the same instant, they
will get processed at more or less the same time.
This will result in a negative balance, but valid
insertions into the database, which then get picked up
by the withdrawal daemon.”
https://bitcointalk.org/index.php?topic=499580
7. Testing time
public void transfer(String fromIban, String toIban, Long transferCents) {
Long fromBalance = getBalance(fromIban);
if(fromBalance >= transferCents) {
addBalance(fromIban, (-1) * transferCents);
addBalance(toIban, transferCents);
}
}
15. Read-Modify-Write
public void transfer(String fromIban, String toIban, Long transferCents) {
Long fromBalance = getBalance(fromIban);
if(fromBalance >= transferCents) {
addBalance(fromIban, (-1) * transferCents);
addBalance(toIban, transferCents);
}
}
16. Read-Modify-Write
public void transfer(String fromIban, String toIban, Long transferCents) {
Long fromBalance = getBalance(fromIban);
if(fromBalance >= transferCents) {
addBalance(fromIban, (-1) * transferCents);
addBalance(toIban, transferCents);
}
}
17. What went wrong?
• Reads and writes were executed in separate transactions
• Designed for serial execution
• ACIDRain [1]
[1]: http://www.bailis.org/papers/acidrain-sigmod2017.pdf
18. Jim Gray – 1981 – Transaction properties
“A transaction is a transformation of
state which has the properties of
atomicity (all or nothing), durability
(effects survive failures) and
consistency (a correct
transformation).”
http://research.microsoft.com/en-us/um/people/gray/papers/theTransactionConcept.pdf
29. Jim Gray – 1981 – Concurrency Control
“A simpler and more efficient scheme is to lock an
object when it is accessed. ... If the object is already
locked, then the requestor waits.
Multiple readers can be accommodated by
distinguishing two lock modes: one indicating update
access and another indicating read access. Read locks
are compatible while update locks are not.”
http://research.microsoft.com/en-us/um/people/gray/papers/theTransactionConcept.pdf
40. Challenges have changed
“At present [1981], the largest airlines and
banks have about 10,000 terminals and about
100 active transactions at any instant.
These transactions live for a second or two and are
gone forever.”
The Transaction Concept: Virtues and Limitations - http://research.microsoft.com/en-us/um/people/gray/papers/theTransactionConcept.pdf
86. SQL Standard Isolation Levels
Isolation
Level Dirty Read
Non-
Repeatable
Read
Phantom
Read
Read
Uncommitted Yes Yes Yes
Read
Committed No Yes Yes
Repeatable
Read No No Yes
Serializable No No No
87. Oracle Isolation Levels
Isolation
Level Dirty Read
Non-
Repeatable
Read
Phantom
Read Read Skew Write Skew Lost Update
Read
Committed No Yes Yes Yes Yes Yes
Serializable No No No No Yes No
88. Isolation level Dirty Read
Non-
Repeatable
Read
Phantom
Read Read Skew Write Skew Lost Update
Read
Uncommitted Yes Yes Yes Yes Yes Yes
Read
Committed No Yes Yes Yes Yes Yes
Repeatable
Read No No Yes No No No
Serializable No No No No No No
Read
Committed SI No Yes Yes Yes Yes Yes
Snapshot
Isolation No No No No Yes No
SQL Server Isolation Levels
89. Isolation level Dirty Read
Non-
Repeatable
Read
Phantom
Read Read Skew Write Skew Lost Update
Read
Uncommitted No Yes Yes Yes Yes Yes
Read
Committed No Yes Yes Yes Yes Yes
Repeatable
Read No No No No Yes No
Serializable No No No No No No
PostgreSQL Isolation Levels
90. MySQL Isolation Levels
Isolation level Dirty Read
Non-
Repeatable
Read
Phantom
Read Read Skew Write Skew Lost Update
Read
Uncommitted Yes Yes Yes Yes Yes Yes
Read
Committed No Yes Yes Yes Yes Yes
Repeatable
Read No No No No Yes Yes
Serializable No No No No No No
92. ACID – Internet Era
• What about multi-request logical transactions?
• DB-level ACID is no longer sufficient
• Application-level transactions require application-level concurrency
control
113. Versionless optimistic locking
@Entity
@Table(name = "post")
@DynamicUpdate
@OptimisticLocking(type = OptimisticLockType.DIRTY)
public class Post {
@Id
private Long id;
private String title;
private long views;
private int likes;
}
118. Explicit optimistic locking modes
Lock Mode Type Description
NONE In the absence of explicit locking, the application will use implicit locking (optimistic or pessimistic)
OPTIMISTIC
Always issues a version check upon transaction commit, therefore ensuring optimistic locking
repeatable reads.
READ Same as OPTIMISTIC.
OPTIMISTIC_FORCE_INCREMENT
Always increases the entity version (even when the entity doesn’t change) and issues a version
check upon transaction commit, therefore ensuring optimistic locking repeatable reads.
WRITE Same as OPTIMISTIC_FORCE_INCREMENT.
PESSIMISTIC_READ
A shared lock is acquired to prevent any other transaction from acquiring a PESSIMISTIC_WRITE
lock.
PESSIMISTIC_WRITE
An exclusive lock is acquired to prevent any other transaction from acquiring a PESSIMISTIC_READ
or a PESSIMISTIC_WRITE lock.
PESSIMISTIC_FORCE_INCREMENT
A database lock is acquired to prevent any other transaction from acquiring a PESSIMISTIC_READ or
a PESSIMISTIC_WRITE lock and the entity version is incremented upon transaction commit.
124. Google Developers
“We believe it is better to have application
programmers deal with performance problems
due to overuse of transactions as bottlenecks arise,
rather than always coding around
the lack of transactions.”
Spanner: Google’s Globally-Distributed Database - https://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf