session: prod โ€” read carefully before EXECUTE
db internals ยท safety guide
EXPLAIN ANALYZE your assumptions first

What happens
between SELECT and
the rows you get back.

A query doesn't just "run." It passes through five distinct stages before a single row reaches you โ€” and almost every production incident traces back to one of them. This is the actual flow, followed by the dos and don'ts mapped to the exact stage that causes them.

stages: 05 primary failure points: 02 read time: ~9 min

the journey of a query

Five stages, one row at a time

Click any stage below to see what it actually does, and โ€” for the two stages marked RISK โ€” exactly how it takes prod down when nobody's watching.

01
Parser
Syntax check, builds parse tree
02
Optimizer
Reads stats, picks the execution plan
03
Executor
Walks the plan, fetches rows
04 risk
Storage engine
Buffer pool, disk, locks, redo/undo
05 risk
Transaction mgr
Commit, rollback, isolation, locking

click a stage above โ†‘ โ€” stages 04 and 05 are where almost every prod incident actually happens

why the rules exist

Every "don't" maps to a stage above

None of these are arbitrary house rules. Each one exists because of something specific that happens at steps 4 or 5 of the flow you just walked through.

stage 02optimizer

The optimizer decides whether your query touches 50 rows or 5 million. EXPLAIN shows you its decision before you commit to it on prod. If you see type: ALL on a large table โ€” that's the optimizer telling you, in plain text, that it found no usable index and intends to scan every row.

stage 04storage engine

This is where row and table locks actually live. A write here doesn't just change data โ€” it holds a lock for as long as the statement runs, and every other transaction wanting that same data queues up behind it.

stage 05transaction mgr

Commit and rollback happen here, along with isolation-level bookkeeping. A transaction you forgot to close โ€” even a plain SELECT under REPEATABLE READ โ€” keeps an MVCC snapshot pinned open, which can block cleanup processes from reclaiming space.


field manual

The dos and don'ts

Click to expand. Each one names the stage it protects and shows the actual query pattern.

do Always EXPLAIN before you run on prod
stage 02 โ€” optimizer

Run EXPLAIN on every query that touches a table you don't fully trust the size of. If the plan shows type: ALL on something like OFBiz's OrderHeader or ReturnItem, stop โ€” that means a full table scan, which on a large table means scanning and potentially locking millions of rows.

EXPLAIN SELECT * FROM ReturnItem WHERE returnId = 'X';
-- look at the "type" column: const/eq_ref/ref = good, ALL = full scan
don't Run unbounded UPDATE / DELETE
stage 04 โ€” storage

A single big write holds row or table locks for its entire duration, queueing every other transaction touching that table behind it. This is the most common cause of "the SQL didn't even error but prod froze."

-- dangerous: locks every matching row at once, possibly the whole table
UPDATE OrderItem SET statusId = 'ITEM_CANCELLED' WHERE orderId = 'X';

-- safer: batch it
UPDATE OrderItem SET statusId = 'ITEM_CANCELLED'
WHERE orderId = 'X' LIMIT 500;
-- repeat with monitoring, or loop with a short sleep between batches
do Wrap risky writes in a transaction you can roll back
stage 05 โ€” txn mgr

Give yourself an exit. Run the write, sanity-check what actually changed, and only then decide whether to keep it.

START TRANSACTION;
UPDATE ...;
-- check row count, sanity check results
COMMIT; -- or ROLLBACK if something looks wrong
don't Leave long-running transactions open on prod
stage 05 โ€” txn mgr

An open transaction โ€” even a read-only SELECT under REPEATABLE READ โ€” holds onto an MVCC snapshot. That snapshot can block the engine's internal cleanup processes from reclaiming old row versions. Always commit or roll back promptly; don't leave a session idle mid-transaction while you go check something else.

do Treat missing indexes as the first suspect
stage 02 โ€” optimizer

Most "slow query brought down prod" stories trace back to a missing index on a JOIN or WHERE column โ€” not clever-but-wrong SQL syntax. Check the indexes before you start rewriting query logic.

SHOW INDEX FROM tablename;
do Run exploratory queries on a read replica
stage 04 โ€” storage

If a replica exists, point your SELECT-heavy investigation and reporting queries there. That keeps ad-hoc OFBiz reporting from competing with live order processing for locks and I/O on the primary.

do Always test with SELECT before UPDATE / DELETE
stage 03 โ€” executor

Run the read version of your write first. Confirm the exact row count and content it would touch โ€” only convert it to a write once that result looks right.

-- run this first to see exactly what you'd affect
SELECT * FROM ReturnItem WHERE returnId = 'X';
-- then convert to UPDATE only after confirming row count/content

before you hit enter on prod

  • EXPLAIN the query and confirm there's no full table scan on a large table.
  • SELECT first to see exactly what rows a write would touch.
  • LIMIT / batch any UPDATE or DELETE that could match more than a few hundred rows.
  • Wrap the write in a transaction you can roll back if the sanity check looks wrong.
  • Close the transaction immediately after โ€” don't leave it idle.
  • Replica for anything exploratory, not the primary.
further reading

For a much deeper dive into database internals โ€” storage engines, indexing strategy, and how production systems actually behave under load โ€” Arpit Bhayani's writing is one of the best ongoing resources on the subject: arpitbhayani.me

โ† Back to Portal