缓存\DB数据一致性

数据库、缓存数据一致性的问题一直是生产中一直存在的,有一些程序直接忽略这个问题,有些自己编写程序去处理。但是还是存在一下问题:

  • 先更新数据库,更新缓存
  • 先删除缓存,在更新数据库
  • 先更新数据库,在删除缓存

一、中间件Canal

其实在工作中不论遇到什么问题,尽量要想到是否有成熟的中间件可以处理,这要比自己造轮子强很多,毕竟经过大部分人验证没问题的中间件是很香的。

1、canal介绍

canal是阿里开发并开源的用于Mysql增量日志数据的订阅、消费和解析的。是要原理是模拟一个Mysql的备机去监听binlog,当Mysql数据发生变化的时候消息回同步给canal组件。可以做:

  • 数据库的镜像
  • 数据库的实时备份
  • 索引构建和实时为何(拆分异构索引、倒排索引等)
  • 业务cache刷新
  • 带业务逻辑的增量数据处理

官网:https://github.com/alibaba/canalhttps://github.com/alibaba/canal

2、canal工作原理

mysql的主从复制步骤:

  1. 当master数据发生改变,会写入二进制文件
  2. salve会在一定时间内对master上的二进制文件进行检测,如果发生改变则开启一个线程请求master的二进制事件日志
  3. master为每个请求线程启动一个dump线程,向其发送二进制事件日志
  4. slave将接受到的二机制事件日志保存到本地中继日志文件中,并启动线程读取在本地重放,保证和master数据一直
  5. 各个线程都会进入休眠状态,等待下次同步

canal模拟的就是Mysql slave的交互协议,把自己伪装成一个slave,向master发送dump协议,master将binlog推送给canal。

缓存\DB数据一致性_第1张图片

3、canal的安装 

1、配置mysql

  • 当前的 canal 支持源端 MySQL 版本包括 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x。

  • 查看mysql是否开启binlog,因为canal就是监听binlog的,所以必须开启

    • log-bin=mysql-bin #开启 binlog

    • binlog-format=ROW #选择 ROW 模式

      • ROW:记录每个字段的变化,空间占用多

      • STATEMENT:只记录执行的sql语句

      • MIX模式:表结构改变使用STATEMENT模式,行数据改变使用ROW

    • server_id=1    #配置MySQL replaction需要定义,不要和canal的 slaveId重复

  • 创建canal授权账号

2、配置canal服务端

在官网上下载相关工具:

  • canal.admin-1.1.5.tar.gz:用于canal软件的管理,配置Server和Instance
  • canal.deployer-1.1.5.tar.gz:用于mysql数据同步
  • canal.adapter-1.1.5.tar.gz:canal.deployer的增强版,更多的数据源

这里感谢 Canal高可用架构部署 - 简书,写的很全面

3、canal客户端编写

二、缓存双写一致性讨论

1、一致性的理解

  • 缓存中有数据:和DB值相同
  • 缓存中无数据:DB中值要最新的

2、缓存分类

  • 只读缓存
  • 读写缓存:想要保证缓存和数据库一致,需要采用同步直写策略
    • 同步直写策略:写缓存的时候同时写数据库(或者写数据库的时候同时写缓存),缓存和数据库中的数据一致

缓存和数据库的数据同步和异步,需要根据数据的类型,热点数据、实时数据都不需要同步写的,当数据的实时性没有那么高的,就可以异步。但是如果当同步直写失败的时候,就需要使用异步写入作为补偿方案。

3、数据库和缓存一致性的集中更新策略

不论什么方式,最终目的就是期望缓存和数据库数据一致性。

  • 先更新数据库,更新缓存
    • 可能存在数据不一致性,例如:数据库更新完成,缓存更新失败,最大的问题是可能读取到旧数据
  • 先删除缓存,在更新数据库(推荐1)
    • 缺点
      • 缓存击穿
      • mysql还没更新完毕,另外的线程读取到了mysql的旧数据,把旧值写回缓存
      • 数据是主从同步,master没写完,读取线程去slave读取
    • 解决办法
      • 延时双删策略:更新线程操作流程 删除->更新数据库,修改为删除->更新数据库->休眠->删除,这样就保证了在更新过程中读取线程误写入旧数据问题(但是还是在一段时间内读到旧数据)也就是最终保证写线程能把在修改期间读线程写的脏数据删除。
        • 休眠时间的确定:需要根据具体业务场景进行确定,其实就是修改过程中会存在多少读取的进程,这些进程会用多长时间更新了旧数据
        • 吞吐量降低了:可以开启另外一个线程处理第二次删除
  • 先更新数据库,在删除缓存(推荐2)
    • 缺点
      • 缓存删除失败或者来不及删除,就会导致读取线程读取到缓存中的旧值
    • 解决办法:充实机制+MQ缓存\DB数据一致性_第2张图片

方案2和方案3,在业务场景怎么选,其实只要符合当前业务场景,哪个都可以,甚至方案1也是可以的,但是对比而言,更推荐方案3,方案3首先是避免了缓存击穿的问题,同时避免了延时双删需要确定休眠时间的问题,何乐而不为呢 。

方案2和方案3对比:

缓存\DB数据一致性_第3张图片

你可能感兴趣的:(redis,redis)