Cache and database always inconsistent: update database first or delete cache first? Is delayed double deletion really reliable?
When cache and database are inconsistent, the default order in the Cache Aside pattern is to update the database first and, only after it succeeds, delete the cache. In 2026 project deliveries, a more robust approach is to add delayed double deletion and TTL as a fallback. Deleting the cache first makes concurrent reads more likely to refill old values, widening the inconsistency window; updating the database first concentrates the problem into “whether the cache deletion succeeds,” which can then be retried and observed.
Why is it better to update the database before deleting the cache, rather than delete the cache first?
“Delete the cache first, then update the database” creates a cache gap: any request arriving during this period finds a cache miss and falls back to the database. If the database transaction has not committed yet, it reads the old value, which is then written back into the cache. By the time the transaction commits, the cache may already hold stale data. As long as this key remains hot, the old value is continuously refreshed, and the dirty data window typically ranges from minutes to hours. In production troubleshooting, clearing the cache is often used as an emergency measure.
Conversely, updating the database before deleting the cache limits the inconsistency window to the short period between the database commit and the cache deletion. Even if the deletion fails, the problem is centralized: the cache naturally expires when the TTL elapses, or a compensation task can fill in. Compared with “old value prefilled earlier,” this order carries much lower systemic risk.
- Common pitfall: delete the cache only once without TTL, and log nothing on deletion failure, finally discovering the problem only through user complaints.
- Acceptance baseline: set an expiration time for every cached key; log, count, and trigger retry or alert for every deletion failure.
Delayed double deletion + TTL: implementation details and experience range
Even with update-first-then-delete-cache, a short window remains. If the business can tolerate eventual consistency, projects commonly use “delayed double deletion + TTL” to cover it:
- Delete the target cache once before updating the database.
- Update the database in a transaction and commit successfully.
- Wait for a short delay (experience range 200–500 ms), then delete the cache again.
- Set an expiration time for the target key (typical range 300–900 seconds) as the final fallback for missing deletions.
The delay must be longer than the time needed for a refill, i.e., the time taken by “one database read + one cache write.” In ordinary projects, start at 200 ms; if there are slow queries on the database, move toward 500 ms. If set too short, the second deletion happens before the old value can be refilled, making it a no-op. If set too long, the inconsistency window is widened. In one delivery, we handled a backend configuration sync: the config key was read heavily but updated rarely, and the team did not have a ready Binlog consumer pipeline. Our initial approach was “delete the cache first, then update the database,” but the new rule did not take effect for nearly a day after the upgrade — only user feedback exposed the problem. We then switched to “update the database first, delayed double deletion at 400 ms (within the typical 200–500 ms range), with a uniform TTL of 300 seconds.” After running concurrent read/write simulations in the test environment, we brought the dirty-data window down from hours to seconds. The cost was one extra release to clear historical cache and one new alert for deletion failures, but we gained a much more controllable consistency window.
If deletion steps frequently fail, or if the synchronous flow cannot afford a waiting period, you can add asynchronous compensation on top of delayed double deletion. The comparison below is based on experience ranges from real projects — use it as a reference for business decisions, as the actual cost varies by team and technical stack:
- Plan A: Synchronous deletion + retry on failure log — Minimal change, good performance; but when deletion fails, the inconsistent period is longer. Development effort is generally less than 1 person-day.
- Plan B: Delayed double deletion + TTL — Covers the common “concurrent refill of stale values” scenario, but you need to control one more delay parameter in operations. Development effort is about 1–2 person-days.
- Plan C: Local message table or Binlog subscription for supplementary deletion — Takes the deletion action out of the request path, enables automatic retry on failure, and gives more stable consistency. Local message table: about 2–5 person-days. Binlog subscription: about 4–8 person-days if no existing component is available, and requires maintaining data-sync components.
Applicable and non-applicable boundaries
This approach suits read-heavy, write-light scenarios that allow eventual consistency, such as product details, configuration tables, and aggregated list pages. Briefly stale data does not affect critical decisions, and once the TTL expires, the cache can be rebuilt from the database.
- Applicable: cache read hit rate is high and writes are relatively few; database transactions ensure successful commit or rollback; cache deletion failures are logged and have a retry path.
- Not applicable: states that require strong consistency, such as account balance, orders, and inventory. These should be governed by database transactions and locks; never use the cache as the basis for business decisions. Delayed double deletion adds little value and increases complexity in such scenarios.
- Not necessary: when concurrent read is low and each database query takes only a few milliseconds, optimizing slow SQL, adding indexes, or consolidating requests is more effective than introducing a cache consistency framework.
In addition, deleting a cache key creates a gap window for hot keys, and a sudden surge of requests can hit the database directly — that is a cache stampede. Sequence optimization solves cache–database consistency, not stampedes. Stampede protection requires mutex fallback, hot-key preheating, or rate limiting. Do not rely on altering the deletion order to eliminate it.
How to verify whether a consistency solution is acceptable
A consistency solution is not complete once it “works in production.” Clearly define acceptance checks:
- After a successful write, the cache is eventually deleted or overwritten; deletion failures have logs, counters, and automatic retry.
- When the database update fails, the cache stays as-is, and no pointless deletion occurs.
- In the test environment, deliberately shut down the cache middleware for a few seconds and then restore it to confirm the write path can retry and compensate.
- TTL acts as a fallback within the expected error margin. A TTL too short causes frequent fallback reads; too long extends inconsistency. The typical range is 300–900 seconds.
In 2026 project-delivery practice, making “every cache deletion failure visible in monitoring” a release criterion is closer to the acceptance baseline than relying on ad-hoc cache clearing.
Frequently Asked Questions
What delay should be set for delayed double deletion?
The typical experience range is 200–500 ms. It should exceed the time for “one database read + one cache write.” If slow queries are common, lean toward the upper side; do not blindly copy a fixed value found online.
What if cache deletion fails?
Log it and trigger a retry or alert, and if necessary, use a local message table or Binlog subscription for compensation. Logging without retrying is the same as leaving consistency to the luck of TTL.
Can a cache stampede be solved by changing the deletion order?
No. The deletion order only addresses eventual consistency between cache and database. Stampedes must be handled with mutex fallback, distributed locks, hot-key preheating, and fallback concurrency control.
Can strongly consistent data like inventory or balance be cached?
It is not advisable to put core state directly into a cache, and never use the cache as the source for deductions or payments. Database transactions and locks are the reliable line of defense. Caches are only for read acceleration.
If you decide to add a cache, first clarify the inconsistency window your business can tolerate, then choose “update database first + delayed double deletion + TTL fallback”; for strong-consistency scenarios, just query the database and avoid adding a cache tier that only increases your burden.
-
Upload folders ship with code and images are lost—should files be stored locally or in object storage?
Date: Sep 13, 2026 Read: 2
-
Saved Just Now but the Detail Page Still Shows the Old Record — Does Read-Write Splitting Mean Every Read Has to Go to the Primary?
Date: Sep 12, 2026 Read: 6
-
Scheduled Jobs Run Fine on One Machine but Duplicate on Multiple Servers — Where Should You Stop Them?
Date: Sep 11, 2026 Read: 11
-
Auto-increment primary keys are convenient when a table first goes live — how much trouble is it to change them on the day you actually shard?
Date: Sep 10, 2026 Read: 15
-
Why did APIs get slower after increasing the database connection pool, and what is the appropriate connection count?
Date: Sep 9, 2026 Read: 18




