ApiaryActive
Try: pause · settings · learn · wipe
← Community / Reading Room
DC
knowledge · 18 min read

Database Concurrency Control

At its heart, concurrency control answers a simple question: When two or more transactions want to access the same piece of data, how do we decide who gets to…

The hum of a busy hive, the chatter of thousands of agents, the thrum of a data center—each is a world where many actors must work together without stepping on each other’s wings. In the digital realm, that choreography is called concurrency control. It is the invisible set of rules that lets dozens, hundreds, or even millions of transactions read and write the same data at the same time while guaranteeing correctness, durability, and fairness. For the Apiary community—whether you are a researcher tracking bee health, a developer building self‑governing AI agents, or a conservationist designing a citizen‑science platform—understanding these mechanisms is not a luxury; it is the foundation for trustworthy, scalable systems.

In the next few thousand words we will walk through the core ideas, the classic algorithms, the modern twists, and the concrete numbers that show why a well‑tuned concurrency control layer can be the difference between a thriving ecosystem and a chaotic crash. You’ll see how locking, timestamps, and versioning keep data consistent, how deadlocks are detected before they become a hive‑collapse, and how the same principles that keep a database safe can inspire resilient coordination among bees and autonomous agents alike.


1. Foundations of Concurrency Control

At its heart, concurrency control answers a simple question: When two or more transactions want to access the same piece of data, how do we decide who gets to read, write, or modify it, and in what order? The answer must satisfy three classic properties—Atomicity, Consistency, Isolation, and Durability (the ACID guarantees). While durability is handled by logging and recovery, isolation is the direct responsibility of concurrency control.

The most widely studied model is the transaction: a sequence of read and write operations that should appear to execute atomically. In a relational database, a transaction might transfer $500 from Alice’s account to Bob’s, update the inventory of a product, or log a sensor reading from a hive temperature probe. If two such transactions interleave incorrectly, we can get lost updates (both think they wrote the same value), dirty reads (reading uncommitted data), or non‑repeatable reads (seeing different values on successive reads).

To avoid these anomalies, the system defines a schedule—the order in which operations actually execute. A schedule is conflict‑serializable if it is equivalent to some serial execution (one transaction after another). Concurrency control mechanisms aim to guarantee conflict‑serializability (or a weaker but still useful property such as snapshot isolation) while allowing as much parallelism as possible.

In practice, the implementation choices fall into three broad families:

  1. Lock‑based protocols – transactions explicitly acquire locks on data items before accessing them.
  2. Timestamp‑based protocols – each transaction receives a logical timestamp that determines a global ordering.
  3. Optimistic protocols – transactions proceed without locks, but validate at commit time that no conflicting writes occurred.

Each family has trade‑offs in terms of latency, throughput, and complexity. The sections that follow unpack the mechanics, the mathematics, and the real‑world numbers behind each approach.


2. Lock‑Based Protocols

2.1 Two‑Phase Locking (2PL)

The classic algorithm for guaranteeing conflict‑serializability is Two‑Phase Locking. A transaction goes through two distinct phases:

  1. Growing phase – it may request any number of shared (S) or exclusive (X) locks, but never releases any.
  2. Shrinking phase – it may release locks, but cannot acquire any new ones.

Because all lock requests happen before any releases, the set of locks held by a transaction defines a critical region that cannot be interleaved with another transaction’s conflicting region. The resulting schedule is guaranteed to be conflict‑serializable.

In practice, most commercial DBMSs adopt a stricter variant called Strict 2PL, where a transaction holds all its exclusive locks until it commits or aborts. This eliminates cascading aborts—situations where a transaction must roll back because a prior transaction that it read from later aborts.

A concrete example: imagine a beekeeping app that records the number of honey frames harvested each day. Transaction T1 reads the current count (shared lock), increments it, and writes the new value (exclusive lock). Transaction T2 concurrently reads the same count to compute a daily average. Under Strict 2PL, T2 will be blocked from reading the exclusive lock until T1 commits, guaranteeing that the average uses the final, committed value.

2.2 Lock Granularity

Locks can be taken at many granularities:

GranularityTypical SizeExampleContentionOverhead
TableWhole tableSELECT * FROM hive_logsLow parallelism, high contentionMinimal lock table entries
Page8 KB–64 KB (depends on DB)Reading a page of sensor dataModerateOne lock per page
RowSingle recordUpdating a single bee’s health recordHigh parallelism, low contentionMany lock entries
ColumnSingle column (rare)Updating only the “temperature” fieldVery fine‑grainedComplex lock manager

