Skip to main content

Query Cache

Older MySQL tutorials sometimes recommend enabling the query cache. For WordPress, it is usually a net negative: it can introduce contention under concurrency and provides limited benefit for an application that constantly writes to shared tables (options, sessions, orders, carts).

Action Required

Do not enable the MySQL query cache for WordPress.

  • MySQL removed query cache in 8.0.
  • On older MySQL or on MariaDB, ensure any query cache directives are disabled/removed.

The Structural Failure of Query Cache

The theoretical concept of trapping database query results locally to avoid redundant fetching logically aligns with the WordPress caching paradigm. However, the exact mechanical execution of the MySQL internal cache completely destroys concurrent applications.

The Invalidation Death Loop

The MySQL Query Cache operates blindly utilizing exact macroscopic table-level validation rules.

  1. The cache logs the exact result of SELECT * FROM wp_options WHERE option_name = 'siteurl'.
  2. A random plugin cleanly fires an UPDATE command altering a completely unrelated row deeply inside the exact same wp_options table.
  3. Because the wp_options table formally received a write command, the legacy Query Cache systematically dumps the entirety of every single cached query strictly tied to that table instantly, regardless of explicit mapping.
  4. The database subsequently burns massive CPU cycles attempting to rebuild the destroyed caching matrix, only for another background plugin to instantly trigger the identical table-invalidation purge.

The Global Locking Bottleneck

To securely maintain absolute data integrity during these massive invalidation purges, the database engine enforces a hard global mutex lock. During this lock, the database strictly entirely refuses to accept any incoming SELECT statements globally, forcing all active PHP FPM workers to hang completely simultaneously until the purge formally finalizes.

The Disablement Procedure

You must cleanly map the configuration directly into your master configuration file to definitively ensure the cache engine shuts down natively.

1. Identify the Current State

query-cache-check.sh
# Query the active runtime variables securely
mysql -e "SHOW VARIABLES LIKE 'query_cache%';"

If query_cache_size returns any integer greater than zero safely, or query_cache_type registers as ON, you are actively shedding CPU resources managing a fragmented cache.

2. Force the Disablement Directive

Inject the explicit overrides into the configuration safely:

my.cnf
[mysqld]
# Ensure the logic strictly maps to zero mathematically
query_cache_type = 0
query_cache_size = 0
query_cache_limit = 0

3. Cycle the Execution Engine

apply-cache-override.sh
sudo systemctl restart mysql

The Mandatory Alternative (Object Caching)

If you disable the database query cache, the replacement is not another database-level cache. Instead, use the right caching layers for WordPress:

  • Page cache + CDN cache for public pages
  • InnoDB buffer pool for hot data/index pages
  • Object caching (Redis) for repeated dynamic lookups (especially logged-in/admin/checkout flows)

Common Mistakes & Troubleshooting

Configuration FailureOperational SymptomRemediation Protocol
Legacy upgrades fail to startUpgrading to MySQL 8 fails with Unknown variable 'query_cache_size'.Remove old query cache directives from your config.
Performance regression after disablingResponse time gets worse after removing query cache settings.Confirm OPcache is configured and consider object caching/page caching rather than DB query caching.

What's Next