Your site's running fine, then someone hits checkout on WooCommerce or a couple of editors save posts at the same time, and MySQL throws back Deadlock found when trying to get lock; try restarting transaction. The page fails, the order doesn't save, and if it's happening under real traffic, support tickets start piling up fast. Here's what's actually going on and how to stop it for good.
Symptom: What You'll Actually See
The error rarely shows up as a clean white page. Usually you'll spot it in one of these places:
- WooCommerce checkout fails with a generic "An error occurred" message, and the order sits as Failed or never appears at all.
- Your PHP error log (or
wp-content/debug.logifWP_DEBUG_LOGis on) shows a line likeWordPress database error Deadlock found when trying to get lock; try restarting transaction for query UPDATE wp_options SET... - Bulk actions in wp-admin (deleting posts, updating stock in bulk) fail partway through.
- Cron-heavy plugins (stock sync, backup, SEO re-indexing) log the same deadlock message right when traffic peaks.
The key detail: it's intermittent. The exact same click that failed once will often succeed on retry. That's the fingerprint of a locking problem, not a broken query.
Cause: Why MySQL Deadlocks in the First Place
InnoDB (the storage engine almost every WordPress and WooCommerce install uses) locks rows during a transaction so two processes can't corrupt the same data at once. A deadlock happens when two transactions each hold a lock the other one needs — Transaction A has locked row 1 and wants row 2, Transaction B has locked row 2 and wants row 1. Neither can proceed, so MySQL picks one as the "victim," kills it, and returns the deadlock error so your application can retry.
On a typical WordPress site this shows up around a handful of predictable hot spots:
| Trigger | What's actually locking |
|---|---|
| WooCommerce checkout at volume | wp_woocommerce_order_itemmeta and stock rows in wp_postmeta updated by concurrent orders |
| Autoloaded options | wp_options rows being written by cron, caching plugins, and admin-ajax heartbeat all at once |
| Session/cart plugins | Custom session tables updated on every page load under high concurrency |
| Bulk edits + live traffic | A long-running bulk UPDATE holding locks while normal visitor requests try to write the same rows |
None of this means your database is broken. It usually means the table's indexing or the transaction size doesn't match how much concurrent traffic is hitting it.
Fix: Get the Site Working Again
Start by confirming this really is a deadlock and not something else masquerading as one.
mysql -u root -p -e "SHOW ENGINE INNODB STATUS\G" | less
Scroll to the LATEST DETECTED DEADLOCK section. It names the exact two queries and the exact table/index involved — don't guess, read it. That's the fastest way to know whether this is WooCommerce stock updates, a plugin's custom table, or something in wp_options.
Once you know the table, work through these in order:
- Add a retry wrapper if you're on WooCommerce. Recent WooCommerce versions already retry deadlocked stock transactions automatically. If you're on an older version, update it — this single fix resolves the majority of checkout deadlocks.
- Check for missing indexes. A deadlock is far more likely when a query has to scan/lock more rows than necessary because a column isn't indexed. Run
EXPLAINon the query named in the InnoDB status output and confirm it's using an index, not a full table scan. - Shorten long-running transactions. If a plugin wraps a big bulk operation in one transaction, split it into smaller batches (a few hundred rows at a time) so it doesn't hold locks for seconds at a stretch.
- Reduce autoloaded options bloat. A bloated
wp_optionstable with hundreds of autoloaded rows makes every single page load write-contend on the same table. Check it with:
SELECT SUM(LENGTH(option_value)) AS bytes, COUNT(*) AS rows
FROM wp_options WHERE autoload = 'yes';
Anything over a few MB of autoloaded data is worth cleaning up — old transients that never expired are usually the culprit.
- Lower
innodb_lock_wait_timeoutfor faster failure instead of long hangs if requests are piling up and slowing everything else down while they wait on a lock:
SET GLOBAL innodb_lock_wait_timeout = 15;
This doesn't fix the root cause, but it stops one stuck transaction from cascading into a full site slowdown while you work on the real fix.
Prevention: Stopping It From Coming Back
Once the immediate fire is out, a few habits keep deadlocks rare instead of routine:
- Keep WooCommerce, WordPress core, and caching plugins current. Locking behavior around stock and cart tables gets tuned in almost every WooCommerce release.
- Cap concurrent checkout load with a queue or hold-stock plugin if you regularly run flash sales — that's the single biggest source of checkout-time deadlocks.
- Audit cron jobs. If your backup plugin, SEO plugin, and a stock-sync tool are all scheduled at the same wall-clock time, stagger them. Simultaneous heavy writes are exactly what creates lock contention.
- Trim autoloaded options regularly. A plugin like WP-Optimize or a manual query cleanup every few months keeps
wp_optionslean. - Use object caching (Redis or Memcached) so repeated reads don't turn into repeated writes on session/meta tables under load.
If deadlocks keep appearing on a specific custom table a plugin created, that's worth flagging to the plugin author directly — it usually means their schema is missing an index that would let InnoDB lock a narrower row range instead of a wider one.
Frequently Asked Questions
Does a deadlock mean I lost data?
No. MySQL rolls back the losing transaction cleanly — nothing gets half-written. The failed request (a checkout, a save) needs to be retried, but existing data stays consistent.
Why does it only happen during busy periods?
Deadlocks need at least two transactions competing for the same rows at the same moment. Low traffic means requests rarely overlap; high traffic means they constantly do, so the same underlying weak point only shows up under load.
Is this a hosting problem or a WordPress/plugin problem?
Almost always it's application-level — how WordPress, WooCommerce, or a plugin structures its queries and transactions. Hosting resources (CPU, RAM) rarely cause deadlocks directly, though an underpowered VPS running MySQL can make transactions run longer and increase the odds of overlap.
Can I just disable transactions to stop the errors?
Don't. Transactions are what keep your orders and post data from getting corrupted when two things write at once. Removing them trades a visible, recoverable error for silent data corruption, which is much worse.
Where do I check this on a SkyServer VPS or cPanel plan?
On a VPS, run the SHOW ENGINE INNODB STATUS command over SSH as shown above. On shared cPanel hosting, use phpMyAdmin's SQL tab to run the same query, or open a support ticket and we'll pull the InnoDB status for you.
