Redis和MySQL数据一致性解决方案(动态KEY)

2019-04-10  本文已影响0人  醒了_2440

背景

我们经常使用redis作为缓存,以减轻直接访问数据度带来的并发压力。基本操作如下:
查询

请求进来,先查询redis,如果有值则直接返回;如果没有则查询数据库(不考虑并发情景)并更新redis,再返回结果

更新

先删除redis原数据,成功后再更新数据库,然后再写入redis。

在更新redis时,就有可能带来数据库和redis数据不一致的问题:

如果在删除redis后,立刻有新的请求进来(很有可能,本来redis就在高并发下使用),则"删除失效"。因为当前数据库事务未提交,查询数据库依旧能读到旧值(数据库事务隔离要保证无脏读,如RC, RR或串行化),redis就被写入旧值。因此可直接考虑在更新redis前,redis还保留旧值的情况

上面这种redis写入异常,就会带来"数据不一致"的问题

思考方向

有一种解决数据一致性的方案是使用补偿机制,原理大体在更新redis后,数据状态无法确定时,保留异常记录id,后续使用补偿查询的方式以获知"未知记录"的真实情况。并且在明确数据状态之前,记录都无法使用。

这种方案有时延和数据不可用的缺陷,直接导致服务中断一段时间。

其实我们不一定要让"数据一致",只要请求得到的结果是预计正确即可。设想一下,在发生写入异常的时候,不管redis更新成功还是失败,只要我们让这条redis记录得不到使用,再把数据库回退,是不是也能让请求的结果是预计正确的。

设计方案

  1. 现有一个key的结构为PREFIX+参数值,其中PREFIX为常量,如表名等能表示数据含义的字符串。另有一个静态的、初始化后的HashMap<String,String> suffixMap,其key为PREFIX,value表示数据库当前值的\color{red}{版本},用V表示。"版本"值是uiid。
  2. 更新开始(\color{red}{并发控制})
    (1)请求的KEY=PREFIX+参数值+Vi+1,其中Vi = suffixMap.get(PREFIX),Vi表示上一个版本,Vi+1表示的是即将生效版本。
    (2)先update数据库,如果成功,则利用RedisTemplate.opsForValue().set(KEY,记录值)。如果更新数据库失败,程序退出。其中写入redis会出现以下a,b两种情况:
    a.写入异常

评价

暂未进行实际测试,待完善!

上一篇下一篇

猜你喜欢

热点阅读