[{"data":1,"prerenderedAt":102},["ShallowReactive",2],{"qa-\u002Fsql\u002Ftransactions\u002Ftransactions":3},{"page":4,"siblings":94,"blog":99},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":85,"related":86,"seo":87,"seoDescription":88,"stem":89,"subtopic":90,"topic":6,"topicSlug":91,"updated":92,"__hash__":93},"qa\u002Fsql\u002Ftransactions\u002Ftransactions.md","Transactions",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","SQL","sql",{},true,1,"\u002Fsql\u002Ftransactions\u002Ftransactions",[23,28,32,36,40,44,48,52,56,60,64,69,73,77,81],{"id":24,"difficulty":25,"q":26,"a":27},"what-is-transaction","easy","What is a database transaction?","A **transaction** is a sequence of one or more SQL statements that the\ndatabase treats as a single unit of work — either **all succeed** or\n**none take effect**. Transactions ensure that partial failures never leave\nthe database in an inconsistent state.\n\n```sql\n-- Transfer $100 from account 1 to account 2 atomically\nBEGIN;\n  UPDATE accounts SET balance = balance - 100 WHERE id = 1;\n  UPDATE accounts SET balance = balance + 100 WHERE id = 2;\nCOMMIT;\n-- If either UPDATE fails, ROLLBACK is triggered and neither change persists.\n```\n\nWithout a transaction, a crash between the two UPDATEs would subtract $100\nfrom account 1 but never add it to account 2 — money disappears.\n\n**Rule of thumb:** wrap any sequence of writes that must succeed or fail\ntogether in a single transaction. A transaction that touches only one row\nis still valid — it gives you the crash-recovery guarantee.\n",{"id":29,"difficulty":25,"q":30,"a":31},"acid-properties","What are the ACID properties?","**ACID** is the set of guarantees a database must provide for transactions\nto be reliable:\n\n- **Atomicity** — the transaction is all-or-nothing. A failure at any point\n  rolls back every change made so far.\n- **Consistency** — a transaction can only bring the database from one valid\n  state to another. All constraints, triggers, and rules still hold after\n  the commit.\n- **Isolation** — concurrent transactions do not see each other's uncommitted\n  changes. The degree of isolation is configurable (see isolation levels).\n- **Durability** — once committed, the changes survive crashes. The database\n  writes them to persistent storage (WAL \u002F redo log) before acknowledging\n  the commit.\n\n```sql\n-- Atomicity: if the second UPDATE fails, the first is rolled back\nBEGIN;\n  UPDATE orders SET status = 'shipped' WHERE id = 42;\n  INSERT INTO shipments (order_id, shipped_at) VALUES (42, now()); -- fails?\nCOMMIT; -- only reaches here if both statements succeed\n```\n\n**Rule of thumb:** when someone asks \"how does your database prevent X?\" the\nanswer maps to one of the four ACID letters. Know which letter covers which\nclass of problem.\n",{"id":33,"difficulty":25,"q":34,"a":35},"begin-commit-rollback","What do BEGIN, COMMIT, and ROLLBACK do?","- **`BEGIN`** (or `START TRANSACTION`) opens a new transaction. All\n  subsequent statements are part of this transaction until it is ended.\n- **`COMMIT`** permanently saves all changes made in the transaction and\n  releases any locks held.\n- **`ROLLBACK`** discards all changes made since `BEGIN` and releases locks.\n  The database returns to the state it was in before the transaction started.\n\n```sql\nBEGIN;\n  INSERT INTO invoices (customer_id, total) VALUES (7, 299.99);\n  UPDATE accounts SET balance = balance - 299.99 WHERE customer_id = 7;\nCOMMIT;   -- both rows persist\n\n-- Error path\nBEGIN;\n  DELETE FROM orders WHERE id = 99;\nROLLBACK; -- deletion is undone\n```\n\nIn Postgres, a failed statement inside a transaction automatically puts the\ntransaction into an **error state** — further statements are rejected until\nyou issue `ROLLBACK` (or `ROLLBACK TO SAVEPOINT`).\n\n**Rule of thumb:** always pair every `BEGIN` with either a `COMMIT` or a\n`ROLLBACK`. An open transaction that is never closed holds locks and blocks\nother sessions.\n",{"id":37,"difficulty":25,"q":38,"a":39},"autocommit","What is autocommit and how does it affect transactions?","In **autocommit mode** (the default in most databases), every SQL statement\nthat is not inside an explicit `BEGIN` block is automatically wrapped in its\nown single-statement transaction and committed immediately.\n\n```sql\n-- Autocommit ON (default): each statement is its own transaction\nUPDATE users SET email = 'new@example.com' WHERE id = 1;\n-- ^ committed immediately, cannot be rolled back\n\n-- Explicit transaction: autocommit suspended until COMMIT\u002FROLLBACK\nBEGIN;\n  UPDATE users SET email = 'new@example.com' WHERE id = 1;\n  -- still uncommitted, can still ROLLBACK\nCOMMIT;\n```\n\n- **Postgres**: autocommit is on by default; `BEGIN` suspends it.\n- **MySQL**: autocommit is on by default; `SET autocommit = 0` or `BEGIN` turns it off.\n- **SQL Server**: autocommit is on by default; `BEGIN TRANSACTION` starts an explicit one.\n\n**Rule of thumb:** never rely on autocommit for multi-statement operations.\nAlways open an explicit transaction when two or more writes must be atomic.\n",{"id":41,"difficulty":14,"q":42,"a":43},"savepoint","What is a SAVEPOINT and when would you use one?","A **SAVEPOINT** marks a point within a transaction that you can roll back\nto without aborting the entire transaction. This lets you recover from a\npartial failure while keeping the work done before the savepoint.\n\n```sql\nBEGIN;\n  INSERT INTO orders (customer_id, total) VALUES (5, 100.00);\n\n  SAVEPOINT after_order;\n\n  INSERT INTO order_items (order_id, product_id, qty) VALUES (99, 1, 2);\n  -- ^ suppose this fails (e.g. FK violation)\n\n  ROLLBACK TO SAVEPOINT after_order;\n  -- the orders INSERT is still intact; only order_items is undone\n\n  -- Try a different fix or log the error, then commit what we have\nCOMMIT;\n```\n\nSavepoints are especially useful in ORMs and application frameworks that\nwrap nested operations in sub-transactions.\n\n**Rule of thumb:** use savepoints for \"nested transaction\" patterns — when\na library or service call may fail but you want to keep the outer transaction\nalive. Don't overuse them; a simpler design often avoids the need.\n",{"id":45,"difficulty":14,"q":46,"a":47},"release-savepoint","What does RELEASE SAVEPOINT do?","`RELEASE SAVEPOINT name` destroys the savepoint but **does not commit or\nroll back** the work done since it. The changes remain part of the enclosing\ntransaction and will be committed or rolled back with it.\n\n```sql\nBEGIN;\n  INSERT INTO audit_log (event) VALUES ('start');\n\n  SAVEPOINT sp1;\n  UPDATE config SET value = 'new' WHERE key = 'theme';\n  RELEASE SAVEPOINT sp1;   -- sp1 is gone; UPDATE is still pending\n\n  -- Cannot ROLLBACK TO SAVEPOINT sp1 anymore\nCOMMIT; -- both the INSERT and UPDATE persist\n```\n\nAfter `RELEASE`, you can no longer roll back to that savepoint name.\n`RELEASE` is useful to free memory when you are certain you will not need\nto roll back to a particular point.\n\n**Rule of thumb:** release savepoints once you are confident the work they\ncover is correct. This is a minor housekeeping step; most applications omit\nit since savepoints are released automatically on `COMMIT` or `ROLLBACK`.\n",{"id":49,"difficulty":14,"q":50,"a":51},"implicit-transaction","What is an implicit transaction in SQL Server?","SQL Server supports an **implicit transaction** mode (`SET IMPLICIT_TRANSACTIONS ON`)\nwhere the database automatically begins a transaction before each DML or DDL\nstatement without an explicit `BEGIN TRANSACTION`. The user must still\nissue `COMMIT` or `ROLLBACK` to end it.\n\n```sql\n-- SQL Server with implicit transactions enabled\nSET IMPLICIT_TRANSACTIONS ON;\n\nUPDATE products SET price = price * 1.1; -- transaction auto-started\n-- still uncommitted! Must explicitly end it:\nCOMMIT;\n```\n\nThis differs from autocommit (where each statement commits automatically)\nand from explicit transactions (where you write `BEGIN TRANSACTION`).\nImplicit transactions are an easy source of long-running uncommitted\ntransactions and should be used carefully.\n\n**Rule of thumb:** avoid `IMPLICIT_TRANSACTIONS ON` in SQL Server — it\nsurprises developers who expect autocommit behavior. Prefer explicit\n`BEGIN TRANSACTION … COMMIT` for clarity.\n",{"id":53,"difficulty":14,"q":54,"a":55},"transaction-log","What is the transaction log (WAL) and why does it matter?","The **transaction log** (called **WAL** — Write-Ahead Log — in Postgres)\nrecords every change before it is written to the main data files. On a\ncrash, the database replays the log to bring data back to a consistent\nstate (redo) and removes uncommitted changes (undo).\n\n```\nTimeline of a COMMIT:\n1. Changes are written to the WAL on disk  ← durability guaranteed here\n2. Database acknowledges COMMIT to the client\n3. Changes are eventually flushed from buffer pool to data files\n(crash between steps 2 and 3 is safe — WAL replay restores the data)\n```\n\nThe WAL also powers **replication** (streaming the log to replicas) and\n**point-in-time recovery** (replaying the log up to a specific timestamp).\n\n**Rule of thumb:** understand that `COMMIT` does NOT mean \"data is in the\ntable file\" — it means \"data is in the WAL and therefore durable.\" The\nactual table file update is asynchronous.\n",{"id":57,"difficulty":14,"q":58,"a":59},"long-running-transactions","Why are long-running transactions harmful?","A long-running transaction holds **row\u002Fpage locks** that block other writers,\naccumulates **undo\u002Frollback data** that inflates the database's version store\nor transaction log, and in Postgres prevents **VACUUM** from reclaiming dead\nrow versions (causing table bloat).\n\n```sql\n-- Postgres: find long-running transactions\nSELECT pid,\n       now() - pg_stat_activity.xact_start AS duration,\n       query,\n       state\nFROM   pg_stat_activity\nWHERE  xact_start IS NOT NULL\n  AND  now() - xact_start > INTERVAL '5 minutes'\nORDER  BY duration DESC;\n\n-- Terminate if necessary\nSELECT pg_terminate_backend(pid) FROM pg_stat_activity\nWHERE  now() - xact_start > INTERVAL '1 hour';\n```\n\n**Rule of thumb:** keep transactions as short as possible. Do not hold an\nopen transaction while waiting for user input, making HTTP calls, or doing\nslow computation. Open the transaction, write, commit — then do anything\nslow.\n",{"id":61,"difficulty":14,"q":62,"a":63},"deadlock","What is a deadlock and how does the database resolve it?","A **deadlock** occurs when two (or more) transactions each hold a lock that\nthe other needs, so neither can proceed.\n\n```\nSession A: locks row 1, waits for row 2\nSession B: locks row 2, waits for row 1\n→ circular wait → deadlock\n```\n\nDatabases automatically detect deadlocks via a cycle-detection algorithm\nand resolve them by choosing a **victim** (typically the transaction with\nthe least work done) and rolling it back with an error.\n\n```sql\n-- Avoid deadlocks by always locking rows in the same order\n-- BAD: Session A locks user 1 then order 5; Session B locks order 5 then user 1\n-- GOOD: both sessions always lock by (user_id, order_id) order\n\n-- Postgres: SELECT FOR UPDATE to acquire locks explicitly in order\nSELECT * FROM users WHERE id = 1 FOR UPDATE;\nSELECT * FROM orders WHERE id = 5 FOR UPDATE;\n```\n\n**Rule of thumb:** prevent deadlocks by always acquiring locks in a\n**consistent order** across all transactions. Keep transactions short.\nHandle deadlock errors in application code with a retry loop.\n",{"id":65,"difficulty":66,"q":67,"a":68},"optimistic-vs-pessimistic","hard","What is the difference between optimistic and pessimistic locking?","**Pessimistic locking** acquires a lock *before* reading the data and holds\nit until the transaction commits — preventing any other writer from touching\nthe row during that window.\n\n**Optimistic locking** reads the data without a lock, does work, then\nchecks at write time whether another writer has changed the data since it\nwas read. If yes, it retries rather than committing stale data.\n\n```sql\n-- Pessimistic: lock the row immediately on read\nBEGIN;\n  SELECT * FROM inventory WHERE product_id = 42 FOR UPDATE;\n  -- no other transaction can UPDATE this row until we COMMIT\n  UPDATE inventory SET stock = stock - 1 WHERE product_id = 42;\nCOMMIT;\n\n-- Optimistic: use a version column to detect conflicts\n-- Read phase (no lock):\nSELECT stock, version FROM inventory WHERE product_id = 42;\n-- → stock=10, version=7\n\n-- Write phase: only update if version hasn't changed\nUPDATE inventory\nSET    stock = 9, version = 8\nWHERE  product_id = 42 AND version = 7;\n-- If 0 rows affected → conflict detected → retry\n```\n\n**Rule of thumb:** use pessimistic locking for high-contention resources\n(inventory, seat reservations) where conflicts are frequent. Use optimistic\nlocking for low-contention resources where conflicts are rare — it scales\nbetter by avoiding lock waits.\n",{"id":70,"difficulty":14,"q":71,"a":72},"select-for-update","What does SELECT FOR UPDATE do?","`SELECT FOR UPDATE` reads rows and immediately acquires an **exclusive row\nlock** on each row returned, preventing other transactions from updating or\nlocking those rows until the current transaction commits or rolls back.\n\n```sql\nBEGIN;\n  -- Lock the row so no other session can change it before we write\n  SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;\n  -- → balance = 500\n\n  UPDATE accounts SET balance = balance - 100 WHERE id = 1;\nCOMMIT;\n```\n\nVariants:\n- `FOR SHARE` — shared lock; others can read but not write.\n- `FOR UPDATE SKIP LOCKED` (Postgres, MySQL 8+) — skip rows already locked;\n  useful for job queues where workers should not compete for the same row.\n- `FOR UPDATE NOWAIT` — fail immediately if the row is already locked.\n\n**Rule of thumb:** use `SELECT FOR UPDATE` when you read a value and\nimmediately use it to compute an update — it closes the time-of-check \u002F\ntime-of-use race condition that would exist if the read and write were\nunprotected.\n",{"id":74,"difficulty":66,"q":75,"a":76},"skip-locked","How do you implement a concurrent job queue with SKIP LOCKED?","`SKIP LOCKED` lets multiple workers pull jobs from a queue table without\ncompeting for the same row — each worker skips rows already locked by\nanother worker.\n\n```sql\n-- Schema\nCREATE TABLE job_queue (\n  id         BIGSERIAL PRIMARY KEY,\n  payload    JSONB NOT NULL,\n  status     TEXT NOT NULL DEFAULT 'pending',\n  created_at TIMESTAMPTZ NOT NULL DEFAULT now()\n);\n\n-- Worker: claim one job atomically\nBEGIN;\n  SELECT id, payload\n  FROM   job_queue\n  WHERE  status = 'pending'\n  ORDER  BY created_at\n  LIMIT  1\n  FOR UPDATE SKIP LOCKED;   -- skip rows locked by other workers\n\n  UPDATE job_queue SET status = 'processing' WHERE id = \u003Cclaimed_id>;\nCOMMIT;\n```\n\nEach worker gets a different row. If a worker crashes, the transaction\nrolls back, returning the row to `pending` for another worker to claim.\n\n**Rule of thumb:** `SELECT … FOR UPDATE SKIP LOCKED` is the correct pattern\nfor building a reliable job queue in SQL. It is atomic, crash-safe, and\nscales to many concurrent workers without external coordination.\n",{"id":78,"difficulty":66,"q":79,"a":80},"two-phase-commit","What is two-phase commit (2PC) and when is it used?","**Two-phase commit** is a distributed coordination protocol that ensures\na transaction spanning **multiple independent databases** either commits\non all of them or rolls back on all.\n\n- **Phase 1 (Prepare)**: the coordinator asks each participant to prepare\n  (write to their WAL, acquire locks, but do not commit). Each replies\n  \"yes\" or \"no\".\n- **Phase 2 (Commit\u002FAbort)**: if all said \"yes\", the coordinator tells all\n  to commit. If any said \"no\", the coordinator tells all to abort.\n\n```sql\n-- Postgres prepared transactions (the participant side of 2PC)\nBEGIN;\n  UPDATE accounts SET balance = balance - 100 WHERE id = 1;\nPREPARE TRANSACTION 'txn-xyz-001'; -- phase 1: prepared, not committed\n\n-- Later, coordinator decides to commit or rollback:\nCOMMIT PREPARED 'txn-xyz-001';\n-- or\nROLLBACK PREPARED 'txn-xyz-001';\n```\n\n**Rule of thumb:** 2PC solves cross-database atomicity but adds latency\nand coordinator failure risk. In modern systems, the **Saga pattern**\n(compensating transactions) is often preferred over 2PC for microservice\narchitectures.\n",{"id":82,"difficulty":14,"q":83,"a":84},"transaction-best-practices","What are the most important best practices for writing transactions?","1. **Keep transactions short** — open, write, commit. Do not hold a\n   transaction open while making network calls, waiting for user input, or\n   running slow computations.\n2. **Access tables in a consistent order** — prevents deadlocks across\n   concurrent transactions that touch the same set of tables.\n3. **Handle errors explicitly** — always `ROLLBACK` on any exception; never\n   swallow errors and then commit.\n4. **Do not use transactions for reads alone** — unless you need a consistent\n   snapshot across multiple queries, a plain `SELECT` outside a transaction\n   is cheaper.\n5. **Retry on transient failures** — deadlocks and serialization failures\n   are expected; build retry logic with exponential back-off.\n\n```sql\n-- Pattern: wrap in try\u002Fcatch, always rollback on error (Python psycopg2)\ntry:\n    cur.execute(\"BEGIN\")\n    cur.execute(\"UPDATE ...\")\n    cur.execute(\"INSERT ...\")\n    cur.execute(\"COMMIT\")\nexcept Exception:\n    cur.execute(\"ROLLBACK\")\n    raise\n```\n\n**Rule of thumb:** a transaction should be as wide as necessary (all writes\nthat must be atomic) and no wider. Every extra statement inside a transaction\nis a longer lock hold and a bigger rollback payload.\n",15,null,{"description":11},"SQL transactions interview questions — ACID properties, BEGIN\u002FCOMMIT\u002FROLLBACK, SAVEPOINT, autocommit, implicit vs explicit transactions, and best practices across Postgres, MySQL, and SQL Server.","sql\u002Ftransactions\u002Ftransactions","Transactions & ACID","transactions","2026-06-20","OBbo4RNRAA_7U1YkzIh4CFLgUM6Qp7vn03xJXnQssNU",[95,96],{"subtopic":90,"path":21,"order":20},{"subtopic":97,"path":98,"order":12},"Isolation Levels & Concurrency","\u002Fsql\u002Ftransactions\u002Fisolation-concurrency",{"path":100,"title":101},"\u002Fblog\u002Fsql-transactions-acid-commit-rollback","SQL Transactions — ACID, COMMIT, ROLLBACK, and SAVEPOINT",1782244107280]