redis和mysql保证数据一致性

--

redis面试经典问题,如何保证redis和mysql的数据一致性

前提:在高并发的业务场景下,为了缓解关系型数据库的压力,我们通常将一部分数据缓存到redis总,让访问请求先查询redis,而并不是直接访问我们的关系型数据库,这样就可以极大的缓解数据库的压力。伴随着高并发业务场景,两个库必然带来了一个老问题,就是如何保证数据一致性。或者根据CAP理论,这样说更合理,如何尽可能的保证数据一致性。
一说来说redis和mysql保证数据一致性有这么几种做法

  1. 先更新缓存在更新数据库
  2. 先更新数据库在更新缓存
  3. 延时双删(同步&异步)
  4. 基于中间件对数据库日志进行串行化处理

先更新缓存在更新数据库

redis和mysql保证数据一致性插图

正常我们的理想的业务流程应该是

  1. 线程A修改缓存数据,再修改mysql数据
  2. 线程B修改缓存数据,再修改mysql数据
    然而在现实的分布式的高并发环境下,很可能会发生一些极端的情况
  3. 线程A执行修改缓存数据,成功后,线程A在执行后续业务的时候出现了卡顿,在此时
  4. 线程B执行了修噶缓存数据,成功后,并迅速修改了mysql的数据
  5. 线程B执行完毕后,线程A缓过来了。并成功修改mysql的数据,
  6. 此时造成的后果就是缓存存储的是线程B的数据,mysql存储的是线程A执行的数据

先更新数据库在更新缓存

redis和mysql保证数据一致性插图1
这里的逻辑和先更新缓存在更新数据库大致,不在赘述

延时双删(同步&异步)

普通的双删,这里不做介绍了。直接看延时双删了。
延时双删的逻辑大致如下,在程序执行数据库更新的前后都进行删除缓存操作

public void update(String key,Object data){
    redis.delKey(key);
    db.updateData(data);
    Thread.sleep(x秒);
    redis.delKey(key);
}

它主要想既觉得问题是 更新数据库后,未更新缓存时候,其他线程会从缓存拿到旧的数据
他主要想解决下面这种业务场景

  1. 线程A执行del缓存操作
  2. 线程B查询缓存,没有,只能查询数据库
  3. 线程B查询结果后,更新缓存
  4. 线程A执行update数据库操作
  5. 线程A休眠
  6. 线程A执行del缓存操作,下次重新查询的时候,再重新缓存一边

看上去是没啥问题。但是再想一下,如果在5,6两步之间再来一个线程C 查询缓存,直接可以查到线程B缓存的缓存了。而此时的mysql数据已经被线程A给修改了
有人说那既然这样,我不要第4不,直接第4步到底6不不就好了。这样又带来一个问题,就是如果我们的第3步,线程B缓存延迟了一点。拖到底6步后面,不就是线程B缓存了一个老的数据了吗。
这里的第5步延时,主要就是为了方式这个情况,当然只能说是尽量避免这个情况。因为这个值不能设的过大,也不能过小,

异步双删其实就是把,5,6两步放在一个单独的线程去执行,这样在update方法里面就不会有睡眠这个操作,毕竟我们对系统的响应时间是有要求的,不能随便一个方法就睡眠一段时间。
主要思路就是在第4步之后,发出一个删除缓存指令,可以放在mq或者那个缓存容器中,保证另一个执行删除的线程能够串行化执行删除操作。当然这样的写法也是有缺陷的,就是对业务代码造成了很大的侵入性。

如此看来延时双删也并不能很好的解决数据一致性问题。

基于canal的串行化处理

这个方法是基于中间件,项目更新数据库操作前后不做缓存的任何操作,而是通过中间件监听mysql的binlog,然后修改缓存
canal是阿里巴巴开源的一个基于mysql的binlog的消费订阅框架
主要工作原理是:

  • canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议
  • MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
  • canal 解析 binary log 对象(原始为 byte 流)
    可以通过客户端去连接canal服务,执行操作,canal也可以投递消息到消息队列中去。具体可以看自己的业务需要
    关于canal的简单部署与测试我在另一篇文章里面有介绍
    canal的部署测试

当然如果高并发的情况下,产生大量的缓存数据库更新操作,用这个方法还是会有一定的延时。这也是分布式架构决定的,正如CAP无法一次性满足一样。
还是开篇的
撇开业务场景谈架构都是耍流氓,每个方式都有可取之处,也有试用场景,并不见得哪一种就是行业规范。

3 对 “redis和mysql保证数据一致性”的想法;

发表评论

邮箱地址不会被公开。