百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

接口开发过程中遇到的MySQL和Redis数据一致性问题的解决方案设想

mhr18 2024-11-24 18:49 22 浏览 0 评论

有时候,我们经常会借助Redis做一些并发控制,比如秒杀库存的控制以及一些抽奖上限和每天数量的控制,比如规定活动每天只能中100个,每个奖品每天只能中20个等,正常这种我们都会用redis来控制提高性能,还有秒杀的库存用redis的自减decr操作老来控制,比如如下业务场景。这里不讨论用redis做缓存,因为用redis做缓存不涉及一致性的问题,坚持的原则都是:先从redis获取,redis获取不到则从数据库中获取然后写回redis中,所以这里值讨论用redis做并发控制导致redis和数据库一致性问题的解决方案和设想。

场景1、数据库操作成功,单个redis操作失败

该场景必须保证redis在最后操作,并且只有一个redis操作,那么解决办法就比较简单,如果redis操作失败,直接抛出异常触发数据库事务回滚即可。

场景2、redis操作成功,数据库操作失败

这里举一个抽奖设计过程中的例子,控制奖品每天的抽奖上限,伪代码如下

//1、从redis获取每天已中奖数目
Long size = redisUtil.getTodayWinSize(key);
//2、如果已中奖数超过限制,则提醒用户不能中奖
if(size>100){
    return "已中奖数超过限制,用户不能中奖";
}else{
    //3、对每天已中奖数key进行自增
    Long new_size = redisUtil.incr(key);
    //4、如果自增后的值超过限制,则提醒用户不能中奖,以及回滚自增的值
    if(new_size>100){
           //5、对自增后的值进行回滚
           redisUtil.decr(key);
           return "已中奖数超过限制,用户不能中奖";
    }else{
          //6、用户可以中奖,对数据库进行操作
          MySQLUtil.insertAward();
    }
}

下面我们列举一下上面的代码逻辑里可能会存在的一些问题

问题1:第1步如果redis中没有数据如何处理

这里我们借助的是redis来操作,那么如果redis中没有值,那么我们需要从数据库中统计回去,但是这里我们并不需要对数据库进行锁表,我们直接借助redis的setNx操作。逻辑如下

Long size =  从redis中获取值
if(size==null){
    //从数据库中获取值不需要锁表
    size = 从数据库中获取值
   if(size!=null){
        Long result = redisUtil.setNx(key,size);
        //如果保存成功,则表示获取的值就是最新的值
       if(result!=0l){
             return size;
       }else{
            //有其它的线程已经放入redis中了,直接返回即可
           size = 从redis中获取值
           return size;
       }
   }
}

setNx的性质就是如果redis中有值了就不放入redis中了,所以在多线程的情况下,以最先放入的值为准。

问题2:第5步,为啥明明超过了值,还要对自增后的值进行回滚

这里如果不进行回滚,那么如果别的线程数据库操作失败了,回滚了redis,然后你这里确没有回滚的话会导致后面的用户还是中不了奖。

问题3:如果第6步数据库操作失败了怎么办

这里上面redis操作成功,然后这里数据库操作失败了,那么我们需要对redis进行回滚,也就是要对数据库操作的代码进行trycatch捕获,伪代码如下

          ...
          //6、用户可以中奖,对数据库进行操作
          try{
              MySQLUtil.insertAward();
          }catch{
              //7、对redis进行回滚
               redisUtil.decr(key);
             //触发数据库事务回滚
          }
    }
}

是不是上面的操作就完美了呢?正常来说,上面的操作可以满足大多数业务场景的需求了,但是如果我们的系统要更高可用一点,在对第7步对redis进行回滚的时候因为网络抖动,redis连接失败了,也就是回滚失败了怎么办呢,那当天的已中奖数目就多统计了一个,会让今天的实际中奖数目少1个,如何解决呢?

如果系统真的考虑到这种程度,那么我们在redis回滚操作失败后进行消息等级,让消费程序来进行回滚,伪代码如下

          ...
          //6、用户可以中奖,对数据库进行操作
          try{
              MySQLUtil.insertAward();
          }catch{
              //7、对redis进行回滚
              try{
                    redisUtil.decr(key);
              }catch{
                    登记消息,让消费程序来做回滚。
               }   
              //触发数据库事务回滚       
          }
    }
}

也许还有更较真的人就会说,要是消息登记也失败了呢?怎么办呢?这里可以参考我的一篇笔记,将失败的概率再次降低:三、架构设计:对RocketMQ消息登记报错和数据库事务之间关系的一些设想

也许还会有更更更转牛角尖的同学,可能会说,如果在redis回滚的时候,或者登记消息的时候程序挂了怎么办?额,想法是好,这种都要考虑是不是太心累了,你说我一定要考虑,我们的系统可用性就是要超级超级高。那好,这里提供一个终极,也是最最最兜底的解决方案:”最终一致性”,怎么实现呢?

“给key设置有效期”

这样子的话,就算当前key的数据因为回滚的问题导致不正确的,但是等失效重新从数据库中获取后就一致啦!

场景3、redis操作需要进行回滚怎么办

有时候我们的业务场景是要对多个redis进行操作的,但是如果数据库操作失败了,或者其中以恶搞redis操作失败了,前面的redis操作成功的也需要进行回滚。

这种情况,正常来说我们使用场景2的解决方案就可以了,如果有操作失败就捕获异常对之前的数据进行回滚,然后不放心可以采取消息队列,这里需要记得,消息队列进行回滚登记建议有多少个key进行回滚就登记多少条消息,不然到消费程序哪里有涉及到第一个redis回滚成功,第二个redis回滚失败,然后怎么办的恶心问题,如果分开来登记,那么如果还是回滚失败,那就继续返回false重新消费就好了。而登记消息怕失败的话可以参考我的解决方案:三、架构设计:对RocketMQ消息登记报错和数据库事务之间关系的一些设想

总结

最兜底的解决办法就是对redis的key进行有效期设置,这样子就可以保证最终一致性,不需要考虑上面这么多复杂的解决方案。

注:码字不易,转载请注明出处!

相关推荐

【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库

如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...

Pure Storage推出统一数据管理云平台及新闪存阵列

PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...

对Java学习的10条建议(对java课程的建议)

不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...

SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!

官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...

JDK21有没有什么稳定、简单又强势的特性?

佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...

「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了

在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...

Java面试题及答案最全总结(2025版)

大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...

数据库日常运维工作内容(数据库日常运维 工作内容)

#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...

分布式之系统底层原理(上)(底层分布式技术)

作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...

oracle 死锁了怎么办?kill 进程 直接上干货

1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...

SpringBoot 各种分页查询方式详解(全网最全)

一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...

《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略

《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...

LoadRunner(loadrunner录制不到脚本)

一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...

Redis数据类型介绍(redis 数据类型)

介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...

RMAN备份监控及优化总结(rman备份原理)

今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...

取消回复欢迎 发表评论: