数据库事务和redis最终一致性的解决方法

一:一致性问题及两种处理方案

事务同时操作db和redis,在高并发等场景下,可能会遇到db和redis存储数据不一致的问题,遇到这种问题应该怎么解决?根据使用场景提供了几种简单处理方案:

  • 单系统中事务处理,事务提交后延迟再删除一次redis,保证数据库数据和redis最终一致性。
  • 多活系统,A机房,B机房,A、B机房数据是底层binlog同步的,独立搭建的应用和redis集群,有业务场景可能到A、B机房访问数据,redis是不相互复制的,所以我们是把在一个机房处理的缓存,使用MQ的方式推送到各个机房,然后当前机房也监听到信息后再处理一次,保证数据库数据和redis的最终一致性。

在处理系统间数据的最终一致性,通常也是考虑使用MQ异步的处理方式,同时考虑异常重处理、告警等。

 

二、举例常用发生的场景

  1. 详细场景问题描述:一个事务,同时操作db和redis,事务提交和redis操作不是同时的,不能保证数据库数据和redis数据的强一致性,例如事务提交前,删除了缓存,然后有其他场景又查询了redis缓存,redis为空时,会从db加载,由于事务没有提交,导致查询出来的数据和事务提交后的数据不一致,导致缓存和db不一致,在某些应用场景,会严重影响用户体验产生投诉,或者影响后续功能处理。
  2. 场景对应设计:会员信息表、会员信息缓存(redis)

查询会员信息接口:用户查询会员信息,会优先查询会员信息缓存,如果查询不到,调用db查询会员信息并且加载到redis中,如果查询不到,默认给设置一个空值标识到redis,防止高并发查询透传到数据库,减轻数据库压力。

更新会员信息接口:更新会员信息,删除会员信息redis缓存。

三、场景解决方案详解

  1. 单系统redis延迟处理

 

时序图:

 

代码块:

//延迟删除缓存的时间配置(毫秒),从zookeeper获取
long delayMillis = 2000;
// 开始执行任务时间戳,计算单位:毫秒
long doTaskMillis = System.currentTimeMillis();
// 还需要延迟的时间 = zookeeper配置的延迟时间 - 任务从创建到执行的时间差(原因:该任务可能在队列中呆了一段时间, 这段时间也属于延迟时间的范围,需要减掉)
// createTaskMillis是任务创建时间
long delayMillis = delayMillis - (doTaskMillis - createTaskMillis);
try {
    // 还需要延迟的时间 > 0
    if (delayMillis > 0) {
        // 睡眠还需要延迟这么长时间,(延迟的方法需要优化)
        Thread.sleep(delayMillis);
    }

    // TODO 删除缓存操作

} catch (InterruptedException e) {
    // TODO 输出日志,用于分析问题
    // 中断该线程
    Thread.currentThread().interrupt();
} catch (Exception e) {
    // TODO 输出日志,用于分析问题
}

2、多活系统处理方案

   事务中删除当前机房缓存后,把需要删除的缓存使用json的格式插入到待推送表,异步job推送需要删除的缓存信息到MQ,各机房再监听MQ消息,再删除缓存,保证数据库和缓存最终一致性

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值