如何实现Mysql和Redis数据一致性

本文讨论了在开发中如何处理缓存和数据库双写时的数据一致性问题,包括Redis有数据和无数据两种情况下的解决方案,以及先更新数据库后更新缓存、先更新缓存后更新数据库、先删除缓存再更新数据库等策略的优缺点及解决方法。还提到了canal在监控MySQL更新日志同步到Redis的应用。
1、简介

        在开发中,我们只要用到缓存就会涉及缓存和数据库双写,保持数据的一致性,如何解决数据的一致性,以及数据库和缓存如何更新保证数据一致性成为大家主要的问题,本文将从这两个方面分别介绍相应的解决方案。

2、缓存双写一致性

        对于缓存双写一致性来说,分为两种情况:1)Redis有数据;2)、Redis无数据。对于Redis有数据来说就是确认缓存中的数据和数据库一致;对于Redis无数据来说,我们在查询到Mysql中的数据后可以回写Redis,保证数据最新。

        对于第二种情况,我们可以采用双检加锁策略实现,如下:

public static final String RREDIS_KEY = "user";
public User queryUserById(Integer id)
    {
        User user = null;
        String key = RREDIS_KEY + id;
        //1、先从redis里面查询,如果有直接返回结果,如果没有再去查询mysql,
        // 第1次查询redis,加锁前
        user = (User) redisTemplate.opsForValue().get(key);
        if(user == null) {
  //2、对于高QPS情况,进来就先加锁,保证一个请求操作,让其他的等待一下,避免击穿mysql
            synchronized (UserService.class){
                //第2次查询redis,加锁后
                user = (User) redisTemplate.opsForValue().get(key);
                //3、二次查redis还是null,可以去查mysql了(mysql默认有数据)
                if (user == null) {
                    //4、查询mysql拿数据(mysql默认有数据)
                    user = userMapper.selectByPrimaryKey(id);
                    if (user == null) {
                        return null;
                    }else{
                //5、mysql里面有数据的,需要回写redis,完成数据一致性的同步工作
                      redisTemplate.opsForValue().setIfAbsent(key,user,7L,TimeUnit.DAYS);
                    }
                }
            }
        }
        return user;
    }

}
3、数据库和缓存更新策略

        数据库和缓存更新策略主要有四点:1)、先更新数据库,再更新缓存;2)、先更新缓存,再更新数据库;3)、先删除缓存,再更新数据库;4)、先更新数据库,再删除缓存。

3.1、先更新数据库,再更新缓存

        对于先更新数据库,再更新缓存,会出现两种异常情况:

1)、出现Mysql更新成功,Redis中更新失败,导致数据不一致,Redis里面出现脏数据;

2)、在多线程情况下,会导致不同线程更新不同步,出现错乱导致数据不一致。(不推荐使用)

3.2、先更新缓存,再更新数据库

        对于先更新缓存,再更新数据库,会出现两种异常情况:

1)、出现Redis更新成功,Mysql更新失败,导致数据不一致;

2)、在多线程情况下,会导致不同线程更新不同步,出现错乱导致数据不一致。(不推荐使用)

3.3、先删除缓存,再更新数据库

        对于先删除缓存,再更新数据库,会出现以下异常情况:

1)、在多线程情况下,线程1将Redis中缓存删除了,Mysql还没有更新,线程2读取不到缓存,就到数据库中读取旧值,并将旧值写回Redis,导致缓存是脏数据,Mysql中是最新的;

解决方式:延时双删策略

核心思想:让更新线程更新的时候进行休眠,让其他线程读取数据库中的旧值,更新到缓存中,当更新线程休眠结束,将缓存删除,就可以保证缓存中和数据库中数据一致。

具体实现如下:

public void update(User user){
    Jedis jedis = RedisUtils.getJedis();
    try{
        // 更新之前删除
        jedis.del(key);
        userDao.update(user);
        try{
            TimeUnit.SECONDS.sleep(2);
        }catch (Exception ex){
            ex.printStackTrace();
        }
        // 更新之后删除,这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。
        jedis.del(key);
    }catch (Exception ex){
        ex.printStackTrace();
    }
}

注:这种实现方式有个小缺陷,就是需要更新线程睡会,如何保证吞吐量,通过异步线程的方式,将缓存中的脏数据删除。 

3.4、先更新数据库,再删除缓存

         对于先更新数据库,再删除缓存,会出现以下异常情况: 

1)、当数据库更新成功,缓存删除失败,会导致其他线程读取到的缓存还是旧值,缓存和Mysql中的数据不一致。

解决方案:保证最终一致性

在Mysql更新完数据后,将尝试删除缓存,如果删除失败,将更新的数据写入mq中,进行异步重试操作,保证Redis中的数据和Mysql中的最终一致。

以上各种方式都不能够保证Mysql和Redis的实时一致性,可以使用canal进行监控Mysql更新日志,将更新操作同步到Redis,具体见博文:使用canal实现数据库(MySQL)和缓存(Redis)数据一致_canal 缓存一致性实现-优快云博客

