Recent from talks
Nothing was collected or created yet.
Concurrency control
View on WikipediaIn information technology and computer science, especially in the fields of computer programming, operating systems, multiprocessors, and databases, concurrency control ensures that correct results for concurrent operations are generated, while getting those results as quickly as possible.
Computer systems, both software and hardware, consist of modules, or components. Each component is designed to operate correctly, i.e., to obey or to meet certain consistency rules. When components that operate concurrently interact by messaging or by sharing accessed data (in memory or storage), a certain component's consistency may be violated by another component. The general area of concurrency control provides rules, methods, design methodologies, and theories to maintain the consistency of components operating concurrently while interacting, and thus the consistency and correctness of the whole system. Introducing concurrency control into a system means applying operation constraints which typically result in some performance reduction. Operation consistency and correctness should be achieved with as good as possible efficiency, without reducing performance below reasonable levels. Concurrency control can require significant additional complexity and overhead in a concurrent algorithm compared to the simpler sequential algorithm.
For example, a failure in concurrency control can result in data corruption from torn read or write operations.
Concurrency control in databases
[edit]Comments:
- This section is applicable to all transactional systems, i.e., to all systems that use database transactions (atomic transactions; e.g., transactional objects in Systems management and in networks of smartphones which typically implement private, dedicated database systems), not only general-purpose database management systems (DBMSs).
- DBMSs need to deal also with concurrency control issues not typical just to database transactions but rather to operating systems in general. These issues (e.g., see Concurrency control in operating systems below) are out of the scope of this section.
Concurrency control in Database management systems (DBMS; e.g., Bernstein et al. 1987, Weikum and Vossen 2001), other transactional objects, and related distributed applications (e.g., Grid computing and Cloud computing) ensures that database transactions are performed concurrently without violating the data integrity of the respective databases. Thus concurrency control is an essential element for correctness in any system where two database transactions or more, executed with time overlap, can access the same data, e.g., virtually in any general-purpose database system. Consequently, a vast body of related research has been accumulated since database systems emerged in the early 1970s. A well established concurrency control theory for database systems is outlined in the references mentioned above: serializability theory, which allows to effectively design and analyze concurrency control methods and mechanisms. An alternative theory for concurrency control of atomic transactions over abstract data types is presented in (Lynch et al. 1993), and not utilized below. This theory is more refined, complex, with a wider scope, and has been less utilized in the Database literature than the classical theory above. Each theory has its pros and cons, emphasis and insight. To some extent they are complementary, and their merging may be useful.
To ensure correctness, a DBMS usually guarantees that only serializable transaction schedules are generated, unless serializability is intentionally relaxed to increase performance, but only in cases where application correctness is not harmed. For maintaining correctness in cases of failed (aborted) transactions (which can always happen for many reasons) schedules also need to have the recoverability (from abort) property. A DBMS also guarantees that no effect of committed transactions is lost, and no effect of aborted (rolled back) transactions remains in the related database. Overall transaction characterization is usually summarized by the ACID rules below. As databases have become distributed, or needed to cooperate in distributed environments (e.g., Federated databases in the early 1990, and Cloud computing currently), the effective distribution of concurrency control mechanisms has received special attention.
Database transaction and the ACID rules
[edit]The concept of a database transaction (or atomic transaction) has evolved in order to enable both a well understood database system behavior in a faulty environment where crashes can happen any time, and recovery from a crash to a well understood database state. A database transaction is a unit of work, typically encapsulating a number of operations over a database (e.g., reading a database object, writing, acquiring lock, etc.), an abstraction supported in database and also other systems. Each transaction has well defined boundaries in terms of which program/code executions are included in that transaction (determined by the transaction's programmer via special transaction commands). Every database transaction obeys the following rules (by support in the database system; i.e., a database system is designed to guarantee them for the transactions it runs):
- Atomicity - Either the effects of all or none of its operations remain ("all or nothing" semantics) when a transaction is completed (committed or aborted respectively). In other words, to the outside world a committed transaction appears (by its effects on the database) to be indivisible (atomic), and an aborted transaction does not affect the database at all. Either all the operations are done or none of them are.
- Consistency - Every transaction must leave the database in a consistent (correct) state, i.e., maintain the predetermined integrity rules of the database (constraints upon and among the database's objects). A transaction must transform a database from one consistent state to another consistent state (however, it is the responsibility of the transaction's programmer to make sure that the transaction itself is correct, i.e., performs correctly what it intends to perform (from the application's point of view) while the predefined integrity rules are enforced by the DBMS). Thus since a database can be normally changed only by transactions, all the database's states are consistent.
- Isolation - Transactions cannot interfere with each other (as an end result of their executions). Moreover, usually (depending on concurrency control method) the effects of an incomplete transaction are not even visible to another transaction. Providing isolation is the main goal of concurrency control.
- Durability - Effects of successful (committed) transactions must persist through crashes (typically by recording the transaction's effects and its commit event in a non-volatile memory).
The concept of atomic transaction has been extended during the years to what has become Business transactions which actually implement types of Workflow and are not atomic. However also such enhanced transactions typically utilize atomic transactions as components.
Why is concurrency control needed?
[edit]If transactions are executed serially, i.e., sequentially with no overlap in time, no transaction concurrency exists. However, if concurrent transactions with interleaving operations are allowed in an uncontrolled manner, some unexpected, undesirable results may occur, such as:
- The lost update problem: A second transaction writes a second value of a data-item (datum) on top of a first value written by a first concurrent transaction, and the first value is lost to other transactions running concurrently which need, by their precedence, to read the first value. The transactions that have read the wrong value end with incorrect results.
- The dirty read problem: Transactions read a value written by a transaction that has been later aborted. This value disappears from the database upon abort, and should not have been read by any transaction ("dirty read"). The reading transactions end with incorrect results.
- The incorrect summary problem: While one transaction takes a summary over the values of all the instances of a repeated data-item, a second transaction updates some instances of that data-item. The resulting summary does not reflect a correct result for any (usually needed for correctness) precedence order between the two transactions (if one is executed before the other), but rather some random result, depending on the timing of the updates, and whether certain update results have been included in the summary or not.
Most high-performance transactional systems need to run transactions concurrently to meet their performance requirements. Thus, without concurrency control such systems can neither provide correct results nor maintain their databases consistently.
Concurrency control mechanisms
[edit]Categories
[edit]The main categories of concurrency control mechanisms are:
- Optimistic - Allow transactions to proceed without blocking any of their (read, write) operations ("...and be optimistic about the rules being met..."), and only check for violations of the desired integrity rules (e.g., serializability and recoverability) at each transaction's commit. If violations are detected upon a transaction's commit, the transaction is aborted and restarted. This approach is very efficient when few transactions are aborted.
- Pessimistic - Block an operation of a transaction, if it may cause violation of the rules (e.g., serializability and recoverability), until the possibility of violation disappears. Blocking operations is typically involved with performance reduction.
- Semi-optimistic - Responds pessimistically or optimistically depending on the type of violation and how quickly it can be detected.
Different categories provide different performance, i.e., different average transaction completion rates (throughput), depending on transaction types mix, computing level of parallelism, and other factors. If selection and knowledge about trade-offs are available, then category and method should be chosen to provide the highest performance.
The mutual blocking between two transactions (where each one blocks the other) or more results in a deadlock, where the transactions involved are stalled and cannot reach completion. Most non-optimistic mechanisms (with blocking) are prone to deadlocks which are resolved by an intentional abort of a stalled transaction (which releases the other transactions in that deadlock), and its immediate restart and re-execution. The likelihood of a deadlock is typically low.
Blocking, deadlocks, and aborts all result in performance reduction, and hence the trade-offs between the categories.
Methods
[edit]Many methods for concurrency control exist. Most of them can be implemented within either main category above. The major methods,[1] which have each many variants, and in some cases may overlap or be combined, are:
- Locking (e.g., Two-phase locking - 2PL) - Controlling access to data by locks assigned to the data. Access of a transaction to a data item (database object) locked by another transaction may be blocked (depending on lock type and access operation type) until lock release.
- Serialization graph checking (also called Serializability, or Conflict, or Precedence graph checking) - Checking for cycles in the schedule's graph and breaking them by aborts.
- Timestamp ordering (TO) - Assigning timestamps to transactions, and controlling or checking access to data by timestamp order.
Other major concurrency control types that are utilized in conjunction with the methods above include:
- Multiversion concurrency control (MVCC) - Increasing concurrency and performance by generating a new version of a database object each time the object is written, and allowing transactions' read operations of several last relevant versions (of each object) depending on scheduling method.
- Index concurrency control - Synchronizing access operations to indexes, rather than to user data. Specialized methods provide substantial performance gains.
- Private workspace model (Deferred update) - Each transaction maintains a private workspace for its accessed data, and its changed data become visible outside the transaction only upon its commit (e.g., Weikum and Vossen 2001). This model provides a different concurrency control behavior with benefits in many cases.
The most common mechanism type in database systems since their early days in the 1970s has been Strong strict Two-phase locking (SS2PL; also called Rigorous scheduling or Rigorous 2PL) which is a special case (variant) of Two-phase locking (2PL). It is pessimistic. In spite of its long name (for historical reasons) the idea of the SS2PL mechanism is simple: "Release all locks applied by a transaction only after the transaction has ended." SS2PL (or Rigorousness) is also the name of the set of all schedules that can be generated by this mechanism, i.e., these SS2PL (or Rigorous) schedules have the SS2PL (or Rigorousness) property.
Major goals of concurrency control mechanisms
[edit]Concurrency control mechanisms firstly need to operate correctly, i.e., to maintain each transaction's integrity rules (as related to concurrency; application-specific integrity rule are out of the scope here) while transactions are running concurrently, and thus the integrity of the entire transactional system. Correctness needs to be achieved with as good performance as possible. In addition, increasingly a need exists to operate effectively while transactions are distributed over processes, computers, and computer networks. Other subjects that may affect concurrency control are recovery and replication.
Correctness
[edit]Serializability
[edit]For correctness, a common major goal of most concurrency control mechanisms is generating schedules with the Serializability property. Without serializability undesirable phenomena may occur, e.g., money may disappear from accounts, or be generated from nowhere. Serializability of a schedule means equivalence (in the resulting database values) to some serial schedule with the same transactions (i.e., in which transactions are sequential with no overlap in time, and thus completely isolated from each other: No concurrent access by any two transactions to the same data is possible). Serializability is considered the highest level of isolation among database transactions, and the major correctness criterion for concurrent transactions. In some cases compromised, relaxed forms of serializability are allowed for better performance (e.g., the popular Snapshot isolation mechanism) or to meet availability requirements in highly distributed systems (see Eventual consistency), but only if application's correctness is not violated by the relaxation (e.g., no relaxation is allowed for money transactions, since by relaxation money can disappear, or appear from nowhere).
Almost all implemented concurrency control mechanisms achieve serializability by providing Conflict serializability, a broad special case of serializability (i.e., it covers, enables most serializable schedules, and does not impose significant additional delay-causing constraints) which can be implemented efficiently.
Recoverability
[edit]- See Recoverability in Serializability
Concurrency control typically also ensures the Recoverability property of schedules for maintaining correctness in cases of aborted transactions (which can always happen for many reasons). Recoverability (from abort) means that no committed transaction in a schedule has read data written by an aborted transaction. Such data disappear from the database (upon the abort) and are parts of an incorrect database state. Reading such data violates the consistency rule of ACID. Unlike Serializability, Recoverability cannot be compromised, relaxed at any case, since any relaxation results in quick database integrity violation upon aborts. The major methods listed above provide serializability mechanisms. None of them in its general form automatically provides recoverability, and special considerations and mechanism enhancements are needed to support recoverability. A commonly utilized special case of recoverability is Strictness, which allows efficient database recovery from failure (but excludes optimistic implementations.
Distribution
[edit]With the fast technological development of computing the difference between local and distributed computing over low latency networks or buses is blurring. Thus the quite effective utilization of local techniques in such distributed environments is common, e.g., in computer clusters and multi-core processors. However the local techniques have their limitations and use multi-processes (or threads) supported by multi-processors (or multi-cores) to scale. This often turns transactions into distributed ones, if they themselves need to span multi-processes. In these cases most local concurrency control techniques do not scale well.
Recovery
[edit]All systems are prone to failures, and handling recovery from failure is a must. The properties of the generated schedules, which are dictated by the concurrency control mechanism, may affect the effectiveness and efficiency of recovery. For example, the Strictness property (mentioned in the section Recoverability above) is often desirable for an efficient recovery.
Replication
[edit]For high availability database objects are often replicated. Updates of replicas of a same database object need to be kept synchronized. This may affect the way concurrency control is done (e.g., Gray et al. 1996[2]).
Concurrency control in operating systems
[edit]This section needs expansion. You can help by adding to it. (December 2010) |
Multitasking operating systems, especially real-time operating systems, need to maintain the illusion that all tasks running on top of them are all running at the same time, even though only one or a few tasks really are running at any given moment due to the limitations of the hardware the operating system is running on. Such multitasking is fairly simple when all tasks are independent from each other. However, when several tasks try to use the same resource, or when tasks try to share information, it can lead to confusion and inconsistency. The task of concurrent computing is to solve that problem. Some solutions involve "locks" similar to the locks used in databases, but they risk causing problems of their own such as deadlock. Other solutions are Non-blocking algorithms and Read-copy-update.
See also
[edit]- Linearizability – Property of some operation(s) in concurrent programming
- Lock (computer science) – Synchronization mechanism for enforcing limits on access to a resource
- Mutual exclusion – In computing, restricting data to be accessible by one thread at a time
- Search engine indexing – Method for data management
- Semaphore (programming) – Variable used in a concurrent system
- Software transactional memory – Concurrency control mechanism in software
- Transactional Synchronization Extensions – Instruction set architecture extension
- Database transaction schedule
- Isolation (computer science)
- Distributed concurrency control
References
[edit]- Andrew S. Tanenbaum, Albert S Woodhull (2006): Operating Systems Design and Implementation, 3rd Edition, Prentice Hall, ISBN 0-13-142938-8
- Silberschatz, Avi; Galvin, Peter; Gagne, Greg (2008). Operating Systems Concepts, 8th edition. John Wiley & Sons. ISBN 978-0-470-12872-5.
- Philip A. Bernstein, Vassos Hadzilacos, Nathan Goodman (1987): Concurrency Control and Recovery in Database Systems (free PDF download), Addison Wesley Publishing Company, 1987, ISBN 0-201-10715-5
- Gerhard Weikum, Gottfried Vossen (2001): Transactional Information Systems, Elsevier, ISBN 1-55860-508-8
- Nancy Lynch, Michael Merritt, William Weihl, Alan Fekete (1993): Atomic Transactions in Concurrent and Distributed Systems , Morgan Kaufmann (Elsevier), August 1993, ISBN 978-1-55860-104-8, ISBN 1-55860-104-X
- Yoav Raz (1992): "The Principle of Commitment Ordering, or Guaranteeing Serializability in a Heterogeneous Environment of Multiple Autonomous Resource Managers Using Atomic Commitment." (PDF), Proceedings of the Eighteenth International Conference on Very Large Data Bases (VLDB), pp. 292-312, Vancouver, Canada, August 1992. (also DEC-TR 841, Digital Equipment Corporation, November 1990)
Citations
[edit]- ^ Philip A. Bernstein, Eric Newcomer (2009): Principles of Transaction Processing, 2nd Edition Archived 2010-08-07 at the Wayback Machine, Morgan Kaufmann (Elsevier), June 2009, ISBN 978-1-55860-623-4 (page 145)
- ^ Gray, J.; Helland, P.; O'Neil, P.; Shasha, D. (1996). Proceedings of the 1996 ACM SIGMOD International Conference on Management of Data. The dangers of replication and a solution (PDF). pp. 173–182. doi:10.1145/233269.233330.[permanent dead link]
Concurrency control
View on GrokipediaCore Concepts
Definition and Scope of Concurrency Control
Concurrency control refers to the mechanisms and protocols used to manage simultaneous operations on shared resources in multi-user computing systems, ensuring data consistency and system integrity by coordinating access and preventing conflicts.[4] This involves synchronizing activities such as reads and writes to avoid interference, allowing multiple users or processes to operate efficiently as if they had exclusive access to the system.[5] The scope of concurrency control spans multiple domains in computing. In database management systems (DBMS), it governs transaction execution to maintain data validity across concurrent queries and updates.[1] In operating systems, it coordinates process and thread interactions with shared memory and hardware resources to prevent race conditions and ensure reliable multitasking.[6] Distributed systems extend this to networked environments, where it synchronizes access to replicated data across multiple sites, addressing challenges like communication delays and partial failures.[4] In programming environments, it provides foundational primitives, such as semaphores and mutual exclusion algorithms, enabling developers to build safe concurrent applications.[7] Historically, concurrency control emerged in the 1970s from database research, with seminal work by Eswaran et al. formalizing serializability as the standard for correct concurrent schedules, alongside early protocols like two-phase locking to enforce it.[8] This foundation addressed the limitations of single-processor systems, where concurrency relied on time-sharing to interleave operations. Over decades, it evolved to support multi-core processors, which introduced true parallelism and required scalable algorithms capable of handling hundreds of cores without performance degradation.[9] By 2025, adaptations for cloud computing environments emphasize elastic, geo-distributed systems, integrating advanced protocols to balance consistency with high availability in massively scaled infrastructures.[10] Central to concurrency control are the ACID properties, which define high-level goals for reliable transaction processing. Atomicity treats each transaction as an indivisible unit: all operations succeed together, or none take effect, preventing partial updates.[11] Consistency ensures that transactions preserve database invariants, transforming valid states into new valid states while adhering to constraints like keys and referential integrity.[11] Isolation guarantees that concurrent transactions do not interfere, yielding results equivalent to some serial execution order, thus maintaining the appearance of sequential processing.[11] Durability commits changes permanently upon transaction completion, surviving failures through mechanisms like logging to enable recovery.[11] These properties guide the design of concurrency mechanisms across all scopes, though trade-offs may arise in distributed or high-throughput settings.Problems Arising from Concurrent Access
In concurrent systems, multiple transactions access shared data resources simultaneously, leading to interleavings of their operations that can produce unexpected and incorrect results. This simultaneous access often results in contention, where two or more transactions or processes attempt to access the same data resource in a conflicting manner, such as through concurrent locking for incompatible reads or writes.[12][13] Without proper coordination, the order in which read and write operations from different transactions are executed—known as an interleaving—can cause race conditions where the outcome depends on timing rather than logic. For instance, naive parallelism assumes independent operations will maintain data integrity, but in practice, this fails because transactions may partially observe or override each other's changes, resulting in inconsistencies that propagate through the system.[14] One classic anomaly is the lost update, where one transaction's modification to a data item is overwritten by another transaction that operates on an outdated copy of the same item. Consider a shared bank account with an initial balance of $100. Transaction T1 reads the balance ($100) and intends to add $50, while concurrently, T2 reads the same balance ($100) and intends to subtract $20. If T1 writes its update first ($150), but T2 then writes its update based on the original read ($80), T1's addition is lost, leaving the final balance at $80 instead of the expected $130. This occurs because neither transaction accounts for the other's changes during the interleaving.[14][15] Another issue is the dirty read, in which a transaction reads data written by another transaction that has not yet committed, potentially basing decisions on transient, unconfirmed changes. Using the same account example, suppose T1 updates the balance to $150 but aborts before committing, rolling back to $100. If T2 reads the uncommitted $150 during this window and uses it to approve a withdrawal, T2 may proceed with invalid assumptions, leading to further errors when T1's rollback exposes the original value.[15] The non-repeatable read anomaly arises when a transaction rereads data previously read within its execution, only to find different values due to another committed transaction's intervening update. For the account balance, T1 might read $100 at the start, perform some calculations, and later reread to verify, but if T2 commits a subtraction of $20 in between, T1 now sees $80, invalidating its internal state and computations.[15] Finally, the phantom read occurs when a transaction executes a query multiple times, but the set of rows returned changes due to concurrent insertions or deletions by another transaction, even though the query criteria remain the same. Imagine T1 querying the total balance of all accounts exceeding $50 in a database of customer accounts (initially summing to $300 from three accounts). If T2 inserts a new account with $60 and commits, T1's subsequent identical query returns $360, including the "phantom" row, which disrupts aggregates or counts reliant on a stable result set.[14][15] These anomalies collectively lead to data corruption, where shared resources end up in states that violate application invariants, such as non-negative account balances or accurate summaries. In multi-user scenarios, race conditions from such interleavings exacerbate inconsistencies, potentially cascading failures across the system and eroding trust in the data's reliability.[15] In real-world applications like early banking systems of the 1980s, which increasingly relied on concurrent processing for ATMs and electronic transfers, these problems manifested as incorrect account balances and unintended overdrafts, highlighting the need for robust controls to prevent financial losses. Such issues underscore how anomalies can deviate from serializability, the key criterion ensuring concurrent executions mimic sequential ones.Key Correctness Criteria
In concurrency control, serializability serves as a primary correctness criterion, ensuring that the outcome of concurrent transaction execution is equivalent to some serial execution where transactions run one at a time without interleaving.[16] Formally, a schedule is serializable if its reads-from relation, write order, and final writes match those of a serial schedule, preserving the database's logical consistency as if transactions were isolated.[17] Serializability encompasses two main types: conflict serializability and view serializability. Conflict serializability requires that the schedule avoids conflicting operations (reads and writes on the same data item by different transactions) in an order that would create cycles in the precedence graph, where nodes represent transactions and edges indicate forced ordering due to conflicts; the absence of cycles guarantees equivalence to a serial schedule.[16] View serializability, a weaker but more permissive condition, focuses on the visibility of writes: a schedule is view serializable if it produces the same reads (each transaction reads from the same write), the same initial reads, and the same final writes as some serial schedule, allowing more concurrent executions at the cost of higher verification complexity.[17] Recoverability addresses the durability aspect by ensuring that transaction outcomes can be restored after failures without propagating errors. A schedule is recoverable if, whenever transaction T2 reads a value written by T1, T1 commits before T2 commits, preventing T2 from basing decisions on uncommitted data that might later be aborted.[17] For example, in a recoverable schedule, if T1 writes A=10 and aborts after T2 reads it to set B=20, T2 must also abort to avoid inconsistent states; a non-recoverable schedule might allow T2 to commit with B=20 based on T1's uncommitted write, leading to permanent errors. Strict schedules enhance recoverability by prohibiting reads or writes of uncommitted data entirely, while cascadeless schedules avoid dirty reads to prevent cascading aborts during recovery, where one transaction's failure triggers rollbacks of dependent ones.[17] In a cascadeless example, enforcing commit-ordering for reads ensures no chain reactions, as seen when T2 only reads committed values from T1 post-T1's commit. Beyond databases, linearizability provides a real-time correctness criterion for concurrent objects, requiring that operations appear to take effect instantaneously at some point between invocation and response, extending serializability to respect real-time ordering for shared data structures like queues.[18] Anomaly-free execution complements these by guaranteeing schedules free of specific inconsistencies, such as lost updates or non-repeatable reads, ensuring predictable behavior under concurrency.[17] Performance metrics evaluate how well concurrency control mechanisms uphold these criteria under load: throughput measures transactions completed per unit time, reflecting efficiency in maintaining serializability without excessive blocking; latency captures the response time for individual transactions, critical for recoverable and linearizable systems where delays could amplify anomalies; and scalability assesses performance growth with increasing concurrency or cores, as poor scaling can compromise overall correctness in high-throughput environments.[9]Fundamental Techniques
Lock-Based Approaches
Lock-based approaches represent a pessimistic concurrency control strategy that prevents conflicts by acquiring locks on shared resources before accessing them, thereby ensuring serializability through mutual exclusion. In this method, transactions explicitly request locks to guard data items, blocking conflicting operations until the locks are released. This technique is foundational in both database management systems (DBMS) and operating systems (OS), where it balances concurrency with consistency by serializing access to critical sections.[8] Locks can be binary or multiple-mode. Binary locks provide simple mutual exclusion, allowing only one transaction to access a resource at a time, akin to a single on/off state. Multiple-mode locks, however, distinguish between read and write operations to permit greater concurrency. Shared locks (S locks) allow multiple transactions to read a data item simultaneously, while exclusive locks (X locks) grant sole access for writes, preventing any concurrent reads or writes. Compatibility between lock modes is governed by a matrix: S locks are compatible with other S locks but conflict with X locks; X locks conflict with both S and X locks. This design enables read operations to proceed in parallel while protecting writes.[8] The two-phase locking (2PL) protocol enforces serializability by structuring lock acquisition and release into two distinct phases for each transaction. In the growing phase, a transaction acquires all necessary locks without releasing any; once the first lock is released, the shrinking phase begins, during which no new locks can be acquired, and existing locks are released progressively. Basic 2PL ensures conflict-serializability but may allow cascading aborts if reads depend on uncommitted writes. To mitigate recovery issues, strict 2PL extends this by holding all exclusive (write) locks until transaction commit or abort, preventing dirty reads and simplifying rollback. Rigorous 2PL further strengthens recoverability by retaining all locks (shared and exclusive) until commit or abort. These variants are widely adopted in production DBMS for their balance of concurrency and durability guarantees.[8][19] Lock granularity refers to the level of detail at which resources are locked, influencing both concurrency and overhead. In databases, fine-grained locking at the page or record level maximizes parallelism by isolating conflicts to small units, while coarser table-level or database-level locking reduces management costs but serializes broader access. Multiple-granularity locking protocols exploit hierarchical data structures (e.g., database > table > page > record) using intention modes: Intention-Shared (IS) signals intent to read descendants, Intention-Exclusive (IX) signals intent to write descendants, Shared (S) for reads, Exclusive (X) for writes, and Shared-Intention-Exclusive (SIX) for mixed access. Before locking a node, ancestors must be locked in appropriate intention modes to propagate compatibility checks efficiently. The compatibility matrix for these modes is as follows:| Mode | IS | IX | S | SIX | X |
|---|---|---|---|---|---|
| IS | Yes | Yes | Yes | Yes | No |
| IX | Yes | Yes | No | No | No |
| S | Yes | No | Yes | No | No |
| SIX | Yes | No | No | No | No |
| X | No | No | No | No | No |
Transaction T:
// Growing phase: Acquire locks
Acquire S-lock on A // If incompatible, wait
Read A
Acquire X-lock on B // If incompatible, wait
Compute using A and B
Write B // Update B
// No unlocks until end (strict 2PL)
Commit T
Release S-lock on A
Release X-lock on B // Shrinking phase
Transaction T:
// Growing phase: Acquire locks
Acquire S-lock on A // If incompatible, wait
Read A
Acquire X-lock on B // If incompatible, wait
Compute using A and B
Write B // Update B
// No unlocks until end (strict 2PL)
Commit T
Release S-lock on A
Release X-lock on B // Shrinking phase
Timestamp and Ordering-Based Methods
Timestamp-based concurrency control methods assign unique timestamps to transactions upon initiation, using these values to enforce a total order on operations without relying on locks. This approach ensures conflict serializability by processing conflicting operations (reads and writes on the same data item) in timestamp order, aborting transactions that violate this order to prevent anomalies like dirty reads or lost updates. Seminal work formalized these algorithms for distributed systems, decomposing concurrency into read-write and write-write synchronization subproblems.[22] In the basic timestamp ordering (TSO) protocol, each transaction receives a unique timestamp , typically generated from a logical clock to guarantee system-wide uniqueness. For a read operation on data item by , the protocol checks if , the timestamp of the last write to ; if true, the read proceeds, and (the latest read timestamp for ) is updated to ; otherwise, aborts and restarts with a new timestamp. For a write on , it verifies and ; if both hold, the write occurs, setting ; else, aborts. This ensures that operations execute as if in serial order of timestamps, guaranteeing serializability.[22] Multiversion timestamp ordering (MVTO) extends basic TSO by maintaining multiple versions of each data item, each tagged with the timestamp of the transaction that created it, allowing readers to access a compatible prior version without aborting writers. In MVTO, a read by on selects the version with the largest creation timestamp , avoiding aborts for late-arriving reads that would conflict in single-version schemes. Writes create a new version unless a younger transaction has already read a version that would be invalidated, in which case the write aborts; this reduces restart frequency, particularly in read-heavy workloads, while preserving view serializability.[23] The Thomas' write rule optimizes timestamp ordering by ignoring obsolete writes rather than aborting the transaction, enhancing efficiency without sacrificing correctness. Specifically, in the write rule, if , the write is simply discarded as outdated, since a later transaction has already updated , but continues; the other conditions (e.g., ) still trigger aborts. This rule, applied during update propagation in multi-copy databases, ensures only the latest relevant updates are incorporated by comparing the update's timestamp against the data item's current timestamp and omitting earlier ones. It permits some view-serializable schedules not achievable under strict conflict serializability, reducing unnecessary rollbacks.[24] In real-time systems, priority inheritance protocols adapt timestamp and ordering concepts to mitigate priority inversion during synchronization, where low-priority tasks block high-priority ones on shared resources. Under the basic priority inheritance protocol, a blocked high-priority task temporarily boosts the priority of the blocking low-priority task to its own level, allowing it to complete the critical section and release the resource promptly; upon release, priorities revert. This bounds blocking chains to a single level per resource, avoiding deadlocks from mutual priority inversions and ensuring predictable response times, though it does not eliminate all chained blocking. The protocol integrates with timestamp ordering by preserving operation precedence while honoring real-time priorities.[25]Optimistic and Validation-Based Methods
Optimistic concurrency control (OCC) is a non-locking technique that allows transactions to execute without acquiring locks, assuming conflicts are rare, and performs validation only at commit time to detect and resolve any inconsistencies.[26] This approach contrasts with locking by prioritizing progress over prevention, making it suitable for environments where read operations dominate and contention is low.[26] OCC divides transaction execution into three distinct phases: the read phase, where a transaction reads data items into private local variables without any synchronization or global updates; the validation phase, where the system checks for conflicts to ensure serializability; and the write phase, where validated updates are applied to the database if no conflicts are found.[26] During the read phase, transactions proceed optimistically without restrictions, collecting read sets (items read) and write sets (items to be updated) locally.[26] Validation occurs just before commit, assigning a transaction number (tn) based on the order of starting the validation.[26] The validation phase employs two primary rules to guarantee serial equivalence: forward validation, which ensures that no transaction with a lower tn (earlier-committing) has a write set intersecting the current transaction's read set; and backward validation, which checks that no currently active transaction (higher tn) has a write set intersecting the current transaction's read or write sets.[26] If either rule fails, the transaction is aborted and restarted from the read phase.[26] In the seminal Kung-Robinson model, validation uses a certification mechanism where the validator compares the transaction's read set against the write sets of preceding transactions and active transactions' write sets against the current read and write sets.[26] This can be implemented serially, using a critical section to process one validation at a time, or in parallel, queuing validations and checking them concurrently to improve throughput under high concurrency.[26] For example, in B-tree index structures, conflict detection involves checking overlaps in small read and write sets (typically 4 items), making validation efficient.[26] Multiversion OCC extends the basic model by maintaining multiple versions of data items, each tagged with a transaction timestamp, to provide readers with consistent snapshots without blocking writers.[27] In snapshot isolation, a popular multiversion variant, each transaction reads from a snapshot of the database as of its start timestamp, ensuring repeatable reads without aborts from read-write conflicts.[27] Writes create new versions, and commits succeed under a first-committer-wins rule for direct write-write conflicts on the same item: if another transaction has committed a conflicting write to an item in the write set since the start, the current transaction aborts. However, snapshot isolation permits anomalies like write skew, requiring additional mechanisms for full serializability.[27] This avoids aborts for pure readers and reduces restarts in low-conflict scenarios compared to single-version OCC.[27] OCC, including its multiversion forms, is particularly applicable in high-contention environments where aborts are infrequent, such as query-heavy workloads or large databases with sparse data access patterns.[26] For instance, in a B-tree of order 199 with 10,000 leaf pages and depth 3, the probability of conflict during insertions is less than 0.0007, keeping validation overhead low relative to the read phase, which dominates execution time.[26] Throughput benefits arise when the validation and write phases are short compared to reads, allowing higher concurrency without frequent restarts.[26]Database Applications
Transaction Models and ACID Properties
In database systems, a transaction represents a logical unit of work that encapsulates a sequence of read and write operations on data, delimited by explicit begin and commit (or abort) boundaries to ensure reliable execution in the presence of failures.[11] This model allows applications to group operations that must succeed or fail as a cohesive whole, maintaining data integrity during concurrent access. Flat transactions, the standard model, treat the entire unit as indivisible without internal structure, whereas nested transactions introduce a hierarchical composition where sub-transactions can be spawned within a parent transaction, enabling partial rollbacks and finer-grained control over recovery. The ACID properties—Atomicity, Consistency, Isolation, and Durability—form the cornerstone of reliable transaction processing in databases, ensuring that concurrent transactions do not compromise data validity.[28] These properties were formalized in the early 1980s, building on foundational work by Jim Gray, who outlined the core concepts in his 1981 paper on transaction virtues and limitations.[11] Atomicity guarantees that a transaction is treated as an indivisible unit: either all operations complete successfully (commit), or none take effect (abort), often implemented via rollback mechanisms that restore the database to its pre-transaction state, as seen in logging techniques during system crashes.[28] Consistency ensures that each transaction transitions the database from one valid state to another, adhering to predefined integrity constraints such as foreign keys or balance rules in a banking system.[11] Isolation provides the illusion that transactions execute sequentially, preventing interference from partial results of concurrent ones, which is critical for avoiding anomalies like lost updates in multi-user environments.[28] Durability commits changes to non-volatile storage upon successful completion, ensuring persistence even after power failures, typically achieved through write-ahead logging to disk.[11] In contrast to the strict ACID guarantees suited for relational databases, NoSQL systems often adopt BASE properties—Basically Available, Soft state, and Eventual consistency—to prioritize scalability and availability in distributed environments. Basically Available ensures the system remains operational under partitions, Soft state allows temporary inconsistencies in data replicas, and Eventual consistency promises that updates propagate to all nodes over time, as exemplified in key-value stores like Dynamo where immediate ACID compliance would hinder performance at massive scale. This shift reflects trade-offs in modern data management, where BASE facilitates high-throughput applications at the expense of immediate consistency.Isolation Levels and Serializability
In database systems, the ANSI SQL standard defines four transaction isolation levels to manage concurrent access while balancing consistency and performance: Read Uncommitted, Read Committed, Repeatable Read, and Serializable. These levels specify the degree to which changes made by one transaction are visible to others, based on preventing specific anomalies such as dirty reads (reading uncommitted data), non-repeatable reads (re-reading the same row yields different results), and phantoms (new rows appear in a range query).[15][29] The following table summarizes the anomalies permitted at each level, including the standard ANSI SQL-92 phenomena (P1, P2, P3) and additional ones (P0, P4, A5A, A5B) from Berenson et al.:| Isolation Level | Dirty Reads (P1) | Non-Repeatable Reads (P2) | Phantoms (P3) | Additional Anomalies Allowed |
|---|---|---|---|---|
| Read Uncommitted | Allowed | Allowed | Allowed | Dirty writes (P0) |
| Read Committed | Prevented | Allowed | Allowed | Lost updates (P4), read skew (A5A), write skew (A5B) |
| Repeatable Read | Prevented | Prevented | Allowed | Write skew (A5B) in some implementations |
| Serializable | Prevented | Prevented | Prevented | None (full serializability) |
Implementation Strategies in DBMS
Database management systems (DBMS) implement concurrency control through specialized components like lock managers that enforce protocols such as two-phase locking (2PL) while integrating with storage structures for efficiency. Lock managers maintain a table of locks on data items, including pages and rows, to serialize access and prevent conflicts during concurrent transactions. In B-tree indexes, 2PL is integrated by acquiring locks on index nodes during searches and updates, with techniques like key-range locking to cover multiple records efficiently. To reduce contention in multicore environments, some systems employ latch-free B-trees, which use optimistic techniques and atomic primitives instead of traditional latches for traversing and modifying index structures, achieving higher throughput without blocking on shared memory access. Multi-version concurrency control (MVCC) is widely implemented in commercial DBMS to support non-blocking reads, particularly for read-only transactions. In PostgreSQL, MVCC creates version chains for each row, where updates append new versions with transaction identifiers (XIDs), allowing read-only transactions to traverse the chain backward to find the visible version based on the transaction's snapshot at start time. This avoids locks for readers, enabling snapshot isolation without interference from concurrent writes. Oracle implements a similar multi-version model using undo segments to store prior row images, which read-only transactions access via consistent read mechanisms that reconstruct the database state as of the transaction's begin time, ensuring read consistency across sessions.[35][36][37] Hybrid models combine elements of locking and optimistic concurrency control (OCC) to balance throughput and latency for mixed workloads. These approaches use locking for write-heavy operations to prevent conflicts early, while employing OCC or timestamp ordering for reads to minimize overhead. Google's Spanner exemplifies this by integrating 2PL at the Paxos leader for read-write transactions with TrueTime timestamps to assign globally consistent commit times, ensuring external consistency without full locking for read-only transactions that use snapshot reads at any replica.[38] Performance tuning in DBMS concurrency focuses on optimizing index locking granularity and buffer pool management to reduce overhead. Finer-grained index locking, such as intent locks on B-tree nodes, allows concurrent access to non-overlapping subtrees, while buffer pools employ eviction policies like clock or LRU to prioritize hot pages, minimizing I/O during lock acquisition. In cloud DBMS, recent advancements incorporate AI for dynamic tuning; for instance, learned models predict workload patterns to adjust lock timeouts or select protocols, as in NeurDB's AI-powered concurrency control that uses machine learning to optimize validation phases in OCC, improving throughput by up to 2x in high-contention scenarios.[39]Operating System Applications
Process and Thread Synchronization
Process and thread synchronization in operating systems coordinates concurrent executions to prevent race conditions and ensure data consistency when multiple processes or threads access shared resources. These mechanisms operate primarily at the user level through libraries or at the kernel level via system calls, enabling safe parallelism in applications like multi-threaded programs. Key primitives facilitate mutual exclusion, signaling, and coordination, allowing developers to structure concurrent code without low-level hardware intervention. Semaphores, introduced by Edsger W. Dijkstra in 1965 as a solution for cooperating sequential processes, serve as fundamental synchronization tools by maintaining a counter to regulate resource access.[40] A semaphore supports two atomic operations: the wait (P) operation, which decrements the counter and blocks if it is zero, and the signal (V) operation, which increments the counter and wakes a waiting process if any exist. Binary semaphores, restricted to values of 0 or 1, act as mutexes to enforce mutual exclusion for critical sections, while general semaphores with higher counts manage pools of resources, such as buffer slots in multi-producer scenarios.[40] Monitors, formalized by C. A. R. Hoare in 1974, offer a structured approach to synchronization by grouping shared variables and procedures into a single module, where only one process can execute monitor procedures at a time, providing implicit mutual exclusion.[41] Condition variables within monitors enable threads to suspend execution until a specific state is reached, using wait operations to release the monitor lock and signal operations to notify waiting threads, thus avoiding busy-waiting and simplifying complex coordination. This abstraction builds on semaphore concepts but reduces programming errors by localizing synchronization logic.[41] Thread models distinguish between user-level and kernel-level implementations, impacting synchronization efficiency. User-level threads, managed entirely by a runtime library within a single process, allow fast context switches without kernel involvement but risk blocking the entire process on I/O operations, as the kernel sees only the parent process. In contrast, kernel-level threads are directly supported and scheduled by the operating system kernel, enabling true parallelism across cores and handling blocking calls per thread, though at the cost of higher overhead from system calls. The POSIX threads (pthreads) standard, defined in IEEE Std 1003.1c-1995, unifies these models with portable APIs for both user and kernel threads, including mutex operations likepthread_mutex_lock() to acquire exclusive access and pthread_mutex_unlock() to release it, ensuring atomic protection of shared data.
Barriers and rendezvous mechanisms further support multi-threaded coordination by synchronizing groups of threads at specific computation phases. A barrier requires all participating threads to reach a designated point before any proceed, commonly used in parallel algorithms to align iterations; in pthreads, this is achieved via pthread_barrier_wait(), which blocks until the barrier's count of threads is met. Rendezvous, a related primitive, ensures threads meet pairwise or in small groups for handoff or synchronization, often implemented with semaphores initialized to zero to enforce waiting until counterparts arrive. These tools are essential for scalable computations, such as divide-and-conquer parallel processing.
A classic illustration is the producer-consumer problem, originally posed by Dijkstra in 1965, where a producer thread generates data into a fixed-size buffer while a consumer thread retrieves it, risking overflow or underflow without coordination.[40] Semaphores solve this with three variables: mutex (binary, for buffer access), full (counting buffer slots filled), and empty (counting available slots), initialized to 1, 0, and buffer size, respectively. The producer performs wait(empty), wait(mutex), adds data, signal(mutex), and signal(full); the consumer mirrors this with wait(full), wait(mutex), removes data, signal(mutex), and signal(empty), ensuring bounded buffer integrity atomically.[40]
// Producer
wait(empty);
wait(mutex);
// add item to buffer
signal(mutex);
signal(full);
// Consumer
wait(full);
wait(mutex);
// remove item from buffer
signal(mutex);
signal(empty);
// Producer
wait(empty);
wait(mutex);
// add item to buffer
signal(mutex);
signal(full);
// Consumer
wait(full);
wait(mutex);
// remove item from buffer
signal(mutex);
signal(empty);
Resource Allocation and Deadlock Prevention
In operating systems, resource allocation involves assigning shared resources such as memory, I/O devices, or files to multiple concurrent processes or threads while ensuring system stability and progress. Improper allocation can lead to deadlocks, where processes indefinitely wait for resources held by each other, halting execution. To mitigate this, operating systems employ strategies for deadlock prevention, detection, and recovery, focusing on reusable resources that processes acquire and release dynamically.[42] Deadlocks occur only if four necessary conditions, known as the Coffman conditions, hold simultaneously: mutual exclusion (resources cannot be shared and must be held exclusively), hold-and-wait (a process holding at least one resource waits for another), no preemption (resources cannot be forcibly taken from a process), and circular wait (a cycle exists in the resource allocation graph where processes wait for each other's resources). These conditions provide a foundational framework for analyzing and addressing deadlocks in resource management. Breaking any one condition enables prevention strategies.[42] Prevention approaches aim to eliminate one or more Coffman conditions proactively. Resource ordering imposes a total linear ordering on all resource types, requiring processes to request resources in strictly increasing order of their assigned numbers; this breaks the circular wait condition by ensuring no cycles can form in the allocation graph. For instance, if resources are numbered 1 to n, a process holding resource 5 cannot request resource 3, preventing potential loops. Another key method is the Banker's algorithm, developed by Edsger W. Dijkstra in 1965, which avoids unsafe states by simulating resource allocations before granting them. The algorithm maintains vectors for available resources, allocated resources per process, and maximum needs per process; it checks for a "safe state" by iteratively allocating to processes that can complete with current availability, ensuring all can finish without deadlock. This dynamic avoidance is particularly useful in systems with multiple resource instances but incurs overhead from repeated safety checks.[43][44] Detection strategies periodically or on-demand identify deadlocks after they occur, using models like the wait-for graph (WFG), introduced by Richard C. Holt in 1972. In a WFG, nodes represent processes, and directed edges indicate a process waiting for a resource held by another; a cycle in the graph signifies a deadlock. Operating systems construct and analyze these graphs—either centrally or in a distributed manner—by tracking lock acquisitions and waits, invoking detection when timeouts or high contention occur. This approach is efficient for systems where prevention overhead is prohibitive, as detection can be triggered sparingly. Upon detecting a deadlock, recovery involves breaking cycles with minimal disruption, often through resource preemption, process termination, or rollback. Preemption forcibly releases resources from one or more processes in the cycle, allowing others to proceed; the preempted process is rolled back to a prior state or restarted. Selection criteria prioritize processes with minimal rollback cost or those holding fewer resources to limit system impact. While effective, preemption requires careful handling of non-preemptible resources like CPU registers to avoid data corruption. In practice, combining detection with preemption balances responsiveness and overhead in resource-constrained environments.[42] A representative example is file system locking in the Linux kernel, where deadlocks are prevented through strict resource ordering and tools like lockdep for validation. Inodes and directory structures are locked using mutexes or semaphores, with developers required to acquire locks in a predefined hierarchy (e.g., parent directory before child) to avoid circular waits during operations like rename or unlink. The kernel's locking documentation emphasizes this ordering, and lockdep dynamically tracks dependencies at runtime, annotating potential cycles during development or boot to enforce prevention. This approach has proven robust in handling concurrent file accesses across thousands of processes in production workloads.Kernel-Level Concurrency Mechanisms
Kernel-level concurrency mechanisms in operating systems manage synchronization at the core of the system, ensuring safe access to shared resources amid interrupts, scheduling, and memory operations. These techniques operate at a low level, often disabling preemption or interrupts to create atomic sections where concurrent access is impossible. In the Linux kernel, for instance, atomic sections are achieved by disabling and enabling interrupts using primitives likelocal_irq_disable() and local_irq_enable(), which prevent interrupt handlers from preempting the current code path and ensure uninterrupted execution for short durations.[45]
Interrupt handling introduces concurrency challenges, as handlers can preempt running code and access shared kernel data structures. To protect critical sections from such interruptions, kernels employ disable/enable interrupt pairs, creating atomic regions where no concurrent execution occurs on the same CPU. This approach is essential for maintaining consistency during hardware events, though it is limited to short operations to avoid excessive latency. For longer critical sections vulnerable to interrupts, spinlocks provide a complementary mechanism; these are busy-waiting locks suitable for short durations, where a thread repeatedly checks the lock until available, minimizing context-switch overhead in multiprocessor environments. In Linux, spin_lock_irq() combines spinlock acquisition with interrupt disabling on the local CPU, ensuring atomicity against both interrupts and other CPUs.[45][46][47]
Scheduler concurrency relies on lock-free or low-contention structures to handle task management without introducing bottlenecks. Read-Copy Update (RCU), a synchronization primitive in the Linux kernel since version 2.5, exemplifies this for read-mostly data structures like linked lists or trees. RCU allows multiple concurrent readers to access data without locks, using lightweight barriers (rcu_read_lock() and rcu_read_unlock()) that impose no synchronization overhead in non-preemptive kernels, while updaters create copies, publish changes atomically, and defer reclamation until all readers complete via synchronize_rcu(). This mechanism scales well on symmetric multiprocessing (SMP) systems by avoiding reader-writer locks, reducing cache-line contention and enabling high-throughput reads concurrent with infrequent updates.[48][49]
Virtual memory operations introduce concurrency issues in multi-threaded contexts, particularly with page faults that can occur simultaneously across threads accessing unmapped pages. In modern kernels like Linux, page fault handling uses per-virtual memory area (VMA) locks to allow concurrent faults on different VMAs without global contention on the mmap_lock, which previously serialized all memory operations and became a scalability bottleneck for multi-threaded processes with large address spaces. This design enables multiple threads to trigger and resolve page faults independently, with the kernel mapping faults to physical frames while maintaining isolation; for example, in multithreaded kernels, each thread can handle its own faults without blocking siblings, using lightweight locking to protect VMA metadata during concurrent reads.[50][51][52]
As of 2025, modern kernels incorporate extended Berkeley Packet Filter (eBPF) for safe, concurrent extensions without modifying core code. eBPF programs, verified and loaded dynamically into the kernel, enable user-defined concurrency primitives like lightweight testing of thread interleavings for bug detection, running safely alongside kernel threads with bounded execution to prevent races or deadlocks. This trend enhances kernel extensibility for observability and networking, allowing concurrent hooks into scheduler paths or interrupt contexts while maintaining isolation through the eBPF verifier.[53][54]
