Concurrency Control
Databases have long tried to hide concurrency issues from application developers by providing Transaction Isolation. In theory, isolation should make your life easier by letting you pretend that no concurrency is happening.
Read Uncommitted
This doesn’t provide any isolation (if yes, it will only prevent Dirty Write).
Weak Isolation Levels
Read Committed
This prevents:
- Dirty writes:
- Definition: One client overwrites data that another client has written, but not yet committed. Almost all transaction implementations prevent dirty writes.
- Implementation: This can be implemented using row level lock.
- Dirty reads:
- Definition: One client reads another client’s writes before they have been committed. The read committed isolation level and stronger levels prevent dirty reads.
- Implementation: This can also leverage the previously discussed lock. However, a long running write will cause other read-only queries to wait. A better approach would be to remember both old-committed value and newly-written-but-not-yet-committed value that holds the lock.
Snapshot Isolation - Repeatable Reads
Reads never block Writes and Vice Versa. Note: Repeatable Read is a term from SQL Standard - which is old (System R’s 1975 definition of isolation levels, the term “Snapshot isolation” hasn’t been invented yet), ambiguous, imprecise, and not as implementation-independent as a standard should be. Therefore there is confusion in “Repeatable Read” definition, and “Snapshot Isolation” implementations vary significantly.
These prevent:
- Read skew (non-repeatable reads):
- Definition: A client sees different parts of the database at different points in time. This issue is most commonly prevented with snapshot isolation, which allows a transaction to read from a consistent snapshot at one point in time.
- Implementation: It is usually implemented with multi-version concurrency control (MVCC).
- Lost updates:
- Definition: Two clients concurrently perform a read-modify-write cycle. One overwrites the other’s write without incorporating its changes, so data is lost.
- Implementation: some databases prevent this anomaly automatically (detects and aborts the transaction), some leverage atomic compare-and-set operation, while others require a manual lock (SELECT FOR UPDATE).
Strong Isolation Level - Serializability
This prevents:
- Write skew:
- Definition: A transaction reads something, makes a decision based on the value it saw, and writes the decision to the database. However, by the time the write is made, the premise of the decision is no longer true. Only serializable isolation prevents this anomaly.
- Phantom reads:
- Definition: A transaction reads objects that match some search condition. Another client makes a write that affects the results of that search. Snapshot isolation prevents straightforward phantom reads, but phantoms in the context of write skew require special treatment, such as index-range locks. There are 3 implementations (of 2 groups):
- Pessimistic: Low performance
- Actual Serial Execution: Single Threaded, Serial order execution. Tips are:
- Stored Procedure.
- Partitioning.
- Small and Fast transaction.
- Two Phases Locking - 2PL: Reads and Writes block each other. Widely used locks:
- Predicate Lock.
- Index Range Lock.
- Actual Serial Execution: Single Threaded, Serial order execution. Tips are:
- Optimistic - Serializable Snapshot Isolation (SSI): An integration between Snapshot Isolation and Two Phases Locking - 2PL - New and is getting more widely adopted
- All reads within a transaction are made from a consistent snapshot of a Database.
- Add algorithm to detect serialization conflicts among writes and which transactions to abort aka Decisions based on outdated premise.
- Detecting stale MVCC reads: the database needs to track when a transaction ignores another transaction’s writes due to MVCC visibility rules. When the transaction wants to commit, the database checks whether any of the ignored writes have now been committed. If so, the transaction must be aborted.
- Detecting writes that affect prior reads: use a similar locking technique from 2PL like Predicate Lock or Index Range Lock, except that SSI locks don’t block other transactions. When a transaction writes to the database, it must look in the indexes for any other transactions that have recently read the affected data. This process is similar to acquiring a write lock on the affected key range, but rather than blocking until the readers have committed, the lock acts as a tripwire: it simply notifies the transactions that the data they read may no longer be up to date.
Note
Due to the nature of detecting conflict and aborting the transaction, Clients under Snapshot Isolation and SSI schemes need to handle Conflict when being aborted.