When you send βΉ10,000 from your bank account, two things must happen simultaneously: your balance decreases by βΉ10,000, and the recipient's balance increases by βΉ10,000. If the server crashes between these two steps, where does the money go? ACID Properties are the mathematical guarantees that prevent this nightmare.
ACID is a set of four properties that every reliable database transaction must satisfy. These properties are what separate a production-grade database (MySQL, PostgreSQL, Oracle) from a simple file or spreadsheet.
| Property | One-Line Description | Technical Meaning |
|---|---|---|
| A β Atomicity | "All or Nothing" | A transaction is treated as a single unit. Either ALL operations succeed, or NONE of them are applied. No partial completion. |
| C β Consistency | "Valid Before and After" | Every transaction brings the database from one valid state to another. All constraints (PRIMARY KEYs, FOREIGN KEYs, CHECK, NOT NULL) are always enforced. |
| I β Isolation | "Independent Transactions" | Concurrent transactions are invisible to each other while in progress. The result is as if they ran sequentially. |
| D β Durability | "Permanent Once Committed" | Once a COMMIT is executed, the data is permanently saved β even if the server loses power one millisecond later. |
START TRANSACTION;
UPDATE Accounts SET Balance = Balance - 10000 WHERE Account_ID = 1; -- Step 1
UPDATE Accounts SET Balance = Balance + 10000 WHERE Account_ID = 2; -- Step 2
COMMIT;
-- If Step 2 fails (e.g., Account 2 doesn't exist), ROLLBACK reverses Step 1 too.
-- The βΉ10,000 doesn't vanish β it's as if neither step ever ran.-- Example: A FOREIGN KEY constraint enforces Consistency
ALTER TABLE Orders ADD CONSTRAINT fk_cust
FOREIGN KEY (Customer_ID) REFERENCES Customers(Customer_ID);
-- This INSERT will FAIL (violates Consistency β no Customer_ID=999 exists):
INSERT INTO Orders (Customer_ID, Total) VALUES (999, 500);
-- ERROR 1452: Cannot add or update a child row: a foreign key constraint failsUser A (in a transaction): reads Balance = βΉ50,000
User B: transfers βΉ20,000 into User A's account and commits
User A: reads Balance again β still sees βΉ50,000 (isolated from B's change!)
User A: commits β now sees the updated βΉ70,000
(This is REPEATABLE READ isolation β MySQL's default)
MySQL writes every committed transaction to the Redo Log on disk before confirming success. Even if the power fails immediately after a COMMIT, MySQL reads the Redo Log on restart and completes the write.
| ACID Property | MySQL Mechanism |
|---|---|
| Atomicity | Undo Log β stores the "before" values so ROLLBACK can reverse changes |
| Consistency | Constraint checking (FK, PK, CHECK, NOT NULL) at commit time |
| Isolation | MVCC (Multi-Version Concurrency Control) β each transaction reads from a snapshot |
| Durability | Redo Log (Write-Ahead Log) β changes written to Redo Log before the actual table page |
The Redo Log (WAL β Write-Ahead Log):
- Before MySQL writes to the actual InnoDB
.ibddata file, it first writes the change to the Redo Log file. - On crash recovery, MySQL reads the Redo Log and "replays" any committed-but-not-yet-written transactions.
- This allows MySQL to confirm COMMIT instantly (Redo Log write is fast β sequential) rather than waiting for random disk writes to the data file.
Example 1 β E-commerce Checkout (All 4 ACID properties in action):
START TRANSACTION;
-- Step 1: Reduce stock (will be rolled back if anything fails) β ATOMICITY
UPDATE Products SET Stock = Stock - 2 WHERE Product_ID = 45;
-- Step 2: Create order (FOREIGN KEY check ensures Customer exists) β CONSISTENCY
INSERT INTO Orders (Customer_ID, Total, Status) VALUES (123, 1499.98, 'Pending');
-- Step 3: Other transactions can't see the reduced stock yet β ISOLATION
INSERT INTO Order_Items (Order_ID, Product_ID, Qty) VALUES (LAST_INSERT_ID(), 45, 2);
COMMIT; -- Data now permanently saved on disk β DURABILITYExample 2 β Hospital Patient Record (Atomicity):
START TRANSACTION;
-- Admit patient and allocate a bed simultaneously
INSERT INTO Admissions (Patient_ID, Ward_ID, Admit_Date) VALUES (789, 12, NOW());
UPDATE Wards SET Beds_Available = Beds_Available - 1 WHERE Ward_ID = 12;
-- If the ward is already full (Beds_Available < 0 via CHECK constraint):
-- MySQL rolls back BOTH changes. Patient not admitted AND no bed count change.
COMMIT;| Storage Engine | ACID Compliant? | Notes |
|---|---|---|
| InnoDB | β Yes β Full ACID | MySQL's default since version 5.5 |
| MyISAM | β No | No transactions, no rollback |
| MEMORY | β No | Data lost on server restart |
Always use InnoDB for any table that handles business-critical data.
- Using MyISAM for important tables: MyISAM has no transaction support β no ROLLBACK. If a query fails mid-way, partial changes are permanent. Check
SHOW CREATE TABLE tablenameto verify engine is InnoDB. - Assuming autocommit = ACID protection: Autocommit means every statement is its own transaction. It provides Atomicity per statement, but NOT for multi-step operations. Always use explicit
START TRANSACTIONfor multi-step work. - Thinking ACID prevents applications from crashing: ACID protects the database layer. Application code can still have bugs β ACID just ensures the database remains consistent even when the application fails.
- Always use InnoDB for any table that stores user data, financial records, or any business-critical information.
- Wrap multi-step operations in transactions:
START TRANSACTION ... COMMITis the mechanism that activates ACID's Atomicity guarantee across multiple statements. - Test rollback scenarios: Include test cases that deliberately fail mid-transaction to verify your application correctly rolls back and leaves the database clean.
- Task 1: Explain all 4 ACID properties using the analogy of a bank transfer. For each property, describe what would go wrong if that specific property was violated.
- Task 2: Which MySQL mechanism (Redo Log vs Undo Log) handles Atomicity, and which handles Durability? Explain the difference.
- Task 3: A table uses
MyISAMstorage engine. A developer runs anUPDATEthat modifies 5,000 rows and the server crashes after updating 2,000 rows. What happens? How does the answer change if the table usesInnoDB?