Hook: A transaction log is the database’s receipt book: if the server crashes, it is how the engine remembers what was safely done and what still needs to be undone.
Question: What are transaction logs in SQL databases, and why do they matter?
Answer: Transaction logs are append-only records of database changes that help the system recover after a crash and keep committed work durable. They are a core part of ACID: Atomicity means all-or-nothing, and Durability means a committed change survives failure. In many engines this is implemented with write-ahead logging (WAL), which means the log is written before the data pages are finally flushed.
Interview-Ready Answer: “A transaction log is the database’s durable change record. The key idea is write-ahead logging: before the engine says a transaction is committed, it flushes the log to stable storage. That lets the database redo committed work and undo incomplete work after a crash, which is why transactions stay atomic and durable.”
Detailed Explanation: Think of the transaction log as the database’s time-ordered notebook. Every change is appended in the order it happened, so the engine can later replay the notebook from a safe point. The most important mental model is simple: log first, data later. That single rule is what keeps committed changes safe after a power loss.
A transaction log is usually an append-only file or set of files that records enough information to reconstruct changes. Different engines use different names: WAL in PostgreSQL-style systems, redo log in some others, and transaction log in SQL Server-style terminology. The exact format is engine-specific, but the goal is the same: protect committed work.
BEGIN.Flushed means written all the way to stable storage, not just sitting in RAM.redos committed work that had not reached the table files yet and undos incomplete work that never committed.Dirty pages are pages in memory that were modified but not yet written back to disk. The log makes it safe for those pages to trail behind, because recovery can rebuild the truth from the log.
Without a transaction log, a crash could leave half a payment saved, half an inventory update missing, or a row updated in one table but not another. The log gives the database a reliable “before/after” trail so it can choose the correct outcome after failure. It also lets the engine group many commits together, which is much faster than forcing each row update directly to disk.
| Item | Stores | Goal | Read pattern |
|---|---|---|---|
| Transaction log | Changes and commits | Recovery | Sequential append |
| Table data | Current rows | Queries | Random read/write |
| Audit log | Business events | Traceability | Query/report |
The big gotcha is that the engine’s transaction log is not the same as an application audit table. An audit table is something you design for humans and reporting. The internal transaction log is something the database uses to recover correctly, and you usually do not query it directly in normal SQL.
Performance note: log appends are usually amortized O(1) per change, because appending is sequential. Recovery time is roughly proportional to the amount of log replay since the last checkpoint. A checkpoint is a safe point where enough dirty pages have been flushed so recovery does not need to start from the very beginning. On fast SSDs, a single flush may still cost around 1–10 ms; group commit lets many transactions share one flush, which is why throughput can jump dramatically under load.
Edge cases and gotchas: long-running transactions can keep log records alive and make the log grow. If checkpointing falls behind, the log file can expand a lot. Also, “committed” does not always mean “the table file already changed”; it means the log is durable enough that the change can be recovered. Some databases offer delayed durability or relaxed flush settings, but that trades safety for speed and can lose the last few transactions in a crash.
Memory check: if you remember only one thing, remember this: the log is the source of truth during recovery, not the table file on disk.
Real-World Story: Imagine a checkout service for an e-commerce app. One transaction must place the order, charge the card, and decrease inventory. The team uses a transaction so those steps succeed together or fail together. If the machine crashes after the card is charged but before inventory is updated, the transaction log lets the database recover to a consistent state instead of leaving a ghost order.
Here is what a misunderstanding looks like in production: an engineer assumes “the INSERT happened, so the order is safe,” but the transaction never committed. After a restart, the row is gone because the log showed the transaction was incomplete. Users see missing orders, support sees payment receipts, and logs show confusing messages like “charged OK” followed by no committed database record. That is the exact kind of bug transaction logs are built to prevent when used correctly.
Symptoms: duplicate charges, missing orders, inventory mismatches, and recovery logs that mention redo/undo activity. User impact: customers get charged without a visible order, or orders appear in the app but disappear after a crash because the app wrote only part of the workflow outside one transaction.
-- A small, runnable teaching example for transaction logs in practice.
-- This shows the database idea with a real SQL transaction plus an
-- application-level audit table, because the engine's internal WAL/redo
-- log is usually not queryable from plain SQL.
CREATE TABLE accounts (
account_id INTEGER PRIMARY KEY,
owner VARCHAR(50) NOT NULL,
balance INTEGER NOT NULL CHECK (balance >= 0)
);
CREATE TABLE transaction_audit (
log_id INTEGER PRIMARY KEY,
event_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
event_type VARCHAR(20) NOT NULL,
details VARCHAR(200) NOT NULL
);
INSERT INTO accounts (account_id, owner, balance) VALUES
(1, 'Ava', 100),
(2, 'Ben', 50);
INSERT INTO transaction_audit (log_id, event_type, details) VALUES
(1, 'SEED', 'Created two demo accounts');
-- Successful transaction: both updates must happen together.
BEGIN TRANSACTION;
INSERT INTO transaction_audit (log_id, event_type, details)
VALUES (2, 'BEGIN', 'Transfer 30 from Ava to Ben');
UPDATE accounts
SET balance = balance - 30
WHERE account_id = 1;
UPDATE accounts
SET balance = balance + 30
WHERE account_id = 2;
INSERT INTO transaction_audit (log_id, event_type, details)
VALUES (3, 'COMMIT', 'Transfer 30 completed');
COMMIT;
-- Failure path: we detect a risky transfer and roll back before it becomes real.
BEGIN TRANSACTION;
SAVEPOINT risky_transfer;
INSERT INTO transaction_audit (log_id, event_type, details)
VALUES (4, 'BEGIN', 'Attempt transfer 999 from Ava to Ben');
-- In a real app, the client would check the balance before continuing.
-- We read first to show the precondition that would make the change unsafe.
SELECT account_id, owner, balance
FROM accounts
WHERE account_id = 1;
-- Instead of making a bad change and letting it hit the CHECK constraint,
-- we abort cleanly. This is the idea behind undoing incomplete work.
ROLLBACK TO SAVEPOINT risky_transfer;
INSERT INTO transaction_audit (log_id, event_type, details)
VALUES (5, 'ROLLBACK', 'Transfer 999 aborted: insufficient funds');
COMMIT;
SELECT account_id, owner, balance
FROM accounts
ORDER BY account_id;
SELECT log_id, event_time, event_type, details
FROM transaction_audit
ORDER BY log_id;Follow-up & Tricky Questions:
Common Mistakes:
Memory Hook: “The database keeps the receipt before it moves the furniture.” The receipt is the transaction log; the furniture is the table data.
Cheat Sheet:
redo for committed work and undo for incomplete work.Write-ahead logging means log before data.Practice Tasks:
COMMIT.BEGIN, COMMIT, and ROLLBACK events for a sample workflow.