Login

Please fill in your details to login.





s5.07.08 - acid transactions and record locking

A-Level lesson on ACID properties (Atomicity, Consistency, Isolation, Durability) and record locking for database integrity.


image

You have already explored how client-server databases allow multiple users to access and modify data at the same time. While this concurrent access is powerful, it introduces a critical challenge: how do we ensure that the database remains accurate and consistent, even if a system crashes or multiple users try to change the same data simultaneously? Imagine two people trying to book the last seat on a flight, or a bank transfer failing halfway through! This lesson explores the fundamental principles and mechanisms that guarantee data integrity in such complex scenarios: ACID transactions and record locking. These concepts are vital for database administrators, software developers, and anyone managing critical information systems.

Learning Outcomes
Factual (Knowing that)
Recall the definition of a database transaction.
Describe the four ACID properties: Atomicity, Consistency, Isolation, and Durability.
State the purpose of record locking in concurrent database environments.

Conceptual (Knowing why)
Analyse how each ACID property contributes to maintaining database integrity.
Explain the challenges of concurrent access and how locking mechanisms address them.
Compare different strategies for concurrency control, such as serialisation.

Procedural (Knowing how)
Apply knowledge of transactions to understand a sequence of database operations in a scenario.
Interpret the impact of locking on database performance and data consistency.
Suggest strategies to mitigate concurrency issues like deadlocks in a database system.

The Deep Dive

Database Transactions: The Foundation of Reliability


At its core, a database transaction is a single logical unit of work, comprising one or more database operations. Think of it like a set of instructions that must either all succeed, or all fail, together. If you transfer money from one bank account to another, that's a single transaction. It involves deducting money from account A and adding it to account B. If only the deduction happens, your money is lost! Transactions are crucial for ensuring the database remains in a valid and consistent state, even in the face of errors or simultaneous user activity.

The ACID Properties


The acronym ACID stands for four fundamental properties that guarantee reliable transaction processing. These properties are the cornerstone of any robust relational database management system (DBMS).

A for Atomicity


