当前位置: 首页 > news >正文

MySQL数据同步到 Redis 缓存的几种方法

1 Mysql查完数据,再同步写入到Redis中

  1. 缺点1:会对接口造成延迟,因为同步写入redis本身就有延迟,并且还要做重试,如果redis写入失败,还需要重试,那就更费时间了。

  1. 缺点2:不解耦,如果redis崩了,那直接卡线程了

  1. 缺点3:如果人为该数据库,那就没法同步了, 除非再人为删除对应的Redis,但删除Redis这个过程也有个时间差

2 Mysql查完数据,通过发送MQ,在消费者线程去同步Redis

  1. 缺点1:多了层MQ,也就是会有很大的概率导致同步延迟问题.

  1. 缺点2:要对MQ的可用性做预防

  1. 缺点3:如果人为该数据库,那就没法同步了

  1. 优点1:可以大幅减少接口的延迟返回的问题

  1. 优点2:MQ本身有重试机制,无需人工去写重试代码

  1. 优点3:解耦,把查询Mysql和同步Redis完全分离,互不干扰

3 订阅Mysql的Binlog文件(可借助Canal来进行)

  1. CanalServer会伪装成MysqlServer从库,去订阅MysqlServer主库的Binlog文件

  1. Canal启动的时候会配置对应的消息MQ(RabbitMQ, RocketMQ, Kafka), 监听到Binlog文件有变化是,会把变化的sql语句转换成json格式,并作为消息内容发送到MQ中

  1. 项目中只要监听对应MQ,就能拿到Binlog改动的内容,Json数据中有明确的操作类型(CURD), 以及对应的数据。把对应数据同步到redis即可

  1. 缺点1:canal订阅Binlog的整个操作过程是单线程的,所以面临超高并发的情况下,性能可能不太出色。当然可以部署多个Canal 与 多个消费者,但是要注意消息重复消费问题,做好幂等性校验

  1. 优点1:即使人为改数据库,也会监听到,并且也会同步

  1. 优点2:异步同步,不会对接口返回有格外延迟

4 延迟双删

  1. 在执行修改sql之前,先将redis的数据删除

  1. 执行更新sql

  1. 延迟一段时间

  1. 再次删除redis的数据

// 延迟双删伪代码
deleteRedisCache(key);   // 删除redis缓存
updateMysqlSql(obj);        // 更新mysql
Thread.sleep(100);           // 延迟一段时间
deleteRedisCache(key);   // 再次删除该key的缓存

缺点:这个延迟时间不好把控,到底延迟多久,这个很难去评估

扩展: 如果不使用延迟双删,仅仅是delete缓存,然后改mysql数据。只有这两步会出现什么问题呢?

5. 单个请求,单线程没问题,高并发多线程下会出问题

6. 如果Thread1线程要更新数据,此时Thread1线程把redis清理了

7. 此时Thread2线程来了,但Thread1还没有更新mysql完毕

8. Thread2查询redis肯定是null,此时Thread2就要查mysql了,然后再把查到的数据写到缓存

9. 由于Thread1还没来得及修改mysql数据,所以此时Thread2查出来的数据是【旧数据】,Thread2把旧数据又写入Redis 了

10. 此时Thread3线程来了,查询Redis发现有数据,则直接拿缓存数据了,此时【Thread3查出来的是旧数据】,直接带着旧数据返回了,这就是问题所在

11. 而延迟双删的第二次删除作用就是防止Thread2把旧数据又写入了,有了延迟双删,Thread3查询Redis的时候还是null,就会从mysql 去拿最新数据了

12. 所以正常的这个延迟时间,应该是Thread2查缓存到拿mysql数据,到再保存到redis这整个时间,作为Thread1的延迟时间,但是这个Thread2这个过程的时间会受到很多因素影响,因此很难断定究竟会是多久

5 延迟双写

// 延迟双写伪代码
updateMysqlSql(obj);        // 更新mysql
addRedis(key);   // 再次删除该key的缓存

上述代码缺陷;

  1. 高并发下,两条线程同时执行上面代码,并对mysql 修改,且修改内容不通,可能会导致Redis与Mysql数据不一致

  1. T1线程执行完updateMysqlSql,释放了行锁,此时T2线程再执行updateMysqlSql 与 addRedis, 最后T1执行addRedis,这种情况会导致数据库改成了T2线程的数据,但Redis却是T1线程的数据

优化
// 完美延迟双写伪代码
开启事务
updateMysqlSql(obj);        // 更新mysql
addRedis(key);   // 再次删除该key的缓存
提交事务

上述代码改正:

  1. 把两句代码放到一个事务里面,只有T1执行完Mysql 与 Redis的时候,T2才能开始执行,就可以保证数据一致性。推荐使用分布式锁

  1. 双写缺点:Mysql 与 Redis是单线程的。性能方面不行,因此不推荐使用

6 总结

推荐使用Canal的方式,进行异步同步。其次是MQ方式

相关文章:

  • 一个nginx的小项目,不写代码,实现在局域网内访问其他电脑的网页
  • 基于Reactor模式下的epoll多路复用服务器
  • 为什么软件测试面试了几个月都没有offer,从HR角度分析
  • IO多路复用--[select | poll | epoll | Reactor]
  • java面试八股文之------Java并发夺命23问
  • 23.3.14打卡 2022年江西省大学生程序设计竞赛(正式赛)ABL
  • FPGA实现CSI-2 解码MIPI视频 2line 720P分辨率 OV5647采集 提供工程源码和技术支持
  • 改进YOLO系列 | CVPR2023最新Backbone | FasterNet 远超 ShuffleNet、MobileNet、MobileViT 等模型
  • 蓝桥杯刷题冲刺 | 倒计时28天
  • 改进 YOLO V5 的密集行人检测算法研究(论文研读)——目标检测
  • GPT4来了,多模态模型上线
  • 【C/C++】必知必会知识点大总结
  • css 画图之质感盒子
  • C/C++网络编程笔记Socket
  • 学习 Python 之 Pygame 开发魂斗罗(十一)
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • 2017 前端面试准备 - 收藏集 - 掘金
  • HTML中设置input等文本框为不可操作
  • Intervention/image 图片处理扩展包的安装和使用
  • JS字符串转数字方法总结
  • maven工程打包jar以及java jar命令的classpath使用
  • node入门
  • scala基础语法(二)
  • Sequelize 中文文档 v4 - Getting started - 入门
  • Spring核心 Bean的高级装配
  • ViewService——一种保证客户端与服务端同步的方法
  • vue:响应原理
  • Xmanager 远程桌面 CentOS 7
  • 阿里云Kubernetes容器服务上体验Knative
  • 从setTimeout-setInterval看JS线程
  • 翻译:Hystrix - How To Use
  • 服务器之间,相同帐号,实现免密钥登录
  • 基于web的全景—— Pannellum小试
  • 模型微调
  • 如何设计一个微型分布式架构?
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 算法---两个栈实现一个队列
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 在GitHub多个账号上使用不同的SSH的配置方法
  • Nginx实现动静分离
  • puppet连载22:define用法
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​ubuntu下安装kvm虚拟机
  • ​软考-高级-系统架构设计师教程(清华第2版)【第9章 软件可靠性基础知识(P320~344)-思维导图】​
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • #WEB前端(HTML属性)
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • (16)Reactor的测试——响应式Spring的道法术器
  • (BFS)hdoj2377-Bus Pass
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (力扣记录)1448. 统计二叉树中好节点的数目
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会