Transactions, Concurrency, and Data IntegrityIntroductionA database is valuable only when its stored data remains correct while many users and application processes access it simultaneously. In a banking system, an order platform, or a student-registration application, operations rarely consist of one isolated SQL statement. They usually involve a sequence of reads and writes that must succeed together or have no lasting effect. Transactions provide this unit of work. This chapter examines transactions, the ACID properties, concurrency problems, isolation levels, locking, deadlocks, commit and rollback, and integrity controls. MySQL examples use the InnoDB storage engine, because transaction support, row-level locking, foreign keys, and crash recovery depend on the storage engine. MongoDB is mentioned for comparison, but the practical implementation focus is MySQL. Learning objectivesBy the end of this chapter, you should be able to: - explain atomicity, consistency, isolation, and durability;
- distinguish logical integrity constraints from transaction controls;
- identify dirty reads, non-repeatable reads, phantom reads, and lost updates;
- compare MySQL isolation levels and locking mechanisms;
- design commit, rollback, and error-handling workflows;
- diagnose and reduce deadlocks;
- implement safe transactional workflows in MySQL;
- evaluate integrity, security, and operational risks in database applications.
1. Transactions as units of workA transaction is a logically related sequence of database operations treated as one unit. For example, transferring 100 units from account A to account B requires both a debit and a credit. A successful transfer must not leave only one account changed. A transaction normally moves through states: - Active: statements are executing.
- Partially committed: the final statement has completed, but the transaction has not necessarily been made durable.
- Committed: the database guarantees that its effects are permanent under the system's durability assumptions.
- Failed: an error prevents successful completion.
- Aborted: changes are undone through rollback.
A transaction boundary is defined by commands such as START TRANSACTION, COMMIT, and ROLLBACK. In MySQL, many data-definition statements cause implicit commits, so schema changes should not be mixed casually with business transactions. The ACID model| Property | Meaning | Typical protection |
|---|
| Atomicity | All intended changes occur, or none do | Undo logging and rollback | | Consistency | A committed transaction preserves declared and business rules | Constraints, validation, triggers, and correct logic | | Isolation | Concurrent transactions appear sufficiently separated | Locking and multiversion concurrency control | | Durability | Committed effects survive failures, subject to configuration | Redo logging, flushing, and recovery |
ACID is a set of guarantees, not a promise that an application is automatically correct. A transaction can be atomic and durable while still storing an incorrect price if application validation is wrong. Likewise, a database constraint can preserve a rule, but it cannot by itself decide whether a business process is appropriate. 2. Consistency and integrity controlsConsistency has two related meanings. At the database level, it means that every committed state satisfies defined rules. At the application level, it means that the result makes business sense. Strong database design uses both. Entity, domain, and referential integrity- Entity integrity: each row has a stable identity, normally enforced with a primary key.
- Domain integrity: values belong to an allowed domain, enforced through data types,
NOT NULL, CHECK, and suitable defaults. - Referential integrity: a foreign-key value refers to an existing parent row, unless nullability and the model explicitly permit absence.
- Uniqueness integrity: values such as usernames or invoice numbers are not duplicated when uniqueness is required.
Example: CREATE TABLE customers (
customer_id BIGINT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) NOT NULL UNIQUE,
status ENUM('active', 'blocked') NOT NULL DEFAULT 'active'
) ENGINE = InnoDB;
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY AUTO_INCREMENT,
customer_id BIGINT NOT NULL,
total DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_order_total CHECK (total >= 0),
CONSTRAINT fk_order_customer
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
) ENGINE = InnoDB;A DECIMAL(10,2) column is preferable to floating-point arithmetic for monetary values because it represents fixed decimal precision. The CHECK constraint protects the database even when a second application writes to it. The exact behavior of CHECK constraints should be verified against the deployed MySQL version; modern MySQL 8.0 versions enforce them, whereas older versions accepted but ignored some definitions. Constraints should be preferred for rules that belong to the data model. Application validation remains necessary for messages, workflows, and rules requiring external information. A unique constraint, for example, is the final protection against two concurrent requests registering the same email; checking first in application code is not sufficient. 3. Concurrency and anomaliesConcurrency allows several transactions to make progress without waiting for every other transaction to finish. It improves throughput and responsiveness, but uncontrolled concurrency can produce incorrect observations or updates. Consider two transactions, T1 and T2, operating on the same row: | Anomaly | Description |
|---|
| Dirty read | T2 reads data written by T1 before T1 commits; T1 may later roll back | | Non-repeatable read | T1 reads a row twice and obtains different committed values because T2 updated it between reads | | Phantom read | Repeating a range query returns a different set of rows because another transaction inserted or deleted matching rows | | Lost update | One transaction overwrites another transaction's change without incorporating it | | Write skew | Two transactions read a condition and update different rows, together violating a business rule |
Isolation is a spectrum. Preventing every anomaly can require more locking and can reduce concurrency. The appropriate level depends on the business risk and workload. 4. MySQL isolation levelsMySQL provides four standard isolation levels. InnoDB uses consistent, non-locking reads for ordinary SELECT statements under its multiversion concurrency control (MVCC) model. Locking reads such as SELECT ... FOR UPDATE behave differently. | Isolation level | General guarantee | Typical trade-off |
|---|
| READ UNCOMMITTED | May observe uncommitted changes | Highest anomaly risk | | READ COMMITTED | Reads committed versions; each statement gets a current view | More concurrency, less repeatability across statements | | REPEATABLE READ | Consistent non-locking reads in a transaction use a stable snapshot; locking behavior also matters | Strong default behavior with possible waits and range effects | | SERIALIZABLE | Transactions behave most like a serial order | Highest contention and lowest concurrency |
The precise behavior depends on access type, indexes, predicates, and InnoDB's locking rules. Isolation level is not a substitute for explicit protection of a critical read-modify-write operation. A session can select an isolation level as follows: SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
-- transactional work
COMMIT; Use the narrowest scope necessary. A global change affects other workloads and should be made only after measurement and review. MVCC and consistent readsMVCC maintains versions so that a reader can view a logically consistent committed state while writers proceed. This often avoids blocking ordinary readers. It does not mean that all operations are conflict-free: updates, deletes, locking reads, unique checks, and foreign-key checks can still wait for locks. A transaction that must reserve a row for subsequent modification should use a locking read: SELECT available
FROM inventory
WHERE product_id = 42
FOR UPDATE; The selected row is locked until commit or rollback. The statement must use a suitable index; otherwise, more rows may be examined and locked than intended. 5. Locking mechanismsA lock controls concurrent access to a data item. InnoDB commonly uses row-level locks, although the physical and logical effects depend on indexes and predicates. Important concepts include: - Shared lock: permits compatible reads but generally prevents conflicting writes.
- Exclusive lock: required for changes and prevents incompatible access.
- Record lock: protects an indexed record.
- Gap lock: protects a range between index records under relevant conditions.
- Next-key lock: combines a record lock with a preceding gap lock.
- Intention lock: signals that a transaction intends to place row locks within a table.
A plain SELECT normally does not lock rows for later updating. SELECT ... FOR UPDATE requests exclusive-style protection for rows that will be changed. SELECT ... FOR SHARE requests a shared lock where supported and appropriate. Locking is useful in two common patterns. Pessimistic control locks a resource before changing it: START TRANSACTION;
SELECT balance FROM accounts WHERE account_id = 10 FOR UPDATE;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 10;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 20;
COMMIT; The application must also validate that account 10 has sufficient funds and that both rows exist. A production transfer should lock both accounts in a consistent order, such as ascending account_id, to reduce deadlocks. Optimistic control allows concurrent work but verifies that the row has not changed. A version column is useful: UPDATE products
SET price = 19.99, version = version + 1
WHERE product_id = 7 AND version = 3; If the affected-row count is zero, the application reports a conflict or retries. This approach can reduce waiting when conflicts are rare. 6. Commit, rollback, and savepointsCOMMIT ends the transaction successfully. ROLLBACK reverses changes made since the transaction began, subject to the storage engine and transaction boundaries. A savepoint permits partial rollback:
START TRANSACTION;
INSERT INTO orders(customer_id, total) VALUES (5, 49.90);
SAVEPOINT after_order;
-- Optional operation
INSERT INTO order_audit(order_id, event) VALUES (LAST_INSERT_ID(), 'created');
-- If the optional operation fails:
ROLLBACK TO SAVEPOINT after_order;
COMMIT; A savepoint does not release all locks acquired by the transaction, and it does not make earlier work durable. Long transactions should be avoided because they retain locks, consume undo resources, and increase the chance of contention. Applications should use a transaction template: - Validate request shape and authorization outside or before the critical transaction where possible.
- Start a transaction.
- Read and lock the necessary rows in a deterministic order.
- Check current values and business rules.
- Perform all related writes.
- Commit only after every required operation succeeds.
- Roll back on exceptions and return a safe error.
- Retry only errors known to be transient, such as deadlock victims or lock wait timeouts.
Never report success before the commit has succeeded. A connection pool must also reset transaction state before returning a connection to another request. 7. Deadlocks and lock waitsA lock wait occurs when one transaction must wait for another to release a conflicting lock. A deadlock is a cycle of waiting: T1 holds a resource needed by T2, while T2 holds a resource needed by T1. No transaction can proceed without intervention. Illustrative sequence: - T1 locks account 10 and requests account 20.
- T2 locks account 20 and requests account 10.
- T1 and T2 wait for one another.
InnoDB detects many deadlocks and rolls back one transaction, called the victim. The application must treat the resulting error as a failed transaction and may retry the complete unit of work with bounded backoff. It must not retry only the last SQL statement, because the transaction may already have been rolled back. Deadlock prevention and reduction include: - access shared tables and rows in a consistent order;
- keep transactions short;
- use indexes that match search predicates;
- avoid user interaction or network calls inside a transaction;
- update only necessary rows;
- avoid unnecessary range locks;
- inspect deadlock reports and query plans;
- set sensible lock-wait timeouts;
- implement bounded, observable retries.
Deadlocks are not necessarily evidence of a defective database; they are a normal possibility in concurrent systems. The design goal is to make them rare, quickly recoverable, and visible. 8. Safe MySQL workflow: order placementThe following illustrative case shows how transaction design combines integrity constraints, locking, and error handling. Background and problemAn online retailer must create an order, reserve stock, and record payment status. Two customers may attempt to buy the last unit of the same product at nearly the same time. A naive workflow reads stock in application code, waits, and then updates it. Both requests may observe one unit and create orders, producing overselling. AnalysisThe stock check and decrement are one business operation. A unique order identifier is useful, but it does not by itself protect inventory. The inventory row must be changed conditionally within a transaction. ApproachA robust schema might include: CREATE TABLE inventory (
product_id BIGINT PRIMARY KEY,
quantity INT NOT NULL,
CONSTRAINT chk_quantity CHECK (quantity >= 0)
) ENGINE = InnoDB;
CREATE TABLE order_items (
order_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
quantity INT NOT NULL,
PRIMARY KEY (order_id, product_id),
CONSTRAINT chk_item_quantity CHECK (quantity > 0),
FOREIGN KEY (product_id) REFERENCES inventory(product_id)
) ENGINE = InnoDB;A concise transactional implementation is: START TRANSACTION;
INSERT INTO orders(customer_id, total)
VALUES (5, 29.90);
SET @order_id = LAST_INSERT_ID();
UPDATE inventory
SET quantity = quantity - 1
WHERE product_id = 42 AND quantity >= 1;
-- Application checks that ROW_COUNT() = 1.
INSERT INTO order_items(order_id, product_id, quantity)
VALUES (@order_id, 42, 1);
COMMIT; The application must roll back if the update affects zero rows, if the product does not exist, if an integrity constraint fails, or if payment authorization is unsuccessful. In a real system, external payment calls are usually not held inside a long database transaction. Instead, an order state machine and an idempotency key coordinate payment attempts and later fulfillment. Outcome and learning pointsThe conditional update makes the stock decrement atomic with the availability test. The transaction ensures that an order without its corresponding inventory reservation is not committed. The foreign key prevents an item from referring to an unknown product. A unique idempotency key prevents a client retry from creating duplicate business operations. This case also illustrates an important boundary: database transactions provide atomicity inside the database, not automatically across an external payment provider, email service, or message broker. Such distributed workflows require patterns such as an outbox table, explicit states, reconciliation, and idempotent consumers. 9. MongoDB comparisonMongoDB provides atomicity for a single-document write and supports multi-document transactions in replica sets and sharded deployments under documented operational conditions. Its document model often permits related data to be embedded so that one business operation affects one document. When data is distributed across documents or collections, a transaction may be appropriate, but it introduces coordination cost. The same design principles remain relevant: identify the unit of work, enforce schema and validation rules, keep transactions short, handle transient errors, and make retries safe. MongoDB's schema flexibility is not the absence of integrity requirements; validation rules, unique indexes, application checks, and careful modeling are still needed. 10. Testing, observability, and governanceTransaction correctness should be tested under concurrency, not only with single-user unit tests. Useful tests include simultaneous stock purchases, duplicate submissions, forced deadlocks, connection interruption during commit, rollback after constraint failure, and retry behavior. Operational evidence may include transaction duration, lock-wait time, deadlock count, rollback rate, failed commits, and query-plan changes. MySQL diagnostic tools such as SHOW ENGINE INNODB STATUS, performance schema views, and slow-query analysis can help identify contention. Monitoring must avoid exposing personal or financial data in logs. Integrity is also a governance concern. Use least-privilege database accounts, parameterized queries, encrypted connections, protected credentials, audited administrative actions, and carefully controlled backups. Constraints reduce accidental corruption, while authorization controls reduce unauthorized change. Neither replaces the other. 11. Limitations and best practicesNo isolation level can correct flawed business requirements. Serializable execution may reduce throughput, and read committed may be insufficient for a multi-step decision. Locks can protect rows but cannot automatically coordinate external services. Rollback cannot undo an email already sent. A committed transaction can still contain an incorrect value if the application supplied incorrect input. Recommended practices are: - choose InnoDB for transactional MySQL tables;
- define primary keys, foreign keys, unique constraints, suitable types, and check constraints;
- keep transactions short and focused;
- use parameterized SQL;
- lock rows explicitly for read-modify-write operations when required;
- access multiple resources in a consistent order;
- check affected-row counts and commit results;
- handle deadlocks and transient failures with complete bounded retries;
- design idempotent APIs and use idempotency keys;
- test concurrency and failure paths;
- monitor locks, transaction duration, and rollback behavior;
- document isolation assumptions and connection-pool cleanup.
Chapter SummaryTransactions define reliable units of database work. The ACID properties describe atomicity, consistency, isolation, and durability, but each depends on appropriate storage-engine behavior, constraints, and application design. Concurrency improves performance while introducing dirty reads, non-repeatable reads, phantoms, lost updates, and write skew. MySQL isolation levels provide different trade-offs, while MVCC and explicit locking coordinate readers and writers. Primary keys, foreign keys, unique rules, data types, and check constraints protect structural integrity. Commit makes a completed transaction durable under configured recovery guarantees; rollback removes its uncommitted effects, and savepoints support limited partial recovery. Deadlocks are cycles of lock waits and should be reduced through consistent access order, short transactions, suitable indexes, and complete bounded retries. Safe workflows also require idempotency, observability, security, and explicit coordination with external systems. Key TermsACID: The four properties of reliable transactions: atomicity, consistency, isolation, and durability. Atomicity: The all-or-nothing property of a transaction. Commit: The operation that successfully ends a transaction and makes its effects durable under system guarantees. Consistency: Preservation of declared and business integrity rules. Deadlock: A cyclic wait in which transactions hold resources needed by one another. Durability: Persistence of committed changes after permitted failures. Foreign key: A constraint linking a child value to a referenced parent key. Gap lock: A lock protecting an index interval between records. Isolation: The degree to which concurrent transactions are separated. Lost update: An update that unintentionally overwrites another transaction's update. MVCC: Multiversion concurrency control, which provides version-based consistent reads. Optimistic control: Conflict handling that validates whether data changed before applying an update. Pessimistic control: Conflict handling that locks data before a critical operation. Rollback: Reversal of uncommitted transaction changes. Savepoint: A named position to which part of a transaction can be rolled back. Serializable: The strongest standard isolation level, approximating serial execution. Shared lock: A lock compatible with certain reads but generally incompatible with conflicting writes. Transaction: A logically related sequence of database operations treated as one unit. Unique constraint: A rule preventing duplicate values in a specified key or column combination. Review QuestionsShort Answer Questions- Define a database transaction and explain why an order placement may require one.
- State the four ACID properties.
- What is the difference between a dirty read and a non-repeatable read?
- Why is
SELECT ... FOR UPDATE useful in a read-modify-write workflow? - What is a deadlock, and how does InnoDB generally respond to one?
Long Answer Questions- Compare MySQL's four standard isolation levels and discuss their concurrency trade-offs.
- Explain how primary keys, foreign keys, unique constraints, data types, and
CHECK constraints contribute to data integrity. - Describe a complete commit and rollback workflow for a service using a connection pool.
- Explain MVCC and distinguish ordinary consistent reads from locking reads.
- Discuss why database transactions do not automatically provide atomicity across a database and an external payment service.
Analytical / Application Questions- Two transactions transfer money between the same two accounts, but one locks the source account first and the other locks the destination account first. Identify the risk and propose a precise redesign.
- An inventory application first executes
SELECT quantity, then waits for payment confirmation, and finally decrements quantity. Analyze the concurrency risks and redesign the workflow. - A service retries a failed request after a network timeout, but the original transaction may have committed. Design an idempotent strategy that prevents duplicate orders and supports reconciliation.
Multiple Choice Questions with AnswersQuestion 1Which ACID property requires all operations in a transaction to succeed together or have no lasting effect? A. Atomicity B. Consistency C. Isolation D. Durability Correct Answer: A. Atomicity Question 2Which constraint prevents two customers from using the same value where uniqueness is required? A. Foreign key B. UNIQUE constraint C. CHECK constraint D. DEFAULT clause Correct Answer: B. UNIQUE constraint Question 3What is the principal purpose of SELECT ... FOR UPDATE in InnoDB? A. To create a backup copy of a table B. To read rows and lock them for a later modification C. To change the transaction isolation level D. To release all existing locks Correct Answer: B. To read rows and lock them for a later modification Question 4Which anomaly occurs when a transaction reads data written by another transaction that has not yet committed? A. Phantom read B. Lost update C. Dirty read D. Write skew Correct Answer: C. Dirty read Question 5What is a deadlock? A. A transaction that contains no update statements B. A cyclic wait among transactions holding conflicting resources C. A committed transaction whose data is durable D. A query that uses an index Correct Answer: B. A cyclic wait among transactions holding conflicting resources Question 6Which command permanently completes a successful transaction under the database's durability guarantees? A. SAVEPOINT B. ROLLBACK C. COMMIT D. TRUNCATE Correct Answer: C. COMMIT Question 7Why should multiple resources commonly be locked in a consistent order? A. To increase the number of duplicate rows B. To reduce the likelihood of deadlocks C. To disable foreign-key checking D. To force every query to use a table scan Correct Answer: B. To reduce the likelihood of deadlocks Question 8Which technique updates inventory only if sufficient stock still exists? A. UPDATE inventory SET quantity = quantity - 1 WHERE product_id = 42 AND quantity >= 1 B. Reading quantity once and updating it later without a condition C. Deleting the inventory row before creating the order D. Using a non-transactional table for the update Correct Answer: A. UPDATE inventory SET quantity = quantity - 1 WHERE product_id = 42 AND quantity >= 1 Question 9What does a savepoint permit within a transaction? A. Making part of the transaction durable immediately B. Rolling back to an intermediate point without ending the whole transaction C. Preventing all other transactions from reading any table D. Changing a primary key automatically Correct Answer: B. Rolling back to an intermediate point without ending the whole transaction Question 10Why is an idempotency key useful when a client retries after a network timeout? A. It guarantees that external services never fail B. It prevents a repeated request from creating duplicate business effects C. It removes the need for database constraints D. It converts a relational database into MongoDB Correct Answer: B. It prevents a repeated request from creating duplicate business effects
|