...

Database query cache behavior in hosting: optimization for better performance

I explain how the mysql query cache behavior in modern hosting environments, why MySQL 8.0 has abolished the internal query cache and how I can become noticeably faster with Redis or Memcached. I will show you clear levers for Query caching, cache validation, monitoring and hardware, with which websites deliver more often from the cache and databases work less.

Key points

  • MySQL 8.0: Internal query cache removed, external caches taken over.
  • In-MemoryFrequently read data from RAM at lightning speed.
  • InvalidationTTL, events and versioning against outdated data.
  • MonitoringHit ratio, latency and evictions control tuning.
  • 300%Correct caching reduces load and increases performance.

Query cache behavior in hosting briefly explained

When requests come in, I first check whether the result is already in the Cache is located. If it is there, I respond without database access and save latency and CPU time on the Database server. If the entry is missing, I create the result, save it in the cache and deliver it so that the next hit is faster and the Page load time decreases. In this way, I reduce the number of identical queries and reduce the server load for recurring accesses to popular contents. In hosting setups with many similar requests (start page, product lists, menu structures), the query cache behavior brings significant benefits. Acceleration.

From MySQL Query Cache to Redis/Memcached: the modern way

The old MySQL query cache slowed down many write accesses, so MySQL 8.0 removed the Function. I rely on Redis or Memcached instead, because I can use them to store caches independently of the Database and can use granular keys, TTLs and eviction strategies. This noticeably reduces the load on MySQL, because read requests hit the In-memory cache, while MySQL concentrates on real transactions. I deliberately keep cache keys small, version them when changes are made and thus ensure a high level of security. Hit rate. This approach delivers consistent responses at high utilization and scales across multiple Worker or containers.

Why was the internal query cache really removed? It blocked highly parallelized systems through global locks, often invalidated complete table areas when changes were made and caused a lot of administration overhead with mixed read/write workloads. The result: the more write accesses, the lower the benefit - up to and including network brakes. Modern caches are therefore located outside of MySQL, use isolated TTLs per key, allow horizontal scaling and can be deployed independently. MySQL itself continues to benefit from the InnoDB buffer pool, good indexes and prepared statements - but result caching remains the task of the application level.

Understanding cache levels: in-memory, database, application

I differentiate between three levels so that the Caching application-related cache (Redis/Memcached), database-related cache (e.g. buffer pool) and HTTP/reverse proxy caches. Close to the application, I cache complete query results or rendered Fragments, which offers the highest flexibility. Close to the database, I benefit from optimized indexes and the InnoDB Buffer Pool, which stores frequently read pages in the RAM holds. At the HTTP level, I minimize dynamic calls when content is really static are. I offer a quick overview of tactics in the compact Guide to caching strategies, which facilitates the appropriate use depending on the application scenario.

Caching patterns in comparison

I choose the pattern according to the risk, the frequency of change and the need for consistency:

  • Cache-Aside (lazy loading): Application checks cache, loads from DB on miss, writes to cache. Simple, flexible, low coupling - but susceptible to stampedes when the TTL expires.
  • Read-ThroughA cache layer loads automatically from the data source. Uniform behavior, but additional complexity in the intermediate layer.
  • write-through: With each write, data first moves to the cache and then to the DB. Very consistent, but the write path is longer.
  • Write-BehindCache accepts write operations and flows asynchronously into the DB. Fast, but tricky in the event of failure; only use with clear guarantees.
  • Stale-While-RevalidateExpired entries can be briefly returned „old“, while a background job fills fresh entries. Ideal against load peaks.

Cache validation without data errors

I plan cache invalidation in such a way that current data always has priority and Speed remains. I set Time-to-Live (TTL) short enough to show changes promptly, but long enough so that the hit ratio remains high. During write operations, I delete specific keys (write-through/write-behind) or increase a Version in the key namespace so that subsequent accesses pull the fresh data set. For sensitive content (prices, stocks, accounts) I use shorter TTL or immediate invalidation after updates. This prevents outdated responses and maintains data consistency in distributed Systems.

Prevent cache stampede: stale-while-revalidate, locks and jitter

To avoid the „dogpile problem“, I use combined mechanisms: a Soft TTL, which allows a few seconds of „stale“ while a single-flight worker updates the object; a short Mutex (e.g. via Redis SET NX + TTL), so that only one process reloads; and a Jitter to TTLs (random deviation) so that thousands of keys do not expire at the same time. In the event of errors in the original source, I allow stale-if-error and protect the database from avalanches.

Size, TTL and eviction: the right adjustment screws

