因为一个Bug,差点损失了100w
大家好,我是洋子
最近在做单接口的性能测试比较多,在压测过程发现了一个比较有意思的问题,拿出来和大家分享一下
背景是这样的,最近在搞线上的抽奖活动,压测的对象是一个抽奖接口,主要的逻辑见程序的流程图
这个抽奖的接口逻辑是先通过检查Redis里面存入的已发放奖品数量
查出已发放奖品数量后,与活动配置当中的奖品库存进行对比
若无库存,此时已发放的奖品数量大于了活动预先配置的奖品库存,那么返回库存为空的信息
若还有库存,在Redis里面新增本次中奖的用户信息,设置Redis过期时间,接着进入后续发奖品的逻辑(写DB,修改发送状态等)
在无并发(同一时间内只有一个用户请求)的场景下,这样处理并没有问题,但是在压测当中,有并发请求的场景下,我发现DB里面写入超出库存数量的记录,换句话说上面通过Redis检查库存,拦截多发奖的逻辑存在Bug,没有正常拦截
通过Review代码,我发现这段检测库存的逻辑Redis 的zCard查询,再zAdd插入,为非原子性操作,zCard与zAdd为串行执行,在并发场景下,zCard可能查出的库存是相同的,如活动配置的库存为6,有10个并发用户同时查询出当前已抽奖品数量为5,因为总库存6>已抽奖品数5,当前还有剩余库存,则正常为这10个并发用户发送奖品
// 查Redis:当前活动已抽出的奖品数量$strKey = sprintf(PrizeStockKey, PrizeInfo_Id, $strDayTime);$intStock = $daoRedis->zCard($strKey);// 检查当前库存,是否超过活动预先配置的库存if($intStock >= $arrPrizeInfo['stock']) {Log::warning("the stock empty");return $Output;}$ret = $daoRedis->zAdd($strKey, $intUserId, $intTime);//后续逻辑:写DB发送奖品,此处省略
超发奖品这样的逻辑显然是不符合预期的,那我们该如何修改呢,先zAdd再zCard,行不行呢,答案也是不可以,因为先zAdd可能会导致所有用户均无法进行奖品发送
举个例子,总库存为6,此时并发10个用户进行zAdd,再进行zCard 查询为10,超过总库存,此时程序认为奖品已经发完,无法正常发奖
// 查Redis:当前活动已抽出的奖品数量$strKey = sprintf(PrizeStockKey, PrizeInfo_Id, $strDayTime);//先zAdd,再查询zCard$ret = $daoRedis->zAdd($strKey, $intUserId, $intTime);$intStock = $daoRedis->zCard($strKey);// 检查当前库存,是否超过活动预先配置的库存if($intStock > $arrPrizeInfo['stock']) {Log::warning("the stock empty");return $Output;}//后续逻辑:写DB发送奖品,此处省略
产生这样的现象,归根结底还是以上逻辑Redis都是非原子操作,我们先了解一下什么是原子操作
原子操作是指在计算机科学中的一种操作方式,它被设计成在执行期间不可中断的单个操作。原子操作要么完全执行,要么完全不执行,不会出现中间或部分执行的情况。原子操作通常用于多线程或并发编程中,用于确保共享资源的一致性和并发访问的正确性
原子操作的特点
原子性:原子操作是不可分割的单个操作,要么全部执行成功,要么全部不执行。没有其他线程能够观察到原子操作的中间状态
独立性:原子操作是独立于其他操作的,不受其他线程的干扰或影响。原子操作的执行不会受到并发环境的影响。
原子操作的作用
可以用于实现对共享数据的互斥访问,以避免竞态条件(race condition)的发生。竞态条件是指多个线程对同一共享资源进行并发访问时可能导致的不确定或不正确的结果
常见的原子操作类型
原子读取(atomic read)、原子写入(atomic write)、原子递增(atomic increment)、原子比较并交换(atomic compare-and-swap)等。这些操作通常由硬件或操作系统提供支持,以确保其执行的原子性。
为了修复此问题,我们将zCard和zAdd改成incrBy即可解决上面的问题,因为Redis的incrBy是原子操作,在并发场景也不会出现因并发访问而导致的数据不一致或竞态条件问题
//写redis:当前奖品抽出数量+1$intStock = $daoRedis->incrBy($strKey, 1);// 检查当前库存,是否超过活动预先配置的库存if($intStock >= $arrPrizeInfo['stock']) {Log::warning("the stock empty");return $Output;}//后续逻辑:写DB发送奖品,此处省略
结束语
完事后,我仔细想想,还好通过这次压测,发现了这个问题,要是活动当中本来发1w现金,最后发成了100w,那我不直接被开了
因为一个Bug,差点损失了100w相关推荐
- 一个Bug能有多大影响:亏损30亿、致6人死亡、甚至差点毁灭世界...
欢迎关注方志朋的博客,回复"666"获面试宝典 作者:博雯 来源:量子位(QbitAI) 一个Bug就地蒸发5亿美元: 软件设计层面出Bug致6人死亡: DeBug不成功直接世 ...
- 一个BUG导致3000万损失!涉及资金交易的功能该如何做好测试和项目管理
一.前言 最近朋友公司发生了一个非常严重的事情,在对接公司客户的充值接口时,一个BUG导致了3000万左右的资金入错帐! 当听说这个事情的时候,我第一反应可能是前端的充值按钮以及充值接口未做防重复提交 ...
- 一个 bug ,罚款 200,我真待过这样的公司
01 事情起因 我为什么想突然聊这个话题呢?因为前几天我看到吴军老师发了一条关于谷歌史上最贵 bug 的案例. 吴军老师是这样写的: 谷歌最昂贵的一个 bug,是广告系统中的一个 bug,每天损失 2 ...
- Bug 险中求: 作为新手,我怎样才能快速找到不和别人重复的第一个 bug?
聚焦源代码安全,网罗国内外最新资讯! 编译:奇安信代码卫士团队 当 Katie Paxton-Fear (@InsiderPhD) 第一次受邀参加在伦敦举办的 HackerOne 现场活动时,她未曾 ...
- 真赔麻了!!一个BUG和一个回帖直接赔了20万!
往期热门文章: 1.聊聊Spring中最常用的11个扩展点 2.Java内部类有坑,100%内存泄露! 3.分库分表后,如何保证数据一致性? 4.JDK 19 / Java 19 正式发布,虚拟线程来 ...
- 程序bug致损失400亿,判程序员坐牢? 搞笑我们是认真的
号外!号外!走过,路过,不要错过!日本 IT 业的狗血八卦继续独家放送啦!! 2015 年 9 月 3 日,随着东京最高法院驳回瑞穗证券的上诉,维持二审的原判结果,一个长达 10 年的诉讼终于画下了句 ...
- 软件测试培训分享:做软件测试工作如何清楚的描述一个bug
一名合格的软件测试工程师是需要清楚的交代自己的工作任务的,必须要清楚的告诉技术员出现的bug,那么做软件测试工作如何清楚的描述一个bug呢?来看看下面的详细介绍. 软件测试培训分享:做软件测试工作如何 ...
- EnterLib PIAB又一个BUG?
在<这是EnterLib PIAB的BUG吗?>一文中我们讨论了PIAB关于抽象基类的BUG,今天又发现了一个新的问题.问题的起因源于<IoC+AOP的简单实现>这篇文章,因为 ...
- 从一个Bug开始,重新认识一个强大的 Gson
点击上方"方志朋",选择"置顶公众号" 技术文章第一时间送达! 作者:Mafly, 地址:www.cnblogs.com/mafly/p/gson.html 从 ...
最新文章
- PHP新手上路(六)
- mysql5.7 too many_Mysql 错误too many connections解决方案
- 字符编码转换_进制转换(GB2312,GBK,JNI,HexTOStr)
- 吴恩达深度学习笔记5-Course2-Week1【深度学习的实用层面】
- 使用 Arthas 排查开源 Excel 组件问题
- 【论文】Awesome Relation Extraction Paper(关系抽取)(PART III)
- 菜鸟学Linux 第095篇笔记 MySQL 5.6主从复制
- 番茄时间管理法(Pomodoro Technique)
- libvlc 裁剪及编译
- mcu和服务器通讯协议pdf,Modbus DTU与服务端间的通讯协议
- 据说是“缓存之王”? Caffeine高性能设计剖析
- os.rename和os.renames区别
- 软件测试-自我介绍-整体框架
- Java 实现视频弹幕功能
- 启德教育:2018英国留学报告
- python培训实习报告
- 硬核| 实例|货架设计利器|有限元法
- linux vi粘贴格式易错乱
- PHP创建PDF文件(通过FPDF类库)
- 【testNG】执行多个suit
热门文章
- python杨辉三角代码,python实现杨辉三角的几种方法代码实例
- 管理 - 如何与领导相处
- 【xlwings api语言参考】Range.ShrinkToFit 属性
- 华为 android 安全,华为 EMUI/Magic UI 安全更新 2020-3
- Unable to resolve 'JNDI_EcsReadUpf'. Resolved ''; remaining name
- java输入输出语句_Java中的常用输入输出语句的操作代码
- Hadoop 安装部署-多服务器
- 《痞子衡嵌入式半月刊》 第 74 期
- 支持向量机之SVR 用法与参数详解 python
- Java乐图下载_Java平台乐图导航地图测评:实时跟踪是亮点