Fine‑grained locks increase parallelism but also increase the size of the lock table and the cost of deadlock detection. Coarse‑grained locks simplify management but can throttle throughput. Modern DBMSs often employ hierarchical locking (also called multiple granularity locking) where a transaction can lock a table, a page, and a row simultaneously, using intention locks (IS, IX) to signal its intent without blocking unrelated rows.

2.3 Real‑World Performance

A 2020 benchmark from the TPC‑C (Transaction Processing Performance Council) suite measured InnoDB (MySQL’s default engine) on a 64‑core server handling 200 k transactions per second (TPS) with a mixture of read‑only and update workloads. The lock manager accounted for ≈ 2 % of CPU time, but lock wait time contributed ≈ 12 % of total latency when the average lock hold time rose above 0.5 ms.

By switching from row‑level to page‑level locking for a read‑heavy workload, the same benchmark saw a 15 % reduction in lock wait time, at the cost of a modest 3 % increase in lock contention for write‑heavy mixes. The takeaway: choose lock granularity based on the read‑write ratio and the typical size of data accessed.


3. Deadlock Detection and Prevention

3.1 What Is a Deadlock?

A deadlock occurs when two or more transactions each hold a lock the other needs, forming a cycle that can never be resolved without external intervention. In a hive, think of two worker bees each waiting for the other to finish a task they both need to complete—no progress is possible.

Consider two transactions:

  • T1: lock X on row A → lock X on row B
  • T2: lock X on row B → lock X on row A

If T1 acquires the lock on A first, T2 will block waiting for B, and then T1 will block waiting for B, which T2 holds. The system must detect this cycle and break it.

3.2 Detection Algorithms

Most DBMSs use a wait‑for graph (WFG): a directed graph where each node is a transaction, and an edge T_i → T_j means T_i is waiting for a lock held by T_j. A deadlock exists when the graph contains a cycle.

  • Periodic detection – the lock manager periodically scans the WFG (e.g., every 100 ms) and aborts one transaction in each cycle (the victim). PostgreSQL’s default deadlock detection interval is 1 second.
  • Immediate detection – every time a lock request is blocked, the manager checks whether adding the edge would create a cycle. This is more responsive but incurs higher CPU overhead.

In a 2019 study of Oracle’s lock manager on a 128‑core server, periodic detection caused an average deadlock resolution time of ≈ 250 ms, while immediate detection cut that to ≈ 80 ms, at the cost of a 3 % increase in lock manager CPU usage.

3.3 Prevention Techniques

Prevention aims to eliminate cycles a priori:

  • Ordered locking – transactions acquire locks in a globally agreed order (e.g., by primary key). This guarantees that no cycle can form, at the expense of flexibility.
  • Timeouts – a transaction that waits longer than a configurable threshold (e.g., 5 seconds) is automatically aborted. While simple, timeouts can cause unnecessary rollbacks if the system is momentarily congested.

In practice, most systems combine detection with a simple prevention rule such as no lock upgrades (a transaction may not convert a shared lock to an exclusive lock without first releasing the shared lock). This eliminates a common deadlock pattern without sacrificing much concurrency.


4. Timestamp Ordering

4.1 Basic Timestamp Protocol

Timestamp ordering (TSO) assigns each transaction a unique logical timestamp—often a 64‑bit integer generated by a monotonic counter or by combining a node identifier with a local counter (Lamport timestamps). The timestamp defines a global serial order: earlier timestamps must appear before later ones.

The protocol enforces two rules for each data item X:

  1. Read rule – a transaction T with timestamp TS(T) may read X only if TS(T) ≤ WTS(X), where WTS(X) is the timestamp of the last transaction that wrote X. If TS(T) < WTS(X), the read is rejected and T is aborted (a read‑old conflict).
  2. Write ruleT may write X only if TS(T) ≥ RTS(X), where RTS(X) is the timestamp of the last transaction that read X. If TS(T) < RTS(X), the write is rejected and T aborts (a write‑old conflict).

Because timestamps never change, the system can guarantee strict serializability without any explicit lock acquisition.

4.2 Example: Hive Temperature Logging

Suppose a temperature sensor writes a reading every second. Transaction T1 (timestamp 100) writes the value 35 °C. A monitoring service starts a transaction T2 (timestamp 101) that reads the temperature to compute a moving average. If T2 reads before T1 commits, the read rule blocks T2 (since TS(T2) > WTS(temperature)) and forces it to wait, preserving consistency.

If later a maintenance script tries to retroactively correct a faulty reading (timestamp 99), the write rule will reject it because RTS(temperature) = 101 (the later read). This prevents “old” data from overwriting newer, already‑observed values.

4.3 Performance Characteristics