I choose the cache size to match the data volume, which is worthwhile in the RAM to lie. Too small increases misses, too large wastes memory, so I measure continuously and react to Load peaks. For eviction, I prefer to use LRU if access patterns are cyclical, and switch to LFU for clear access patterns. Perennial favorites. I keep TTLs differentiated: static navigation longer, dynamic product availability shorter. The following table shows typical starting values, which I then refine using monitoring and adjust to real Use customize.

Parameters Purpose starting value Measured variable
Cache size RAM budget for query or fragment caches 5-15% of the server RAM Evictions/minute, RAM utilization
TTL static Menus, category pages, frequent listings 300-1800 seconds Hit ratio, timeliness requirement
TTL dynamic Prices, stock, personalization 10-120 seconds Error rate, corrections
Eviction LRU/LFU/FIFO per access pattern LRU as standard Miss rate, repeat accesses
Key scheme Versioning against outdated data user:v1:queryhash Missing hit after deploy

I also take object size distributions and upper limits into account. I compress individual objects over 512 KB, for example, or divide them into pages (paging) so that evictions do not displace entire megabyte blocks. Different caches (e.g. „hot“ and „cold“) with separate sizes prevent a few large objects from displacing the many small, frequently read entries.

Key design and normalization

Good keys determine the hit rate and invalidation capability. I normalize query parameters (sorting, upper/lower case, default values), convert lists into a canonical order and hash long parameters into a Query hash, so that keys remain short. I separate facets cleanly in the key: site:v3:en-EN:category:42:page:2:filter:abc123. Personalization, client, currency, locale and device category belong visibly in the namespace. I quantize numerical parameters (e.g. I round price filters to meaningful buckets) to avoid duplicates. Negative caches (e.g. „no hit“) with a very short TTL reduce DB accesses with repeated Miss-Search.

Select serialization and compression correctly

I choose formats according to interface and CPU budget: JSON is universal and legible, MessagePack or Protobuf save RAM/bandwidth. For large objects I use LZ4 or Snappy for fast compression; Gzip only if maximum size is more important than CPU. One Threshold (e.g. from 4-8 KB) prevents small data from being compressed unnecessarily. I pay attention to stable schemas: If I add fields, I increase the Key version, so that old parsers do not break.

Redis vs. memcached: Differences in operation

Memcached scores with its simple architecture, multithreading and Slabs for efficient allocation. It is the first choice for very simple key/value results with extremely high QPS without the need for persistence. Redis offers data structures (hashes, sets, sorted sets), fine TTL control, replication and cluster capability. Redis is ideal for lists, leaderboards, counters and pub/sub. As a pure result cache, I disable persistence (or set sparse snapshots) to save I/O. I use Pipeline and MGET, to reduce round trips, and choose the eviction policy to match the access pattern (allkeys-lfu for clear, continuous burners, volatile-lru for strict TTL usage). I distribute hot keys via sharding/clusters, or I deliberately replicate them several times to cushion bottlenecks.

Monitoring and tuning during operation

I observe the hit ratio, the latency per cache operation and the eviction rate to detect bottlenecks. If the latency increases, I check network paths, CPU saturation and the serialization of objects. I reduce large objects by compressing them or divide them into smaller ones in order to Memory to make better use of it. If the hit ratio drops, I identify missing keys and adjust TTLs or Key schemes on. Tuning remains a cycle of measuring, hypothesizing, adapting and re-tuning. Measurement.

Specific key figures help to analyze the causes: keyspace_hits/misses, evicted_keys, reclaimed (Memcached), used_memory and RSS-deviations for fragmentation, P99 latencies per command, network error rates and Slowlog-entries. I pay attention to continuous, non-jumpy evictions, evenly distributed object sizes and the proportion of „stale served“. If miss→db→set is more frequent than planned, either the TTL is not correct or the keys are varied too widely (lack of normalization).

Security and high availability

I never expose cache servers publicly, but bind them to internal interfaces/VPCs, activate ACLs and where possible TLS. I strictly separate production, staging and test environments so that no keys collide and no data migrates. I lock critical operations (FLUSH*) via authorizations. For Failover I use replication and, depending on the technology, automatic switching (e.g. watchdog/sentinel/cluster). As a pure result cache, persistence is used sparingly or not at all - if the cache fails, the application may only be slower, but correct. I limit commands that scan entire keyspaces and only plan backups where the cache is also used. Source of Truth is (rarely the case).

WordPress and e-commerce: typical patterns and pitfalls

