Imagine transferring money from your savings account to your friend's account using a banking app. Behind the scenes, this single action from your perspective actually involves at least two separate steps: deducting the amount from your account, and adding that same amount to your friend's account. Now imagine the server crashes at the exact moment right after your money has been deducted, but before it has been credited to your friend. Where did that money go? This exact scenario is precisely the kind of problem transaction management exists to solve.
A transaction in DBMS is a single logical unit of work, made up of one or more database operations, that must be treated as an indivisible whole. Either every operation within the transaction completes successfully, or none of them take effect at all. This all-or-nothing guarantee is what protects a database from ending up in a broken, inconsistent state whenever something goes wrong midway through a multi-step operation, whether that something is a system crash, a power failure, or a program error.
In this tutorial, you will learn what a transaction actually is, the four essential properties every transaction must satisfy, known as ACID properties, the different states a transaction moves through during its lifetime, and how schedules describe the order in which operations from multiple transactions can be interleaved. Worked examples are included throughout to connect these ideas to real, practical scenarios like the bank transfer mentioned above.
A transaction is a sequence of one or more database operations, such as reads and writes, that together represent a single meaningful task from the perspective of the application using the database. A transaction always ends in one of two ways: it either commits, meaning every change it made is permanently saved to the database, or it aborts, meaning every change it attempted is completely undone, as if the transaction had never been executed at all.
Transaction T1: Transfer ₹5000 from Account A to Account B Read(A) A = A - 5000 Write(A) Read(B) B = B + 5000 Write(B) Commit
This transaction consists of two internal operations, deducting money from A and crediting money to B, but from the database's point of view, T1 is one single unit. If the system fails after the Write(A) step but before the Write(B) step, the database management system must be capable of noticing that T1 never reached its commit, and rolling back the deduction from A, restoring it to its original value, so the bank's total money never mysteriously vanishes.
Every transaction, no matter how simple or complex, is expected to satisfy four essential properties, remembered using the acronym ACID: Atomicity, Consistency, Isolation, and Durability. These four properties together form the backbone guarantee that makes transactions trustworthy.
| Property | Description |
|---|---|
| Atomicity | A transaction is treated as a single indivisible unit; either all of its operations are applied to the database, or none of them are, with no partial results ever left behind. |
| Consistency | A transaction moves the database from one valid state to another valid state, ensuring that any rules or constraints defined on the data, such as balances never going negative, are never violated once the transaction completes. |
| Isolation | Concurrently executing transactions should not interfere with each other, meaning the intermediate, uncommitted results of one transaction should not be visible to another transaction running at the same time. |
| Durability | Once a transaction has committed, its changes must survive permanently, even in the event of a subsequent power failure or system crash. |
Using the earlier bank transfer transaction T1: If the system crashes after Write(A) but before Write(B), atomicity guarantees that the deduction from A is rolled back during recovery, so the database ends up exactly as it was before T1 began, rather than left in a half-completed state where money simply disappeared.
Suppose a business rule states that Account A's balance must never fall below zero. If Account A currently holds ₹3000, and transaction T1 attempts to transfer ₹5000 out of it, consistency requires the database to reject this transaction entirely, since allowing it to complete would leave Account A in an invalid, negative state that violates the defined business rule.
Suppose transaction T1 is transferring money from A to B, while at the exact same time, transaction T2 is trying to read Account A's current balance to display it on a banking app's dashboard. Isolation ensures that T2 either sees Account A's balance exactly as it was before T1 started, or exactly as it is after T1 fully commits, never a confusing, half-updated value from somewhere in the middle of T1's execution.
Once T1 successfully commits, confirming the transfer of ₹5000 from A to B, durability guarantees that even if the database server loses power one second later, the completed transfer is not lost; upon restart, both account balances correctly reflect the transfer that already took place.
Throughout its execution, a transaction moves through a well-defined sequence of states, tracked internally by the database management system to determine exactly how to handle the transaction at any given moment, including how to recover correctly if something goes wrong.
| State | Description |
|---|---|
| Active | The initial state, where the transaction is currently executing its operations, reading and writing data. |
| Partially Committed | The transaction has finished executing its final operation, but its changes have not yet been permanently saved to the database. |
| Committed | The transaction has successfully completed, and all of its changes have been permanently saved to the database. |
| Failed | The transaction can no longer proceed normally, typically due to a hardware failure, a system crash, or a violation of a database constraint. |
| Aborted | The transaction has been rolled back, undoing any changes it made, restoring the database to the state it was in before the transaction began. |
Active --------------------------→ Partially Committed --------→ Committed | | | (failure occurs) | (failure occurs) ↓ ↓ Failed ----------------------------→ Aborted
A transaction always begins in the active state and, under normal circumstances, moves smoothly through partially committed into committed. If anything goes wrong at any point along this path, the transaction instead moves into the failed state, from which it is rolled back into the aborted state, undoing whatever partial changes had already been made.
In any real database system, multiple transactions typically run concurrently, with their individual operations interleaved with one another to improve overall performance and resource utilization. A schedule is simply the chronological order in which the operations from one or more transactions are actually executed by the system.
A serial schedule executes transactions one completely after another, with no interleaving of operations between different transactions at all. While simple and easy to reason about, serial schedules are inefficient, since they force transactions to wait unnecessarily even when their operations do not actually conflict with each other.
Serial Schedule Example: T1: Read(A), Write(A), Read(B), Write(B), Commit T2: Read(A), Write(A), Commit Executed serially: T1 fully completes first, then T2 begins and fully completes afterward.
A concurrent schedule interleaves the operations of multiple transactions, allowing the database to work on several transactions simultaneously rather than strictly one at a time, which generally improves throughput significantly, especially on systems handling many simultaneous users.
Concurrent Schedule Example: Time Operation 1 T1: Read(A) 2 T2: Read(A) 3 T1: Write(A) 4 T2: Write(A) 5 T1: Commit 6 T2: Commit
This particular interleaving happens to cause a problem: T2 reads A before T1's write takes effect, meaning T2 is working with an outdated value, and then T2's subsequent write may overwrite T1's update entirely, a classic concurrency problem known as a lost update. This exact issue is precisely why concurrency control mechanisms, covered in a later chapter, are needed to carefully manage how concurrent schedules are allowed to interleave.
Without proper transaction management, a database system handling many simultaneous operations would be constantly at risk of corrupted, inconsistent, or lost data, especially in high-stakes applications like banking, airline reservations, and e-commerce checkout systems, where even a single incorrectly processed operation can have serious real-world consequences. The ACID properties, transaction states, and the careful management of schedules together form the foundation that allows database systems to remain reliable even under heavy concurrent load and unpredictable failures.
| Mistake | Correct Understanding |
|---|---|
| Assuming a transaction is the same as a single SQL statement. | A transaction can consist of multiple SQL statements grouped together, all of which must succeed or fail as a single unit. |
| Confusing atomicity with isolation. | Atomicity ensures a transaction's own operations are all-or-nothing, while isolation ensures that concurrent transactions do not interfere with each other's intermediate results. |
| Believing a failed transaction and an aborted transaction are the same state. | Failed indicates the transaction cannot continue due to an error, while aborted indicates the system has already rolled back its changes in response. |
| Assuming concurrent schedules are always faster with no downsides. | Concurrent schedules improve performance but can introduce problems like lost updates if not properly controlled, which is exactly why concurrency control techniques are necessary. |
Transaction management provides the essential guarantees that keep a database trustworthy even as multiple operations execute simultaneously and unpredictable failures occur. Through the ACID properties, the well-defined lifecycle of transaction states, and the careful ordering of operations described by schedules, this chapter showed exactly how a database ensures that a multi-step operation like a bank transfer either completes fully and correctly, or leaves no trace at all if something goes wrong along the way.
In this tutorial, you learned what a transaction is using a worked bank transfer example, explored each of the four ACID properties with its own dedicated example, walked through the states a transaction passes through during its lifetime, and compared serial and concurrent schedules, including a worked example of the lost update problem. With this foundation, you are ready to move on to concurrency control, which covers the specific techniques databases use to safely manage concurrent schedules without running into problems like the one demonstrated in this chapter.