Timestamp ordering eliminates lock contention, which can dramatically improve throughput for read‑only workloads. In a 2018 benchmark on a 48‑core PostgreSQL cluster using snapshot isolation (a form of TSO), read‑only transactions achieved ≈ 5 M queries per second (QPS) with a median latency of 2.7 ms.

However, the cost is higher abort rates under write‑heavy mixes. The same benchmark reported a 23 % abort rate when 30 % of transactions were updates, because many writes conflicted with concurrent reads. Systems that can tolerate occasional aborts (e.g., AI agents that can retry) may still profit from TSO’s simplicity.


5. Optimistic Concurrency Control

5.1 The Optimistic Paradigm

Optimistic Concurrency Control (OCC) assumes that conflicts are rare. Transactions execute without acquiring any locks, reading a snapshot of the database. At commit time, the system validates that no other transaction has written to any data item that the transaction read. If validation succeeds, the transaction’s writes are applied; otherwise, the transaction aborts and retries.

The classic three‑phase OCC algorithm:

  1. Read phase – transaction reads data and records the version numbers (or timestamps) of each item.
  2. Validation phase – transaction checks that none of the recorded versions have changed since the read phase.
  3. Write phase – if validation passes, the transaction writes its updates atomically (often using a compare‑and‑swap operation).

Because no locks are held, OCC eliminates lock wait times entirely.

5.2 Real‑World Example: Mobile Bee‑Tracking App

A field researcher uses a smartphone app to log the location of a tagged bee. The app works offline, storing a local SQLite database. When a network connection becomes available, the app pushes all pending transactions to a central server. Each transaction carries a client‑generated timestamp and a list of keys it read (e.g., the bee’s last known position).

The server runs an OCC validator: if any other client has updated the same bee’s location after the timestamp, the incoming transaction is rejected, and the client receives a conflict response. The client then merges the newer location and retries. This workflow lets thousands of researchers work concurrently without heavy lock management, while still guaranteeing that the central repository reflects a consistent history of bee movements.

5.3 Trade‑offs and Numbers

A 2021 study of Microsoft SQL Server’s snapshot isolation (an OCC variant) on a 64‑core Azure VM processed ≈ 1.2 M mixed read/write transactions per second. The abort rate was ≈ 4 % for a workload where 15 % of transactions performed updates—well within acceptable limits for many applications.

When the write proportion rose to 40 %, aborts climbed to ≈ 18 %, and overall throughput dropped by ≈ 12 % because retries consumed CPU cycles. The authors concluded that OCC is ideal for read‑dominant workloads (read‑only > 80 %) or for environments where retries are cheap (e.g., AI agents that can quickly re‑plan).


6. Multi‑Version Concurrency Control (MVCC)

6.1 Core Idea

MVCC expands the optimistic concept by keeping multiple versions of each data item. Each version is tagged with the timestamp of the transaction that created it. Readers can select the version that was visible at the start of their transaction, while writers create a new version without overwriting the old one.

Because readers never block writers (and vice versa), MVCC provides high read scalability. The classic implementation stores a write‑timestamp (WTS) and a read‑timestamp (RTS) per row, plus a pointer to the previous version.

6.2 Snapshot Isolation

A widely used MVCC variant is Snapshot Isolation (SI). When a transaction starts, it receives a snapshot timestamp S. All reads are performed against the version whose WTS ≤ S and RTS > S (i.e., the latest version committed before S). At commit, the transaction checks whether any concurrent transaction has written to a row that it also wrote (the write‑write conflict). If none exist, the transaction commits; otherwise, it aborts.

SI guarantees serializability for many workloads but suffers from a known anomaly called write skew. Consider two transactions that each read a set of rows and, based on the read, decide not to update a particular row. If both decide independently, the combined effect may violate a global invariant.

6.3 Implementation Details

PostgreSQL, MySQL’s InnoDB, and Oracle all implement MVCC with a transaction ID (XID) that increments monotonically. In PostgreSQL, each row stores xmin (creator XID) and xmax (deleter XID). A row is visible to transaction T if xmin ≤ T.xid < xmax.

MVCC introduces garbage collection challenges: old versions must be removed once no active transaction can see them. PostgreSQL’s VACUUM process scans tables, identifies dead tuples, and rewrites pages. In a 2022 benchmark on a 128‑core server, VACUUM reclaimed ≈ 1.8 GB of dead tuples per hour on a workload that inserted 10 M rows per minute, consuming ≈ 7 % of overall CPU.

6.4 Numbers

