MySQL Deadlock Found: Causes and How to Fix It Fast

· 6 min read · 20 views · Getwebup

Your site throws a WooCommerce order into an error state, or a WP-CLI import halts, and buried in the log is ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction. It looks alarming, but it's not corruption and it's not a crashed table — it's MySQL doing exactly what it's designed to do. Here's what's actually happening, why it tends to show up on busy WooCommerce and high-traffic WordPress sites, and how to stop it from recurring.

What a Deadlock Actually Is

InnoDB (the storage engine behind almost every modern WordPress and WooCommerce database) locks rows when a transaction updates them, 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, and neither can proceed. MySQL detects this standoff and kills one of the transactions — the "victim" — to let the other one finish. That's the deadlock error you see.

This is normal, expected behaviour under concurrency, not a bug in MySQL. The problem is only that your application (WordPress, WooCommerce, a custom script) usually doesn't retry the failed transaction automatically, so the user sees a failed checkout, a stuck import, or a plugin that silently didn't save.

Where It Shows Up Most Often

  • WooCommerce checkout under load — multiple orders updating the same product's stock quantity in wp_postmeta at the same time.
  • WooCommerce sessions table (wp_woocommerce_sessions) — heavy traffic writing session data concurrently.
  • Bulk imports or updates — a WP-CLI or plugin script updating rows in a different order than a simultaneous cron job or another admin action.
  • Caching/cron plugins writing to wp_options or a transients table at the same moment a page load reads/writes the same autoloaded option.

Notice the pattern: it's almost always two processes touching the same rows, in a different order, at roughly the same time. Low-traffic sites rarely see this. Sites doing real concurrent writes — busy stores, membership sites, anything with WP-Cron piling up — see it regularly.

Confirm What's Actually Deadlocking

Don't guess. If you have SSH or cPanel Terminal access with MySQL privileges, pull the engine status right after (or during) an occurrence:

mysql -u root -p -e "SHOW ENGINE INNODB STATUS\G" | less

Scroll to the LATEST DETECTED DEADLOCK section. It lists both transactions, the exact queries involved, and which one was rolled back. This is the only reliable way to know which plugin, query, or cron job is actually colliding — guessing from the error message alone wastes time.

On shared cPanel hosting without root MySQL access, check WordPress → Site Health → Info → Database for slow queries, or enable WordPress debug logging (WP_DEBUG_LOG) and watch wp-content/debug.log around the time the error occurs — the failing query usually gets logged by whichever plugin issued it.

Fixing It

1. Make WordPress/WooCommerce retry automatically

Most single-occurrence deadlocks are harmless if the transaction just retries — that's literally what the error message tells MySQL's client to do. WooCommerce already retries some internal queries, but a custom plugin or script usually doesn't. If you're the one writing the query (a custom plugin, a migration script), wrap it:

global $wpdb;
$attempts = 0;
do {
    $result = $wpdb->query( $sql );
    $deadlocked = ( $wpdb->last_error && strpos( $wpdb->last_error, 'Deadlock' ) !== false );
    $attempts++;
} while ( $deadlocked && $attempts < 3 );

That alone silently fixes the vast majority of deadlock-related order failures.

2. Reduce lock contention at the source

If the same deadlock keeps recurring between the same two operations, retrying just delays the inevitable. Fix the actual contention:

  • WooCommerce stock sync — enable WooCommerce → Settings → Products → Inventory → Hold stock reasonably (don't set it to 0) and consider disabling "manage stock" on products that don't need real-time inventory, which removes a lot of the row-locking on wp_postmeta.
  • Move to HPOS (High-Performance Order Storage) if you haven't — it stores orders in dedicated tables instead of overloading wp_posts/wp_postmeta, which was never designed for that write volume.
  • Stagger cron jobs — if WP-Cron and a system cron job (or two plugins) both fire near the same minute and touch the same options, spread them out. Check cPanel → Cron Jobs and any plugin-scheduled events with wp cron event list.
  • Add missing indexes — deadlocks get more likely when a query without a proper index locks far more rows (or a full table scan) than it needs to. Check slow queries with EXPLAIN on the query from the deadlock output; a missing index on a WHERE or JOIN column is a common root cause.

3. Tune InnoDB lock settings (VPS/root access only)

On a VPS where you manage my.cnf yourself, two settings matter:

SettingWhat it doesReasonable value
innodb_lock_wait_timeoutHow long a transaction waits for a lock before giving up (this is a separate error from deadlock, but related)50 (default is usually fine)
innodb_deadlock_detectWhether InnoDB actively looks for deadlocks vs waiting out the timeoutON (default, keep it on)
innodb_print_all_deadlocksLogs every deadlock to the error log, not just the latest oneON while diagnosing a recurring issue

Turn on innodb_print_all_deadlocks temporarily if deadlocks are frequent enough that SHOW ENGINE INNODB STATUS keeps missing them between checks:

mysql -u root -p -e "SET GLOBAL innodb_print_all_deadlocks = ON;"

Every deadlock then lands in the MySQL error log (usually /var/log/mysqld.log or /var/lib/mysql/hostname.err on a VPS), which gives you a running history instead of a single snapshot.

4. Keep transactions short

The longer a transaction holds its locks, the bigger the window for another process to collide with it. If a custom script wraps a large batch of updates in one transaction, break it into smaller batches with commits in between — a bulk price update on 50,000 products as one transaction is a deadlock waiting to happen; the same update in batches of 500 almost never is.

Prevention Going Forward

  • Keep WooCommerce, WordPress core, and any plugin touching orders or stock fully updated — lock-contention bugs get patched regularly.
  • Move to HPOS if you're still on legacy post-based order storage and run a serious store.
  • Avoid stacking heavy cron jobs (backups, imports, report generation) at the same time as peak traffic.
  • If you're on shared cPanel hosting and deadlocks correlate with traffic spikes, that's often a sign the account has outgrown shared resources — a VPS with dedicated MySQL resources removes a lot of this contention entirely.
  • Monitor SHOW ENGINE INNODB STATUS or your error log after any big plugin update, since a new plugin querying the same tables differently is a common trigger for a fresh round of deadlocks.

One or two deadlocks a week on a busy store is normal and self-healing as long as retries are in place. Dozens a day, or the same query pair showing up every time, means it's time to fix the actual contention rather than keep retrying around it.

Questions people actually ask