Last modified: February 20, 2020
This article is written in: 🇺🇸
Atomicity is a fundamental principle in database systems that ensures each transaction is processed as an indivisible unit. This means that all operations within a transaction must be completed successfully for the transaction to be committed to the database. If any operation fails, the entire transaction is rolled back, leaving the database unchanged. This "all-or-nothing" approach is crucial for maintaining data integrity and consistency.
Imagine a transaction as a series of steps that are tightly bound together. If one step fails, the entire sequence is aborted to prevent partial updates that could corrupt the database.
+---------------------------------+
| Transaction Steps |
| |
| Step 1: Validate Input |
| Step 2: Update Records |
| Step 3: Write to Log |
| Step 4: Commit Changes |
+---------------------------------+
After reading the material, you should be able to answer the following questions:
Atomicity plays a vital role in database transactions by ensuring that partial transactions do not leave the database in an inconsistent state. This is especially important in systems where multiple transactions are occurring simultaneously.
By treating transactions as indivisible units, atomicity prevents scenarios where only some parts of a transaction are applied. This means the database remains accurate and reliable, reflecting only complete sets of operations.
Atomicity simplifies the process of dealing with errors during transaction execution. Developers and database administrators can rely on the database system to automatically roll back incomplete transactions, reducing the need for complex error recovery logic.
To better understand atomicity, let's explore some real-world scenarios where this concept is essential.
Consider the process of transferring money between two bank accounts. This transaction involves debiting one account and crediting another. Both actions must occur together; otherwise, funds could be lost or erroneously created.
Atomicity ensures that either both accounts are updated or neither is, preserving the integrity of the bank's records.
When placing an order online, several operations happen behind the scenes: payment processing, inventory reduction, and order confirmation. If payment processing fails, the system should not reduce inventory or generate an order confirmation.
Atomicity ensures that all steps are completed together, maintaining consistency in the system.
To achieve atomicity, database systems employ various techniques and protocols that manage transactions effectively.
In distributed database systems, the Two-Phase Commit Protocol ensures that all participating databases agree on committing or rolling back a transaction.
This protocol ensures that either all databases commit the transaction or all roll it back, maintaining atomicity across the system.
Coordinator
|
+-- Prepare --> Participant 1 (Ready)
+-- Prepare --> Participant 2 (Ready)
+-- Prepare --> Participant 3 (Ready)
|
+-- Commit --> All Participants
Savepoints provide a way to partition a transaction into smaller segments. They allow partial rollbacks within a transaction without aborting the entire sequence.
SAVEPOINT savepoint_name;
to mark a point within a transaction.ROLLBACK TO savepoint_name;
to undo operations back to the savepoint.RELEASE SAVEPOINT savepoint_name;
to remove the savepoint.Savepoints are useful in complex transactions where certain operations may fail, but earlier successful operations should be retained.
Databases use logs to record all changes made during transactions. This approach allows the system to undo or redo transactions in case of failures.
This mechanism is essential for maintaining data integrity, especially in systems where transactions are frequently interrupted.
In SQL, transactions are managed using commands that explicitly define the start and end of a transaction.
BEGIN TRANSACTION;
marks the start.COMMIT;
saves all changes.ROLLBACK;
undoes all changes since the transaction began.
BEGIN TRANSACTION;
UPDATE accounts
SET balance = balance - 500
WHERE account_id = 1;
UPDATE accounts
SET balance = balance + 500
WHERE account_id = 2;
COMMIT;
In this example, if either UPDATE
statement fails, a ROLLBACK;
command would undo any changes, thanks to the atomicity of the transaction.
BEGIN TRANSACTION;
SAVEPOINT before_update;
UPDATE inventory
SET quantity = quantity - 1
WHERE product_id = 101;
-- Suppose an error occurs here
IF ERROR
BEGIN
ROLLBACK TO before_update;
END
COMMIT;
By rolling back to the savepoint, the transaction undoes changes made after the savepoint without affecting earlier operations.
To really understand atomicity in databases, it's useful to visualize what happens during a transaction. Think of a transaction as a sequence of steps that must either all succeed or none at all—no in-between.
Here’s a simple diagram showing the normal flow of a transaction:
[Start Transaction]
|
[Operation 1]
|
[Operation 2]
|
[Check for Errors]
|
[No Errors]
|
[Commit]
This is the “happy path.” You start a transaction, do your operations, check for any issues, and if nothing’s wrong, you commit the changes. Committing makes everything permanent in the database.
But if any operation fails, you don’t go forward—you go back. That’s the whole point of atomicity. You either do it all or undo it all.
Let’s see what that looks like:
[Start Transaction] <---
| |
[Operation 1] |
| |
[Operation 2] |
| |
[Error Detected] |
| |
[Rollback] ---------
Here’s how this might look in actual SQL (using PostgreSQL syntax):
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
This is transferring $100 from account 1 to account 2. Simple enough.
Now, let’s simulate an error. Say the second UPDATE
fails—maybe account 2 doesn’t exist. We’d use this approach to protect data integrity:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- Suppose this line fails:
UPDATE accounts SET balance = balance + 100 WHERE id = 999;
ROLLBACK;
What happens?
UPDATE
goes through and deducts $100.UPDATE
fails because account 999 doesn’t exist.Why is this good?
Without transactions, you’d have just lost $100 from account 1. Atomicity protects you from half-done operations.
Here’s how you might catch this in application code (Python + psycopg2 example):
import psycopg2
try:
conn = psycopg2.connect(...)
cur = conn.cursor()
cur.execute("BEGIN;")
cur.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1;")
cur.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 999;")
conn.commit()
except Exception as e:
conn.rollback()
print("Transaction failed and was rolled back:", e)
finally:
cur.close()
conn.close()
Output:
Transaction failed and was rolled back: ERROR: account 999 does not exist
So the main idea is: no partial changes allowed. Either all steps complete, or the system undoes everything like nothing ever happened.