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. 无论选择哪种方案,都需要考虑异常处理和重试机制,以提高系统的可靠性。

在实际应用中,可

相关推荐
MonkeyKing_sunyuhua17 分钟前
ubuntu22.04 docker-compose安装postgresql数据库
数据库·docker·postgresql
天郁青17 分钟前
数据库交互的本地项目:后台管理系统
数据库·交互
马剑威(威哥爱编程)23 分钟前
MongoDB面试专题33道解析
数据库·mongodb·面试
小光学长1 小时前
基于vue框架的的流浪宠物救助系统25128(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
数据库·vue.js·宠物
掘金-我是哪吒1 小时前
微服务mysql,redis,elasticsearch, kibana,cassandra,mongodb, kafka
redis·mysql·mongodb·elasticsearch·微服务
零炻大礼包2 小时前
【SQL server】数据库远程连接配置
数据库
zmgst2 小时前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
令狐少侠20112 小时前
explain执行计划分析 ref_
mysql
随心............2 小时前
python操作MySQL以及SQL综合案例
数据库·mysql
€☞扫地僧☜€2 小时前
docker 拉取MySQL8.0镜像以及安装
运维·数据库·docker·容器