接口开发过程中遇到的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、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)