With WordPress, I cache menu structures, query results from WP_Query and important Widgets, while I exclude personalized parts. I make sure that plug-ins do not receive every request. Bypass, by setting sessions or constantly changing cookies. For store systems, I cache category pages, bestseller lists and filter results with short TTL, while shopping baskets and account pages remain dynamic. Those who rely on the old query cache often worsen the Performance; I explain why this is the case here: WordPress Query Cache. This is how I maintain the balance between speed and correct Personalization.

I also vary caches in the right places: Currency, Language, Location and Customer group influence prices, availability and content. I decouple personalization from the rest: The page comes from the cache, only small blocks (e.g. shopping cart count) are dynamically reloaded. For highly variable filters (facets), I normalize the order and build page keys (page=1,2,...) instead of generating huge, confusing keys. And I make sure that „no result“ responses are cached for a short time to reduce DB scans.

Capacity planning and cost model

I make a rough calculation in advance: Average object size × expected number of keys + overhead (10-30%) gives the RAM base. Example: 80,000 objects à 6 KB plus 25% overhead ≈ 600 MB. I plan buffers for growth (e.g. 30-50%). On the throughput side, I estimate read/write ratio, targethit ratio (70-95%) and the resulting reduction in database load. If 60% of the previous DB reads are served from the cache, not only CPU and IOPS load are reduced, but often also the Replication-Lags. I price scenarios: Make RAM more expensive, save DB cores - usually the RAM investment wins significantly because it brings more consistent response times.

InnoDB Buffer Pool, Query Plan and Indexes together

I don't optimize in isolation, but look at the cache, Buffer Pool, query plan and indexes as a package. A well-dimensioned buffer pool increases InnoDB hits, reduces I/O and strengthens each Cache about it. I check slow queries, create missing indexes and keep statistics fresh so that the Optimizer gets the best results. Plan selects. For more in-depth steps, this Buffer pool optimization, which I use in parallel with caching. This adds up to speed: less I/O, more RAM hits and more efficient caching. Queries.

In practical terms, this means that I size the buffer pool so that „hot“ data pages fit into it without starving the operating system. Query profiles reveal whether full-table scans, suboptimal JOINs or missing covering indexes are undermining caches. I check whether SELECTs that are too wide (unnecessary columns) generate large cache objects and slim them down. If queries vary greatly, I normalize parameters in the application or reduce them to a few reusable variants.

Using hardware resources correctly

I reserve enough RAM for Redis/Memcached and for InnoDB Buffer pool so that hard disks hardly block. I pay attention to CPU cores so that the application and cache server can run simultaneously. work can. NVMe SSDs reduce the residual latency if a cache miss does become a problem. Memory takes effect. Network latency remains important, which is why I place cache servers close to the App or in the same host. These decisions often save hosting costs in euros because I can work with fewer cores and lower Load achieve the same response times.

In addition, I pay attention to NUMA and socket topologies, pin processes to cores if necessary and use short network paths (or Unix sockets on the same host). For container setups, I plan „guaranteed“ resources so that the cache is not throttled and keep headroom for peak loads. If hot keys are unavoidable, I distribute traffic across multiple replicas or route it to the most local cache to avoid cross-zone latencies.

Rollout, tests and cache warmup

I test caching changes with load profiles that reflect real usage data. In production, I roll out in stages (Canary), observe hit ratio, latencies and DB load and only then increase the TTLs. For deployments, I increase the Key version and warm up the top N-keys (homepage, top sellers, important categories). Background jobs fill list and detail pages in a targeted manner so that the first users do not bear the warm-up costs. I simulate evictions (test environment) and stress hot paths to verify stampede protection and jitter.

Step-by-step plan for better hosting performance

I start with an inventory: slow Queries, log files, hit ratio, evictions and CPU/RAM profiles. I then define cache keys for the most important pages and create TTLs that balance timeliness and speed. I build in write-through or event-based invalidation for changes so that Consistency remains. I then measure again, increase or decrease TTLs, adjust the cache size and remove Outliers with large objects. Finally, I sharpen the buffer pool, indexes and plans until the page delivery is noticeable. liquid is running.

Briefly summarized

I replace the old MySQL query cache with Redis or Memcached, consciously control keys, TTLs and evictions and keep data reliable with clear invalidation. Depending on the application, I achieve 200-300% Speed, especially when many identical requests arrive. Monitoring guides my decisions: If the hit ratio drops or the latency increases, I adjust the size, TTL and key on. Together with a strong InnoDB buffer pool and clean indexes, the platform scales better and is very responsive. fast. If you understand the mysql query cache behavior as a complete system, you save server load, reduce costs in euros and provide users with a crisp User experience.

Current articles