3.MySQL面试题之Redis 和 Mysql 如何保证数据一致性?

Redis 和 MySQL 数据一致性是分布式系统中的一个常见挑战。保证数据一致性通常涉及几种策略,我会详细解释这些策略并提供相应的代码示例。

  1. 先更新数据库,再更新缓存

这种方法先更新 MySQL,然后更新或删除 Redis 缓存。

java 复制代码
@Transactional
public void updateUser(User user) {
    // 1. 更新MySQL
    userMapper.updateUser(user);
    
    // 2. 更新Redis缓存
    // 方式1:更新缓存
    redisTemplate.opsForValue().set("user:" + user.getId(), user);
    
    // 方式2:删除缓存(推荐)
    redisTemplate.delete("user:" + user.getId());
}

优点:

  • 简单直接
  • 保证数据库有最新数据

缺点:

  • 如果更新缓存失败,会导致数据不一致
  1. 先删除缓存,再更新数据库

这种方法先删除 Redis 缓存,然后更新 MySQL。

java 复制代码
@Transactional
public void updateUser(User user) {
    // 1. 删除Redis缓存
    redisTemplate.delete("user:" + user.getId());
    
    // 2. 更新MySQL
    userMapper.updateUser(user);
}

优点:

  • 避免缓存更新失败导致的不一致

缺点:

  • 在高并发情况下可能出现数据不一致
  1. 延迟双删策略

这种方法在更新数据库前后都删除缓存,并在第二次删除时增加短暂延迟。

java 复制代码
@Transactional
public void updateUser(User user) {
    // 1. 删除Redis缓存
    redisTemplate.delete("user:" + user.getId());
    
    // 2. 更新MySQL
    userMapper.updateUser(user);
    
    // 3. 延迟一段时间后再次删除缓存
    CompletableFuture.runAsync(() -> {
        try {
            Thread.sleep(500); // 延迟500毫秒
            redisTemplate.delete("user:" + user.getId());
        } catch (InterruptedException e) {
            // 处理异常
        }
    });
}

优点:

  • 能够处理高并发场景下的数据一致性问题

缺点:

  • 实现较为复杂
  • 增加了系统延迟
  1. 使用消息队列

使用消息队列来保证数据一致性,先更新数据库,然后发送消息到队列,由消费者来更新缓存。

java 复制代码
@Transactional
public void updateUser(User user) {
    // 1. 更新MySQL
    userMapper.updateUser(user);
    
    // 2. 发送消息到消息队列
    kafkaTemplate.send("user-update-topic", JSON.toJSONString(user));
}

// 在消费者服务中
@KafkaListener(topics = "user-update-topic")
public void consumeUserUpdate(String message) {
    User user = JSON.parseObject(message, User.class);
    // 更新Redis缓存
    redisTemplate.opsForValue().set("user:" + user.getId(), user);
}

优点:

  • 解耦了数据库操作和缓存操作
  • 可以处理高并发场景

缺点:

  • 增加了系统复杂度
  • 可能引入短暂的数据不一致
  1. 使用 Canal 进行 MySQL binlog 同步

使用 Canal 监听 MySQL 的 binlog,然后更新 Redis 缓存。

java 复制代码
@Component
public class CanalClient {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @PostConstruct
    public void init() {
        CanalConnector connector = CanalConnectors.newSingleConnector(
            new InetSocketAddress("127.0.0.1", 11111), 
            "example", "", "");
        
        try {
            connector.connect();
            connector.subscribe(".*\\..*");
            
            while (true) {
                Message message = connector.getWithoutAck(100);
                long batchId = message.getId();
                List<CanalEntry.Entry> entries = message.getEntries();
                
                if (batchId != -1 && entries.size() > 0) {
                    for (CanalEntry.Entry entry : entries) {
                        if (entry.getEntryType() == CanalEntry.EntryType.ROWDATA) {
                            CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
                            
                            if (rowChange.getEventType() == CanalEntry.EventType.UPDATE) {
                                for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
                                    // 处理更新操作,更新Redis缓存
                                    updateRedisCache(rowData);
                                }
                            }
                        }
                    }
                }
                connector.ack(batchId);
            }
        } finally {
            connector.disconnect();
        }
    }

    private void updateRedisCache(CanalEntry.RowData rowData) {
        // 根据rowData更新Redis缓存
        // 这里需要根据具体的数据结构来实现
    }
}

优点:

  • 实时性高
  • 对应用层代码无侵入

缺点:

  • 配置和维护相对复杂
  • 依赖 MySQL binlog 配置

总结:

  1. 选择哪种方案取决于具体的业务需求、系统架构和性能要求。
  2. 对于读多写少的场景,可以考虑使用"先更新数据库,再删除缓存"的策略。
  3. 对于高并发场景,可以考虑使用延迟双删或消息队列的方案。
  4. 对于实时性要求高的场景,可以考虑使用 Canal 进行 binlog 同步。
  5. 无论选择哪种方案,都需要考虑异常处理和重试机制,以提高系统的可靠性。

在实际应用中,可

相关推荐
hnlucky14 分钟前
redis 数据类型新手练习系列——Hash类型
数据库·redis·学习·哈希算法
LucianaiB1 小时前
【金仓数据库征文】_AI 赋能数据库运维:金仓KES的智能化未来
运维·数据库·人工智能·金仓数据库 2025 征文·数据库平替用金仓
时序数据说1 小时前
时序数据库IoTDB在航空航天领域的解决方案
大数据·数据库·时序数据库·iotdb
.生产的驴2 小时前
SpringBoot 封装统一API返回格式对象 标准化开发 请求封装 统一格式处理
java·数据库·spring boot·后端·spring·eclipse·maven
AnsenZhu2 小时前
2025年Redis分片存储性能优化指南
数据库·redis·性能优化·分片
Johny_Zhao2 小时前
MySQL 高可用集群搭建部署
linux·人工智能·mysql·信息安全·云计算·shell·yum源·系统运维·itsm
oydcm2 小时前
MySQL数据库概述
数据库·mysql
oioihoii2 小时前
C++23中if consteval / if not consteval (P1938R3) 详解
java·数据库·c++23
带娃的IT创业者2 小时前
《AI大模型趣味实战》基于RAG向量数据库的知识库AI问答助手设计与实现
数据库·人工智能
李菠菜3 小时前
非SpringBoot环境下Jedis集群操作Redis实战指南
java·redis