Cache Consistency Strategies and Best Practices for Database‑Cache Synchronization

This article explains why caching is essential for high‑read workloads, describes the consistency challenges when data resides in both MySQL and Redis, compares four cache‑update strategies, and offers practical recommendations such as using expiration, delayed double‑delete, message queues, and binlog subscription to achieve eventual consistency.

IT Architects Alliance
IT Architects Alliance
IT Architects Alliance
Cache Consistency Strategies and Best Practices for Database‑Cache Synchronization

In real‑world business scenarios, data such as orders, members, and payments are persisted in databases for transactional guarantees, but relational databases often cannot handle very high QPS, leading to the need for caching.

Why Cache Matters

Cache replaces slower storage with faster space, improving read performance. Faster storage includes high‑performance media like Redis and local memory (L1/L2 caches), though local memory is limited and suitable only for hot keys.

Consistency Challenges After Introducing Cache

When data exists simultaneously in MySQL and Redis, any delay in synchronizing updates can cause inconsistency. The window of inconsistency is unavoidable without heavy mechanisms such as distributed transactions, which degrade performance.

Four Cache‑Update Strategies

The article examines four common approaches:

Update database then update cache

Update database then delete cache

Update cache then update database

Delete cache then update database

Each strategy is illustrated with timing diagrams and code examples.

1. Update Database → Update Cache

Typical code:

data = queryDataRedis(key);
if (data == null) {
    data = queryDataMySQL(key); // cache miss, read from MySQL
    if (data != null) {
        updateRedis(key, data); // populate cache after DB read
    }
}

In write paths, updating the cache after the DB can cause stale data if concurrent writes reorder operations, as shown by a counter example where the cache ends up with value 99 while the DB holds 98.

2. Update Database → Delete Cache

Code example:

deleteRedis(key); // invalidate cache
updateMySQL(); // then write DB

This avoids writing stale data back to the cache but can still produce inconsistency under read‑write concurrency, e.g., a read thread may repopulate the cache with old data before the DB update completes.

3. Update Cache → Update Database

Code example:

updateRedis(key, data); // write cache first
updateMySQL(); // then write DB

If the DB update fails, the cache holds incorrect data, and rolling back the cache is unreliable.

4. Delete Cache → Update Database

Code example:

deleteRedis(key); // invalidate first
updateMySQL(); // then write DB

This strategy can still suffer from read‑write races where a read thread repopulates the cache with stale data before the DB write finishes.

Recommended Strategy

For read‑heavy, write‑light workloads, updating the database first and then deleting the cache is preferred because the inconsistency window is tiny (≈1 ms) and often negligible. For read‑write balanced scenarios, updating the database then updating the cache may be acceptable if the window can be ignored.

Ensuring Eventual Consistency

Set a reasonable expiration time on cache entries (e.g., 1 minute) so that any missed synchronization will be corrected when the entry expires and the next read falls back to the DB.

Use reliable message queues (at‑least‑once delivery) to send cache‑invalidating messages; if Redis is temporarily unavailable, retries ensure eventual key deletion.

Transactional MQ (e.g., RocketMQ) or a dedicated message table can guarantee that cache‑invalidating messages are sent together with DB updates.

Handling Complex Multi‑Cache Scenarios

When a single DB record maps to multiple cache keys (e.g., user profile, leaderboard, daily stats), each key must be updated or invalidated. To avoid scattered logic, publish a single MQ event after the DB change and let a dedicated service handle all related cache operations.

Alternatively, subscribe to MySQL binlog (using tools like Canal) to detect changes centrally and update caches accordingly.

Summary Table of Strategies

The article concludes with a comparative table summarizing concurrency patterns, potential problems, and mitigation techniques such as distributed locks, delayed double‑delete, or ignoring negligible windows.

Overall, the guidance helps architects design cache layers that balance performance and consistency for large‑scale internet services.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backendcachingdistributed-systemsConsistency
IT Architects Alliance
Written by

IT Architects Alliance

Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.

0 followers
Reader feedback

How this landed with the community

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.