Atomicity (the 'A' in ACID) dictates that a transaction must be treated as an indivisible, atomic unit. This means that either all operations within the transaction are completed successfully (it's committed), or if any part fails, the entire transaction is rolled back, and the database reverts to its state before the transaction began. There are no 'partial' transactions.

Real-World Example: Bank Transfer

Consider our bank transfer:
1
Deduct £100 from AccountA.
2
Add £100 to AccountB.

If the system crashes after Step 1 but before Step 2, Atomicity ensures that the deduction from AccountA is automatically undone. The database rolls back, and AccountA's balance is restored, preventing lost money. This is vital for financial systems.

C for Consistency


Consistency (the 'C' in ACID) ensures that a transaction takes the database from one valid state to another valid state. This means that all rules and constraints defined in the database schema (such as referential integrity, unique keys, and data types) must be maintained. If a transaction attempts to violate any of these rules, it must be rolled back.

image

Real-World Example: Inventory System

Imagine an inventory database where the stockLevel for an item cannot be negative. If a transaction attempts to decrement the stockLevel below zero, the database, enforcing consistency rules, would reject the transaction, roll it back, and keep the database in a valid state. This protects against corrupted data, a key concern for any Data Analyst or Database Administrator.

I for Isolation


Isolation (the 'I' in ACID) guarantees that concurrent transactions execute in isolation from each other. The effect of running multiple transactions concurrently should be the same as if they were executed sequentially. This prevents issues where one transaction reads data that another transaction is in the process of modifying, leading to incorrect results.

image

Concurrency Problems

Without Isolation, several problems can occur:

Dirty Reads: A transaction reads data that has been modified by another transaction but not yet committed. If the modifying transaction is later rolled back, the first transaction read "dirty" (invalid) data.
Non-Repeatable Reads: A transaction reads the same data twice but gets different results because another committed transaction modified the data between the two reads.
Phantom Reads: A transaction executes a query returning a set of rows. Later, the same query is executed, but new rows have been inserted by another committed transaction, causing different results.

D for Durability


Durability (the 'D' in ACID) ensures that once a transaction has been successfully committed, its changes are permanent and will survive any subsequent system failures (like power outages or crashes). This is achieved by storing the committed changes on non-volatile storage (e.g., hard disk) and often by writing to transaction logs.

image

Real-World Example: Online Order

When you place an online order, and the transaction commits, you expect that order to be permanently recorded. Even if the server crashes immediately after your order confirmation, Durability ensures that when the system recovers, your order data is still there. For Software Engineers building e-commerce platforms, this is non-negotiable.

Concurrency Control: Record Locking


To achieve Isolation, databases employ various concurrency control mechanisms, with record locking being a primary method. Record locking prevents multiple transactions from simultaneously accessing and modifying the same piece of data in a way that would violate Isolation.

Types of Locks


Shared Locks (Read Locks): Allow multiple transactions to read the same data concurrently. However, no transaction can write to the data while a shared lock is active.
Exclusive Locks (Write Locks): Prevent any other transaction (read or write) from accessing the data while the lock is held. Only one exclusive lock can be active on a record at any time.

image

How Locking Works

When a transaction wants to read or write a record, it first tries to acquire the appropriate lock.

If a compatible lock is available (e.g., trying to read with a shared lock when another shared lock is already present), the lock is granted.
If an incompatible lock is held (e.g., trying to write with an exclusive lock when a shared or exclusive lock is present), the transaction waits until the lock is released.

Challenges with Locking: Deadlock


While effective, locking can introduce its own problems, notably deadlock. Deadlock occurs when two or more transactions are each waiting for a lock to be released by the other, resulting in a standstill where neither can proceed.

image

Deadlock Example

1
Transaction 1 (T1) locks Record A.
2
Transaction 2 (T2) locks Record B.
3
T1 tries to lock Record B (which T2 holds) and waits.
4
T2 tries to lock Record A (which T1 holds) and waits.

Both transactions are now stuck, waiting indefinitely. Databases typically have mechanisms to detect deadlocks (e.g., using wait-for graphs) and resolve them by aborting one of the transactions (the "victim"), allowing the other to proceed. The aborted transaction is then usually restarted.

Other Concurrency Control Methods


While locking is prevalent, other methods exist:

Serialisation: Ensuring that transactions, when executed concurrently, produce the same outcome as if they had executed one after another in some sequential order. Locking is one way to achieve serialisation.
Timestamp Ordering: Assigns a unique timestamp to each transaction. The DBMS uses these timestamps to determine the serial order of transactions, ensuring that older transactions complete before newer ones.
Optimistic Concurrency Control: Assumes conflicts are rare. Transactions execute without locks, and only at commit time are checks made for conflicts. If conflicts are detected, the transaction is rolled back.

image
Active Learning Directive
Synoptic Linking: Force a connection. Identify two concepts from these notes (e.g., Atomicity and a specific concurrency problem) and explicitly link them to a topic we covered in a previous module or term (e.g., error handling in programming or network protocols). Write a short paragraph explaining how understanding one strengthens your understanding of the other.

Comprehension Questions

Answer these questions on your lesson sheet.

1
Define what is meant by a database transaction.
2
Explain the role of Atomicity in maintaining database integrity.
3
Describe the purpose of record locking in a multi-user database system.
4
Illustrate with a simple example how a deadlock might occur between two transactions.
5
Outline how serialisation helps to manage concurrent transactions.

time limit
Task The Bank Balance Bot

You are a Junior Software Developer tasked with simulating concurrent bank transfers to understand how ACID properties prevent data corruption. You will use a simple SQLite database and Python to mimic transactions.

0
Get Organised!

Open The Computing Café SQLite Editor in your web browser.
Clear any existing database that is loaded into the widget.
Open up a desktop IDE (such as Thonny or IDLE) where you can write Python code.

1
Set Up Your Database (focus on Consistency)

Consistency means the database must always follow its own defined rules. We are going to enforce a strict rule: an account balance can never drop below zero.

Create a new database in the SQLite editor called
bank.db
.
Run the following SQL to create your table with a specific constraint:

CREATE TABLE account (
    account_id INTEGER PRIMARY KEY,
    balance INTEGER CHECK (balance >= 0)
);


Insert two accounts:

INSERT INTO account (account_id, balance) 
VALUES (101, 500), (102, 300);


Run a
SELECT * FROM account;
query to verify your setup.
Export your
bank.db
file and save it somewhere sensible on your filesystem.
On your lesson sheet: Explain how the
CHECK
constraint guarantees database Consistency.

2
Simulate a Flawed Transfer (Focus on ATOMICITY Failure)

Atomicity is the "all or nothing" rule. If a transaction crashes halfway, the database must not save a partial update. Let's see what happens when we do not protect our data.

In your IDE, type out the following code and save it as
flawed_transfer.py
in the same folder as your database.

import sqlite3
import sys

def flawed_transfer(db_path, from_acc, to_acc, amount):
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()

    print(f"1. Taking £{amount} from Account {from_acc}...")
    cursor.execute(f"UPDATE account SET balance = balance - {amount} WHERE account_id = {from_acc}")
    
    # Simulate a sudden power cut or server crash
    print("2. SYSTEM CRASH! Power lost before recipient gets the money.")
    conn.commit() # The partial transaction is saved!
    sys.exit() 
    
    print(f"3. Giving £{amount} to Account {to_acc}...")
    cursor.execute(f"UPDATE account SET balance = balance + {amount} WHERE account_id = {to_acc}")
    
    conn.commit()
    conn.close()


Save the script in the same folder as your downloaded database file, run it and issue this commad in the console:

flawed_transfer('bank.db', 101, 102, 100)


Import your
bank.db
file back into the editor and check the balances.
On your lesson sheet: Where did the £100 go? Explain why this script violates Atomicity.

3
Implement an ACID-Compliant Transfer (Focus on ATOMICITY & DURABILITY)

Now, create a new script called
secure_transfer.py
. We will use explicit transaction control (
BEGIN
,
COMMIT
,
ROLLBACK
) to protect our data.

import sqlite3

def secure_transfer(db_path, from_acc, to_acc, amount):
    # isolation_level=None allows us to manually control transactions
    conn = sqlite3.connect(db_path, isolation_level=None) 
    cursor = conn.cursor()
    
    try:
        cursor.execute("BEGIN TRANSACTION;") 
        print(f"Attempting to transfer £{amount} from {from_acc} to {to_acc}...")
        cursor.execute(f"UPDATE account SET balance = balance - {amount} WHERE account_id = {from_acc}")
        # Simulate a crash if transferring to a non-existent account
        if to_acc == 999:
            raise Exception("Simulated server crash mid-transaction!")
        cursor.execute(f"UPDATE account SET balance = balance + {amount} WHERE account_id = {to_acc}")
        conn.commit() # Save everything permanently 
        print("Transfer successful! Changes written to disk.")
        
    except Exception as e:
        conn.rollback() # Reverse everything 
        print(f"Transfer failed: {e}. Transaction rolled back.")
        
    finally:
        conn.close()


Run the script and then type the following, in order, into the console.

# Test 1: A successful transaction
secure_transfer('bank.db', 101, 102, 100)


# Test 2: A simulated crash halfway through (Tests Atomicity via ROLLBACK)
secure_transfer('bank.db', 101, 999, 100)


# Test 3: Attempting to send money we don't have (Tests Consistency via CHECK)
secure_transfer('bank.db', 101, 102, 5000)


Durability Check: Completely close your Python IDE and the SQLite Editor. Restart your computer if you wish! Re-open
bank.db
in the editor. Your data from Test 1 should still be there.
On your lesson sheet: Explain how the
ROLLBACK
command ensures Atomicity. Furthermore, explain how the
COMMIT
command writing data to your hard drive demonstrates Durability compared to storing data in standard Python variables.

4
The Isolation Test (Simulating Concurrency)

Isolation ensures that concurrent transactions do not interfere with each other (preventing the "lost update" problem). Create a new file called
isolation_test.py
. This script uses
threads
to simulate two entirely separate users trying to access the exact same database at the exact same time.

import sqlite3
import time
import threading

def slow_transfer():
    conn = sqlite3.connect('bank.db', timeout=1, isolation_level=None)
    cursor = conn.cursor()
    try:
        cursor.execute("BEGIN EXCLUSIVE TRANSACTION;")
        print("User A: Started transaction and locked the database.")
        
        cursor.execute("UPDATE account SET balance = balance - 50 WHERE account_id = 101")
        print("User A: Update made. Pausing for 5 seconds (simulating network lag)...")
        
        time.sleep(5) # The lock is held open while the script sleeps
        
        conn.commit()
        print("User A: Transaction finally committed and lock released.")
    except Exception as e:
        print(f"User A Error: {e}")
    finally:
        conn.close()

def concurrent_transfer():
    time.sleep(1) # Wait 1 second to ensure User A grabs the lock first
    conn = sqlite3.connect('bank.db', timeout=1, isolation_level=None)
    cursor = conn.cursor()
    try:
        print("User B: Attempting to start a transaction...")
        cursor.execute("BEGIN EXCLUSIVE TRANSACTION;")
        cursor.execute("UPDATE account SET balance = balance + 100 WHERE account_id = 101")
        conn.commit()
        print("User B: Transaction successful. (You should not see this!)")
        
    except sqlite3.OperationalError as e:
        print(f"\nUser B ERROR: {e}")
        print("DBMS ACTION: The database protected the data by blocking User B!\n")
    finally:
        conn.close()

# Start both user actions at the same time
print("--- STARTING CONCURRENT TRANSFERS ---")
thread_a = threading.Thread(target=slow_transfer)
thread_b = threading.Thread(target=concurrent_transfer)

# Launch the threads
thread_a.start()
thread_b.start()

# Tell the main program to wait for both threads to finish
thread_a.join()
thread_b.join()

print("--- TRANSFERS COMPLETE ---")


On your lesson sheet: Without record locking, what would have happened to User A's update if User B had been allowed to run simultaneously? Explain why the DBMS threw a database is locked error.

5
Extend Your Learning (Use, Modify, Create)

To solidify your understanding, work through these three stages:

USE: Solidify your understanding.

You have already used the scripts above to observe how a Database Management System enforces ACID properties. Review your lesson sheet answers to ensure you understand the mechanics of A, C, I, and D.

MODIFY: Reverse the roles.

Open
isolation_test.py
. Change the
timeout=1
parameter in User B's connection to
timeout=10
. Run the script again.
What happens? Notice that User B no longer crashes instantly. Instead, it waits in a queue until User A finishes, and then executes. This is how modern databases handle high traffic gracefully.

CREATE: Create a new Python script that implements a "Transaction Log".

Create a new table in
bank.db
called
audit_log(id, message, timestamp)
.
Write a new
transfer
function that successfully moves money between two accounts AND writes a record to the
audit_log
table.
Crucially: All three
UPDATE
/
INSERT
statements must be inside a single
BEGIN TRANSACTION
block. If the audit log fails to write, the money transfer must
ROLLBACK
to maintain Atomicity.


Outcome: You have simulated database transactions and observed the importance of ACID properties in maintaining data integrity during concurrent operations.

Checkpoint

image

Hungry for more?


Delve deeper into the practical implementation of concurrency control and explore how real-world database systems manage complex transaction scenarios.

Ask an AI: Click on the carefully written prompt below to learn more...

image
As an expert database architect, explain the concept of Two-Phase Locking (2PL) in relational databases, its two phases, and its role in achieving serialisability for an A-Level Computer Science student. Limit to 150 words. NO intro, NO outro, NO deviation from the topic, NO follow-up questions


Explore NoSQL: Investigate how NoSQL databases (e.g., MongoDB, Cassandra) sometimes relax ACID properties for performance and scalability, often aiming for "eventual consistency."

Sticky Note Challenge

On a sticky note, summarise the key difference between Atomicity and Durability in one sentence each.

image
Today you have learnt how ACID properties and record locking are fundamental to maintaining database integrity.
Last modified: October 23rd, 2026
The Computing Café works best in landscape mode.
Rotate your device.
Dismiss Warning