4、总结

        在工作中我们经常遇到使用Redis缓存来保证高并发情况下的高吞吐量,因此就会遇到两者库中数据一致性问题,本文详细介绍了在各种情况下如何进行操作保证数据一致性。

        本人是一个从小白自学计算机技术,对运维、后端、各种中间件技术、大数据等有一定的学习心得,想获取自学总结资料(pdf版本)或者希望共同学习,关注微信公众号:it自学社团。后台回复相应技术名称/技术点即可获得。(本人学习宗旨:学会了就要免费分享)

编号 文件名称 cwts-specs-001 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:名语术语 cwts-specs-002 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:概述 cwts-specs-003 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:物理信道和传输信道到物理信道的映射 cwts-specs-004 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:信道编码与复用 cwts-specs-005 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:扩频与调制 cwts-specs-006 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:物理层过程 cwts-specs-007 IMT-DS FDD(WCDMA)系统无线接口物理层技术规范:物理层测量 cwts-specs-008 IMT-DS FDD(WCDMA)系统无线接口层2技术规范:物理层向上层提供的服务 cwts-specs-009 IMT-DS FDD(WCDMA)系统无线接口层2技术规范:MAC协议 cwts-specs-010 IMT-DS FDD(WCDMA)系统无线接口层2技术规范:RLC协议 cwts-specs-011 IMT-DS FDD(WCDMA)系统无线接口层2技术规范:PDCP协议 cwts-specs-012 IMT-DS FDD(WCDMA)系统无线接口层2技术规范:BMC协议 cwts-specs-013 IMT-DS FDD(WCDMA)系统无线接口层3技术规范:RRC协议 cwts-specs-014 IMT-DS FDD(WCDMA)系统Iu接口技术规范:概述 cwts-specs-015 IMT-DS FDD(WCDMA)系统Iu接口技术规范:层1技术要求 cwts-specs-016 IMT-DS FDD(WCDMA)系统Iu接口技术规范:信令传输 cwts-specs-017 IMT-DS FDD(WCDMA)系统Iu接口技术规范:RANAP信令 cwts-specs-018 IMT-DS FDD(WCDMA)系统Iu接口技术规范:数据传输和传输信令 cwts-specs-019 IMT-DS FDD(WCDMA)系统Iu接口技术规范:用户平面协议 cwts-specs-020 IMT-DS FDD(WCDMA)系统Iub接口技术规范:概述 cwts-specs-021 IMT-DS FDD(WCDMA)系统Iub接口技术规范:层1技术要求 cwts-specs-022 IMT-DS FDD(WCDMA)系统Iub接口技术规范:信令传输 cwts-specs-023 IMT-DS FDD(WCDMA)系统Iub接口技术规范:NBAP信令 cwts-specs-024 IMT-DS FDD(WCDMA)系统Iub接口技术规范:用于CCH数据流的数据传输和传输信令 cwts-specs-025 IMT-DS FDD(WCDMA)系统Iub接口技术规范:用于CCH数据流的用户平面协议 cwts-specs-026 IMT-DS FDD(WCDMA)系统Iur接口技术规范:概述 cwts-specs-027 IMT-DS FDD(WCDMA)系统Iur接口技术规范:层1技术要求 cwts-specs-028 IMT-DS FDD(WCDMA)系统Iur接口技术规范:信令传输 cwts-specs-029 IMT-DS FDD(WCDMA)系统Iur接口技术规范:RNSAP信令 cwts-specs-030 IMT-DS FDD(WCDMA)系统Iur接口技术规范:用于CCH数据流的数据传输和传输信令 cwts-specs-031 IMT-DS FDD(WCDMA)系统Iur接口技术规范:用于CCH数据流的用户平面协议 cwts-specs-032 IMT-DS FDD(WCDMA)系统Iub/Iur接口技术规范:用于DCH数据流的数据传输和传输信令 cwts-specs-033 IMT-DS FDD(WCDMA)系统Iub/Iur接口技术规范:用于DCH数据流的用户平面协议 cwts-specs-034 TD-SCDMA系统无线接口物理层技术规范 cwts-specs-035 TD-SCDMA系统无线接口层2技术规范 cwts-specs-036 TD-SCDMA系统无线接口层3-RRC技术规范 cwts-specs-037 TD-SCDMA系统Iu接口技术规范 cwts-specs-038 TD-SCDMA系统Iub接口技术规范 cwts-specs-039 TD-SCDMA系统Iur接口技术规范 cwts-specs-040 TD-SCDMA系统基站设备无线收发特性技术规范 cwts-specs-041 TD-SCDMA系统用户终端设备无线收发特性技术规范 CWTS发布的研究报告列表 cwts-reports-001 IMT-DS FDD(WCDMA)系统连接模式下的层间过程(25.303)标准研究报告 cwts-reports-002 IMT-DS FDD(WCDMA)系统空闲模式下UE的流程和连接模式下小区重选流程(25.304)标准研究报告 cwts-reports-003 IMT-DS FDD(WCDMA)系统无线资源管理RRM研究报告 cwts-reports-004 IMT-DS FDD(WCDMA)系统无线资源管理RRM研究报告 cwts-reports-005 IMT-DS FDD(WCDMA)系统UE无线接入能力研究报告
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知其_所以然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值