Cache Consistency Strategies: Updating Database and Cache, Deleting Cache, and Handling Inconsistencies
The article explains why caching is essential for high‑traffic read‑heavy services, analyzes the consistency problems that arise when data exists simultaneously in MySQL and Redis, compares four cache‑update strategies, and provides practical recommendations such as using expiration, delayed double‑delete, message queues, and binlog subscription to achieve eventual consistency.
In real‑world business scenarios, data such as orders, members, and payments are persisted in relational databases (e.g., MySQL) for transactional guarantees, but databases often cannot handle the QPS required by large‑scale traffic, leading to performance bottlenecks.
Because most traffic consists of read requests and the underlying data changes infrequently, caching becomes a powerful tool to improve read performance by using faster storage (Redis) or local memory, effectively trading space for time.
Cache Consistency Challenges
When data is stored both in MySQL and Redis, any delay in synchronizing updates creates a window of inconsistency. Since MySQL and Redis do not share a transaction, a write that succeeds in the database may not yet be reflected in the cache, resulting in stale reads.
Four Cache‑Update Strategies
1. Update DB → Update Cache : After the database update, immediately write the new value to Redis. This can cause temporary inconsistency if another thread updates the cache out of order.
2. Update DB → Update Cache → Delete Cache : Not recommended because if the cache update fails, the stale value remains.
3. Delete Cache → Update DB : Delete the key before updating the database, then let the next read repopulate the cache. This can still produce stale reads under read‑write concurrency.
4. Update DB → Delete Cache (recommended for read‑heavy workloads): Update the database first, then delete the cache key. The inconsistency window is limited to the short period between the DB commit and cache deletion.
Code Examples
Read‑through cache logic:
data = queryDataRedis(key);
if (data == null) {
data = queryDataMySQL(key); // cache miss, query DB
if (data != null) {
updateRedis(key, data); // populate cache
}
}Update‑after‑write strategy (Update DB → Delete Cache):
updateMySQL();
deleteRedis(key); // invalidate cache after DB commitAdditional Techniques
• Cache Expiration : Set a TTL (e.g., 1 minute) so that even if a cache‑invalidation fails, the stale data will be evicted soon, guaranteeing eventual consistency.
• Message Queue (MQ) for Reliable Invalidation : Publish a delete‑cache message to an MQ with at‑least‑once delivery; if Redis is temporarily unavailable, the MQ retry mechanism ensures the key is eventually removed.
• Binlog Subscription : Use tools like Canal to listen to MySQL binlog events and centrally handle cache updates, avoiding scattered cache‑maintenance code across services.
Recommendations
For read‑dominant scenarios, prefer the "Update DB → Delete Cache" strategy combined with short TTLs and optional delayed double‑delete to minimize the inconsistency window. For write‑heavy or read‑write balanced workloads, consider "Update DB → Update Cache" with reliable MQ‑based synchronization.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
