Transactions and Concurrency – Ensuring Data Consistency in SQL Systems

In multi-user database systems, multiple operations often need to be executed safely, without conflict, and with guaranteed data integrity. SQL provides a powerful mechanism for this through transactions and concurrency control. These tools ensure that changes to data are reliable, atomic, and consistent, even when multiple users are interacting with the database simultaneously.

This chapter provides an in-depth exploration of:

  • What transactions are and why they matter
  • ACID properties
  • How to manage transactions with SQL
  • Concurrency control mechanisms
  • Isolation levels and locking strategies
  • Deadlocks and how to avoid them
  • Best practices for reliable data processing

What is a Transaction?

A transaction is a sequence of one or more SQL operations executed as a single unit. The key idea is that all operations within a transaction must either complete successfully or leave the database unchanged.

Example:

START TRANSACTION;
UPDATE Accounts SET Balance = Balance - 500 WHERE AccountID = 1;
UPDATE Accounts SET Balance = Balance + 500 WHERE AccountID = 2;
COMMIT;

This example transfers money between accounts. If either update fails, the transaction should roll back.

ACID Properties

ACID is a foundational principle for transactional databases. It guarantees that a transaction behaves in a predictable and consistent manner.

PropertyDescription
AtomicityAll operations succeed or none do (rollback on failure)
ConsistencyDatabase transitions from one valid state to another
IsolationTransactions do not interfere with each other
DurabilityOnce committed, changes are permanent—even in case of a crash

Together, these ensure that data remains correct and reliable under concurrent access and failure scenarios.

Transaction Control Commands

START TRANSACTION / BEGIN

Begins a new transaction.

START TRANSACTION;

COMMIT

Saves all changes made in the transaction.

COMMIT;

ROLLBACK

Undoes all changes since the last START TRANSACTION.

ROLLBACK;

SAVEPOINT

Creates a save point within a transaction to which you can roll back.

SAVEPOINT sp1;
UPDATE Employees SET Salary = 0;
ROLLBACK TO sp1;

RELEASE SAVEPOINT

Removes a previously defined savepoint.

RELEASE SAVEPOINT sp1;

SET TRANSACTION

Defines transaction characteristics.

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

Isolation Levels – Controlling Concurrency

Isolation level defines how visible the intermediate state of a transaction is to others.

LevelDescriptionCommon Issues Prevented
READ UNCOMMITTEDTransactions can see uncommitted changesNone
READ COMMITTEDOnly committed changes are visiblePrevents dirty reads
REPEATABLE READSame rows read twice will not changePrevents dirty and non-repeatable reads
SERIALIZABLETransactions execute as if in serial orderPrevents dirty reads, phantom reads

Examples:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

Use higher isolation for accuracy, and lower isolation for performance.

Concurrency Issues

Dirty Reads

Reading data that has been modified by another uncommitted transaction.

Non-Repeatable Reads

Reading the same row twice within a transaction and getting different values.

Phantom Reads

A new row appears in a repeated query during a transaction.

Lost Updates

Two transactions overwrite each other’s updates.

Locks – Managing Access to Data

Locks prevent multiple users from accessing data in conflicting ways.

Types of Locks:

Lock TypeDescription
Shared (S)Read-only lock; others can also read
Exclusive (X)Write lock; prevents other access

Row-Level vs Table-Level

  • Row-level: Lock specific rows (better concurrency)
  • Table-level: Lock entire table (simpler but restrictive)

Manual Locking:

LOCK TABLE Employees IN SHARE MODE; -- MySQL
SELECT * FROM Orders FOR UPDATE; -- PostgreSQL

Deadlocks

When two transactions hold locks and wait on each other.

Example:

  • Transaction A locks row 1 and waits for row 2
  • Transaction B locks row 2 and waits for row 1

Solution: Database detects and aborts one transaction.

Deadlock Prevention Techniques

  1. Access tables in consistent order
  2. Keep transactions short
  3. Use lowest necessary isolation level
  4. Avoid user input inside transactions
  5. Use timeouts and retry logic

Real-World Example: Online Banking System

Problem:

Transferring money between accounts should be atomic, isolated, and consistent.

Table:

CREATE TABLE Accounts (
  AccountID INT PRIMARY KEY,
  Balance DECIMAL(10,2)
);

Transaction:

START TRANSACTION;
UPDATE Accounts SET Balance = Balance - 1000 WHERE AccountID = 1;
UPDATE Accounts SET Balance = Balance + 1000 WHERE AccountID = 2;
COMMIT;

Using SAVEPOINT:

START TRANSACTION;
UPDATE Accounts SET Balance = Balance - 1000 WHERE AccountID = 1;
SAVEPOINT beforeCredit;
UPDATE Accounts SET Balance = Balance + 1000 WHERE AccountID = 2;
-- Suppose this fails
ROLLBACK TO beforeCredit;
-- Retry crediting or rollback completely

Transaction Management in Applications

Modern applications (e.g., web apps, mobile apps) use transactions to:

  • Ensure consistency during business operations
  • Maintain logs and audit trails
  • Perform rollback on errors

Example in Python with psycopg2:

conn = psycopg2.connect(...)
cursor = conn.cursor()
try:
    cursor.execute("BEGIN")
    cursor.execute("UPDATE Accounts SET Balance = Balance - 500 WHERE ID = 1")
    cursor.execute("UPDATE Accounts SET Balance = Balance + 500 WHERE ID = 2")
    conn.commit()
except Exception as e:
    conn.rollback()
    print("Transaction failed:", e)

JDBC (Java):

conn.setAutoCommit(false);
try {
    stmt.executeUpdate(...);
    conn.commit();
} catch(SQLException e) {
    conn.rollback();
}

Best Practices

  1. Use transactions around multiple related DML operations
  2. Use COMMIT explicitly – don’t rely on auto-commit
  3. Minimize transaction duration to avoid locks
  4. Catch exceptions and use ROLLBACK wisely
  5. Combine SAVEPOINT for complex transactions
  6. Profile and optimize long-running transactions

Common Mistakes to Avoid

MistakeRisk
Forgetting to commitLeaves locks or uncommitted changes
Holding transaction open for too longCauses lock contention and timeouts
Mixing unrelated operations in one txHarder to roll back selectively
Not handling errors in stored proceduresRisk of partial data changes
Using SERIALIZABLE unnecessarilySlower performance; deadlocks

Monitoring Transactions

Use tools or queries to monitor active transactions:

PostgreSQL:

SELECT * FROM pg_stat_activity WHERE state = 'active';

SQL Server:

SELECT * FROM sys.dm_tran_active_transactions;

MySQL:

SHOW ENGINE INNODB STATUS;

These help identify long-running or blocked transactions.

Summary

Transactions are essential for ensuring correctness, reliability, and consistency in SQL-based systems. Combined with proper concurrency control, they form the backbone of robust, multi-user applications.

Key Takeaways:

  • Transactions follow ACID principles
  • Isolation levels control visibility and prevent conflicts
  • Locks are crucial but must be used wisely
  • Savepoints help manage complex logic
  • Always handle transactions explicitly in applications

In the next chapter, we will explore User Access Control and Security in SQL, where you’ll learn how to manage users, roles, privileges, and protect your data against unauthorized access.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top