A 2023 TPC‑C run on a PostgreSQL cluster with 96 vCPUs and 1 TB of RAM achieved ≈ 4.7 M transactions per second under SI, with an average latency of 3.1 ms. Write‑only transactions (100 % updates) saw a modest ≈ 15 % drop in throughput compared to a lock‑based configuration, but read‑only transactions were ≈ 2× faster because they never waited for locks.

These results illustrate why MVCC is the default for many web‑scale applications (e.g., GitHub, Uber) and why it meshes well with AI pipelines that need to read large datasets while new models are being trained and stored concurrently.


7. Granularity and Hierarchical Locking

7.1 Why Granularity Matters

Lock granularity is a lever for balancing parallelism against overhead. Coarse locks (tables) are cheap to manage but can cause unnecessary blocking; fine locks (rows) allow more concurrency but increase the size of the lock table and the likelihood of deadlocks.

A classic experiment from the 1990s (Gray & Reuter, Transaction Processing) showed that moving from table‑level to row‑level locking on a banking workload increased throughput by ≈ 3.5× on a 16‑processor system, while lock‑table memory usage rose from ≈ 2 MB to ≈ 28 MB. Modern hardware can easily accommodate the larger lock tables, but the principle remains: choose the finest granularity that your hardware and workload can sustain.

7.2 Intent Locks and Hierarchies

To avoid lock escalation (where many fine‑grained locks are automatically promoted to a coarse lock), systems use intent locks:

  • IS (Intent Shared) – signals that a transaction will acquire shared locks on lower‑level items.
  • IX (Intent Exclusive) – signals that a transaction will acquire exclusive locks on lower‑level items.

A transaction that needs an exclusive lock on a row first acquires IX on the containing page and IX on the table. Other transactions that only need a shared lock on a different row can still acquire IS on the page, allowing both to coexist.

7.3 Real‑World Scenario: Bee‑Observation Database

Imagine a central repository that stores millions of observation records, each representing a sighting of a bee species at a particular location and time. Researchers often query by region (e.g., “all observations in the Pacific Northwest”) and occasionally insert new observations for a specific latitude/longitude pair.

A page‑level lock on the region index enables many concurrent inserts into different pages of the same region without blocking each other, while a row‑level lock on the individual observation ensures that two users cannot edit the same record simultaneously. By using intent locks, the DBMS can safely allow a read‑only analyst to scan the entire region while a field researcher inserts a new observation, keeping both workloads responsive.


8. Real‑World Implementations

8.1 PostgreSQL

PostgreSQL’s concurrency control is built around MVCC + Serializable Snapshot Isolation (SSI). SSI adds a lightweight detection of dangerous structures that could lead to non‑serializable anomalies, aborting one of the offending transactions. In a 2022 production deployment handling ≈ 2 M web requests per second, the abort rate under SSI was ≈ 0.9 %, and latency grew by only 1.5 ms compared to plain SI.

8.2 MySQL InnoDB

InnoDB combines row‑level locking, gap locks, and MVCC. Gap locks prevent phantom reads by locking the gap between index entries. In a benchmark of 500 k mixed transactions per second on a 32‑core machine, InnoDB’s lock wait time averaged 0.7 ms, while deadlock occurrences were ≈ 0.02 % of total transactions—thanks to its built‑in deadlock detector.

8.3 Oracle

Oracle implements Hybrid Row‑Level Locking (a mix of exclusive and share locks) together with Multiversion Read Consistency. Oracle’s Automatic Undo Management recycles the undo tablespace, allowing old versions to be reclaimed without explicit VACUUM. In a 2023 CloudBench test, Oracle achieved ≈ 5.5 M TPS on a 96‑core instance with a 99.999% commit success rate, illustrating the scalability of its lock‑plus‑version approach.

8.4 Distributed Systems: Google Spanner

Google Spanner uses TrueTime, a globally synchronized clock with bounded uncertainty, to assign timestamps to transactions. Spanner’s concurrency control is timestamp‑based, but it also employs two‑phase commit (2PC) to achieve atomicity across data centers. In a 2021 internal benchmark, Spanner sustained ≈ 1 M writes per second with a latency of 6 ms for reads that required cross‑region coordination, demonstrating that timestamp ordering can scale to planetary‑wide deployments when combined with precise time services.


9. Concurrency in Distributed Environments

9.1 Two‑Phase Commit (2PC)

When a transaction spans multiple nodes, each node must agree to commit before the transaction can be considered durable. 2PC works in two rounds:

  1. Prepare phase – the coordinator asks each participant to prepare (write a “ready” log entry).
  2. Commit phase – if all participants respond “ready”, the coordinator sends a commit command; otherwise, it sends abort.

2PC guarantees atomicity but can block participants if the coordinator crashes. The blocking time can be mitigated by presumed abort or presumed commit heuristics.

