Backend Development 18 min read

How to Keep Cache and Database Consistent: Strategies, Pitfalls, and Best Practices

This article explains why cache‑database consistency is a classic challenge, compares common update‑then‑delete patterns, analyzes concurrency and master‑slave delay issues, and recommends reliable solutions such as asynchronous retries with message queues or binlog subscription to achieve eventual consistency.

Sanyou's Java Diary
Sanyou's Java Diary
Sanyou's Java Diary
How to Keep Cache and Database Consistent: Strategies, Pitfalls, and Best Practices

Introducing Cache to Improve Performance

When traffic is low, direct database reads and writes are sufficient, but as request volume grows, reading from the database each time becomes a bottleneck, so a cache layer (commonly Redis) is added to accelerate reads.

Simple Full‑Load Cache Strategy

The most straightforward approach is to load all data into the cache without expiration, write only to the database, and use a scheduled task to refresh the cache.

Database data is fully flushed into the cache (no TTL).

Write requests update the database only.

A periodic job syncs the database to the cache.

Advantages: read requests hit the cache directly, yielding high performance. Drawbacks: low cache utilization (stale, rarely‑accessed data stays in cache) and data inconsistency because the cache is refreshed on a timer.

Improving Cache Utilization and Consistency

To maximize cache utilization, only hot data should be kept:

Write requests still update the database only.

Read requests first check the cache; on miss, they read from the database and rebuild the cache.

Cached entries are given an expiration time.

This ensures that infrequently accessed data expires, leaving only hot data in the cache.

Ensuring Consistency When Updating Data

When a record is modified, both the database and the cache must be updated. Two ordering options exist:

Update cache first, then the database.

Update the database first, then the cache.

Both orders suffer from a failure in the second step, leading to stale data in either the cache or the database.

Concurrency Problems

Concurrent updates can cause the cache and database to diverge regardless of the chosen order. Example scenarios show how interleaved operations result in different values stored in the cache and the database.

Delete‑Cache Approaches

Another class of solutions deletes the cache instead of updating it. Two orders are possible:

Delete the cache, then update the database.

Update the database, then delete the cache.

Both suffer from the same “second‑step‑failure” issue, so additional mechanisms are needed.

Asynchronous Retry via Message Queues

Placing the second step (cache update or deletion) into a message queue allows asynchronous retries until the operation succeeds, improving reliability without blocking the main thread.

Subscribing to Database Change Logs

Instead of writing to a queue, applications can listen to database binlogs (e.g., MySQL Binlog) using tools like Canal. When a change is detected, the corresponding cache entry is deleted, eliminating the need for a separate queue write.

Recommended Consistent Solution

Combine the “update database then delete cache” pattern with either a message‑queue‑based asynchronous retry or binlog subscription to guarantee that both steps eventually succeed.

Master‑Slave Delay and Delayed Double Delete

In read‑write split scenarios, replication lag can cause stale data to be written back into the cache. A delayed double‑delete strategy—deleting the cache once immediately and again after a short delay—helps mitigate this risk, though the optimal delay is hard to determine in high‑concurrency environments.

Can Strong Consistency Be Achieved?

True strong consistency across cache and database typically requires heavyweight protocols (2PC, Paxos, Raft) that sacrifice performance. In practice, eventual consistency with careful retry and delay strategies is the realistic goal.

Summary

Cache improves read performance but introduces consistency challenges.

Full‑load caching is simple but unsuitable for large‑scale, high‑consistency needs.

Updating both DB and cache fails under concurrency; deleting the cache is preferable.

Use asynchronous retries via message queues or binlog subscription to ensure both steps succeed.

Delay‑double‑delete can alleviate replication‑lag issues but requires careful timing.

RediscachingMessage Queuedatabase consistencyasynchronous retry
Sanyou's Java Diary
Written by

Sanyou's Java Diary

Passionate about technology, though not great at solving problems; eager to share, never tire of learning!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.