Databases 11 min read

Ensuring Consistency Between MySQL and Redis: Theory, Schemes, and Practical Implementation

This article reviews six theoretical approaches for maintaining MySQL‑Redis data consistency, evaluates their pros and cons, and presents a practical implementation using Java Spring, transactional updates, cache deletion, and asynchronous queue handling to achieve both real‑time and eventual consistency in high‑concurrency systems.

Architect
Architect
Architect
Ensuring Consistency Between MySQL and Redis: Theory, Schemes, and Practical Implementation

Theory

The article begins by recalling six common theoretical solutions for keeping MySQL and Redis data consistent, illustrated with sequence diagrams that show how concurrent requests can cause inconsistencies.

Bad Schemes

1. Write MySQL then Redis

Both requests write to MySQL first and then to Redis; under high concurrency, a delayed Redis write can cause stale data in the cache.

2. Write Redis then MySQL

Similar to the first scheme; the diagram makes the problem obvious.

3. Delete Redis then Write MySQL

If the delete operation stalls, the cache may be empty while MySQL is still being updated, leading to inconsistency.

Good Schemes

4. Delete Redis → Write MySQL → Delete Redis (Cache Double Delete)

After deleting the cache, write to MySQL, then delete the cache again. The article advises against a fixed 500 ms sleep and recommends an asynchronous, serialized delete via a message queue.

5. Write MySQL → Delete Redis

This approach tolerates a single momentary inconsistency and is suitable for scenarios without strict real‑time guarantees.

6. Write MySQL → Binlog → Asynchronous Redis Update

By listening to MySQL binlog and asynchronously updating Redis (e.g., via Kafka), eventual consistency is achieved, though real‑time reads may still see stale data.

Scheme Comparison

The six methods are compared, highlighting why the author rejects some (e.g., writing Redis first) and prefers others based on concurrency, failure tolerance, and operational complexity.

Project Practice

Data Update

In a high‑throughput project the author adopts scheme 5 (write MySQL then delete Redis). Sample Java code shows a transactional method that updates MySQL and subsequently deletes the related Redis key, rolling back on any exception.

@Override
@Transactional(rollbackFor = Exception.class)
public void saveTag(TagReq tagReq) {
    TagDO tagDO = ArticleConverter.toDO(tagReq);
    // Write MySQL first
    if (NumUtil.nullOrZero(tagReq.getTagId())) {
        tagDao.save(tagDO);
    } else {
        tagDO.setId(tagReq.getTagId());
        tagDao.updateById(tagDO);
    }
    // Then delete Redis
    String redisKey = CACHE_TAG_PRE + tagDO.getId();
    RedisClient.del(redisKey);
}

Data Retrieval

Read‑through cache logic: try Redis first, if miss then query MySQL, store the result in Redis with an expiration time.

@Override
public TagDTO getTagById(Long tagId) {
    String redisKey = CACHE_TAG_PRE + tagId;
    // Try cache
    String tagInfoStr = RedisClient.getStr(redisKey);
    if (tagInfoStr != null && !tagInfoStr.isEmpty()) {
        return JsonUtil.toObj(tagInfoStr, TagDTO.class);
    }
    // Fallback to DB and cache the result
    TagDTO tagDTO = tagDao.selectById(tagId);
    tagInfoStr = JsonUtil.toStr(tagDTO);
    RedisClient.setStrWithExpire(redisKey, tagInfoStr, CACHE_TAG_EXPRIE_TIME);
    return tagDTO;
}

Test Cases

JUnit tests demonstrate saving a tag and querying it, verifying the Redis entry after the operation.

@Test
public void save() {
    TagReq tagReq = new TagReq();
    tagReq.setTag("Java");
    tagReq.setTagId(1L);
    tagSettingService.saveTag(tagReq);
    log.info("save success:{}", tagReq);
}

@Test
public void query() {
    TagDTO tagDTO = tagSettingService.getTagById(1L);
    log.info("query tagInfo:{}", tagDTO);
}

Conclusion

For real‑time consistency the author recommends "write MySQL then delete Redis" as the optimal trade‑off, while for eventual consistency the "write MySQL → Binlog → async Redis" approach is deemed the best solution.

Postscript

The article invites readers to download the full project from GitHub and mentions future integrations (RabbitMQ, Elasticsearch, Nacos, MongoDB, Prometheus) for further exploration.

JavaBackend DevelopmentRedisSpringMySQLCache ConsistencyTransactional
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and 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.