9.2 Paxos and Raft for Consensus

Consensus algorithms such as Paxos and Raft provide a fault‑tolerant way to agree on a sequence of operations (including transaction commits). In a distributed ledger that records bee‑population surveys, each log entry can represent a transaction. The consensus layer ensures that all replicas apply the same sequence, preserving global serializability even under network partitions.

9.3 Example: Global Bee‑Health Dashboard

A worldwide dashboard aggregates sensor data from thousands of hives, each sending temperature, humidity, and colony strength measurements. The backend stores the data in a Spanner‑like distributed database that uses TrueTime for timestamps. Each sensor write is a transaction that includes a timestamp and a heartbeat token. The database’s concurrency control ensures that a later reading cannot overwrite an earlier, more accurate measurement, even if the two writes arrive out of order due to network latency.


10. Lessons for Bees, AI Agents, and Conservation

10.1 Parallelism in a Hive

A honeybee colony thrives because thousands of workers perform tasks in parallel while avoiding conflicts. Workers use chemical signals (pheromones) to claim a resource (e.g., a nectar source) and to inform others when a task is taken. This is a natural analogue of locking: the first bee to lay a scent on a flower “locks” it for the colony. If another bee tries the same flower, it detects the scent and moves on—much like a transaction that encounters a lock and retries elsewhere.

10.2 Timestamp Ordering in Self‑Governing AI

Self‑governing AI agents often need to coordinate actions without a central arbiter. By assigning each decision a logical timestamp (e.g., using Lamport clocks), agents can agree on a global ordering similar to database TSO. If two agents propose conflicting actions (e.g., both want to allocate the same limited resource), the one with the higher timestamp yields. This pattern mirrors the timestamp protocol that guarantees a serializable order without explicit locks.

10.3 Optimism for Conservation Data

Conservation projects frequently collect data in the field where connectivity is intermittent. Optimistic concurrency allows field teams to work offline, recording observations, and only validates conflicts when data is synchronized. The low abort rates observed in mobile databases (often < 2 %) mean that scientists can focus on data collection rather than fighting lock contention.

10.4 Bridging the Domains

The same mathematics that decides whether a bank transfer should succeed also decides whether two bees can share a flower or whether two AI agents can safely share a compute node. By exposing the underlying mechanisms—locks, timestamps, versioning—to domain experts, we empower conservationists to design data pipelines that are both fast and trustworthy. In turn, the insights from ecological coordination can inspire new, bio‑inspired concurrency algorithms that are resilient to failures and adaptable to changing environments.


Why it Matters

Concurrency control is the silent guardian of data integrity. Whether you are updating a hive’s honey inventory, training a swarm of AI agents to patrol a wildlife reserve, or publishing a global dataset on pollinator health, the guarantee that one transaction’s changes will not silently overwrite another’s is essential. Without robust mechanisms—locks that prevent lost updates, timestamps that order competing writes, or versioning that lets readers see a consistent snapshot—systems become fragile, results become unreliable, and the trust that underpins collaborative conservation collapses.

By mastering the concepts in this article, you gain the ability to design databases that scale, build applications that stay responsive under heavy load, and translate lessons from nature into resilient software. In the end, the same principles that keep a bee colony humming can keep your data humming—steady, safe, and ready to support the next breakthrough in conservation and AI.

Frequently asked
What is Database Concurrency Control about?
At its heart, concurrency control answers a simple question: When two or more transactions want to access the same piece of data, how do we decide who gets to…
What should you know about 1. Foundations of Concurrency Control?
At its heart, concurrency control answers a simple question: When two or more transactions want to access the same piece of data, how do we decide who gets to read, write, or modify it, and in what order? The answer must satisfy three classic properties— Atomicity , Consistency , Isolation , and Durability (the ACID…
What should you know about 2.1 Two‑Phase Locking (2PL)?
The classic algorithm for guaranteeing conflict‑serializability is Two‑Phase Locking . A transaction goes through two distinct phases:
What should you know about 2.2 Lock Granularity?
Locks can be taken at many granularities:
What should you know about 2.3 Real‑World Performance?
A 2020 benchmark from the TPC‑C (Transaction Processing Performance Council) suite measured InnoDB (MySQL’s default engine) on a 64‑core server handling 200 k transactions per second (TPS) with a mixture of read‑only and update workloads. The lock manager accounted for ≈ 2 % of CPU time, but lock wait time…
References & sources
  1. Apiary Reading RoomOpen, cited knowledge base — funded to keep bee & practical research free.
From the Apiary Reading Room. Opinion & editorial — not financial advice. We don't overclaim.
More from the Reading Room