How to Keep Cache and Database Consistent: 4 Common Strategies and Their Pitfalls
This article examines the double‑write consistency problem between databases and caches like Redis, explains why naïve cache‑first or DB‑first updates can cause stale or phantom data under high concurrency, and evaluates four practical solutions with detailed trade‑offs and recommended retry mechanisms.
Introduction
Database‑cache double‑write consistency is a language‑agnostic issue that becomes critical in high‑concurrency scenarios. When a record is updated in the database but the cache is not refreshed correctly, stale data may be served to users.
Common Cache Usage Pattern
User request checks the cache first; if present, the cached value is returned.
If the cache misses, the database is queried.
When the database returns data, it is written into the cache and then returned to the user.
At first glance this flow seems safe, but it ignores the case where a database row is updated immediately after being cached.
If the database row changes after it has been cached, the cache must be updated; otherwise users may read outdated values.
Four Practical Solutions
1. Write Cache First, Then Database
In this approach the cache is updated before the database. If a network failure occurs after the cache write but before the database write, the cache contains a value that does not exist in the database, leading to a "dirty cache" problem.
Because the cache would hold a 假数据 (fake data), this method is generally unsuitable for production.
2. Write Database First, Then Cache
Here the database is updated first, followed by a cache write. This avoids the dirty‑cache scenario, but introduces new issues:
If the cache write fails, the database holds the new value while the cache still contains the old value, causing inconsistency.
In high‑concurrency environments, two concurrent writes (a and b) can interleave such that the later cache write overwrites the newer database value with an older one.
The extra cache write consumes CPU and memory resources, especially when the cached value requires expensive computation.
3. Delete Cache First, Then Write Database
Instead of updating the cache, the cache entry is deleted before the database write. However, this still suffers from race conditions: a concurrent read may repopulate the cache with stale data before the delete completes.
To mitigate this, a cache‑double‑delete strategy is used: delete the cache before the DB write and delete it again after a short delay (e.g., 500 ms) to ensure any stale repopulation is removed.
4. Write Database First, Then Delete Cache
This method deletes the cache after the database update. It reduces the window where stale data can be read, but if the delete fails, inconsistency remains.
When a delete fails, a retry mechanism is required. Common retry tactics include:
Immediate synchronous retries (e.g., three attempts).
Asynchronous retries using a dedicated thread.
Submitting a retry task to a thread pool (risk of loss on server restart).
Recording the retry in a database table and using a scheduled job (e.g., Elastic‑Job) to retry.
Publishing a message to a message queue for later processing.
Retry Mechanisms for Cache Deletion Failures
If the cache deletion fails after a successful DB write, the system should attempt up to three synchronous retries. If all attempts fail, the operation is recorded for later handling.
For high‑throughput services, asynchronous retries are preferred. Options include:
Creating a dedicated retry thread (may cause OOM under heavy load).
Using a thread pool (risk of loss on restart).
Storing retry data in a table and processing it with a scheduled job.
Sending a retry message to a message queue.
Scheduled Task Retry
A scheduled task can periodically read the retry table, attempt to delete the cache up to five times, and mark the record as failed after exhausting retries. Elastic‑Job is recommended for its sharding capabilities and flexible interval settings.
Message Queue (MQ) Retry
When a cache delete fails, a message can be sent to an MQ (e.g., RocketMQ). The consumer retries the delete up to five times; if still unsuccessful, the message is moved to a dead‑letter queue for manual intervention. MQ‑based retries provide higher real‑time performance than scheduled tasks.
Binlog‑Based Cache Invalidation
Instead of handling cache invalidation in the application code, a binlog subscriber (e.g., Canal) can listen to MySQL binlog events. After the DB write, the subscriber deletes the corresponding cache entry. If the delete fails, the subscriber can publish an MQ message for automatic retries, combining the simplicity of binlog‑driven invalidation with robust retry handling.
Overall, the "write database then delete cache" strategy, complemented by a reliable retry mechanism (scheduled task, MQ, or binlog + MQ), offers the lowest probability of cache‑database inconsistency among the discussed approaches.
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.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
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.
