因为一个Bug,差点损失了100w

news2024/11/26 23:39:04

大家好,我是洋子

最近在做单接口的性能测试比较多,在压测过程发现了一个比较有意思的问题,拿出来和大家分享一下

背景是这样的,最近在搞线上的抽奖活动,压测的对象是一个抽奖接口,主要的逻辑见程序的流程图

请添加图片描述

  • 这个抽奖的接口逻辑是先通过检查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,那我不直接被开了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/551523.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring ioc容器

Spring ioc容器 导入 spring 容器包 使用 ioc 容器之前,需要先导入 Spring 的包 在 spring maven中下载 spring maven 网址:Maven Repository: spring (mvnrepository.com) 搜索 spring 找到 Spring Web MVC点击 spring-webmvc 进入 选择一个版本号点击&#x…

Ocean Optics USB2000光谱仪无法在Win10系统运行

1、问题描述 USB2000型光谱仪,由于生产年代过于久远,虽然能被Win10系统识别,但是驱动程序安装完成后依然报错, 提示:该设备无法启动。(代码 10) 请求USB BOS 描述符失败。 运行SpectraSuite软件…

ETLCloud社区版与Kettle对比分析

ETLCloud社区版本与Kettle社区版本都有什么优势和劣势? Pentaho Data Integration(PDI),也称为Kettle,是一款开源的数据集成工具,国内有很多企业都选择kettle作为数据清洗工具。以下是kettle的主要特点和优势: 广泛的…

python 绘制箱型图一些技巧

引言 本篇是之前有一个需求,需要用python来画箱型图,但要求很多,所以我也不断再版,今天突然想起来这个东西可以总结一下,正好马上得思考下一步做啥了,有足够的空闲时间,所以准备把一些基础概念…

【笔试强训day48】顺时针打印矩阵、左右最值最大差

博主简介:想进大厂的打工人博主主页:xyk:所属专栏: 笔试强训专栏 笔试强训 目录 文章目录 一、选择题 1.1 10.1.0.1/17的广播地址是( ) 1.2 网络地址172.16.22.38/28 请写出此地址的子网ID以及广播地址,此地址所处子网…

[2019“好贷杯“风控能力挑战赛一等奖] 基于神经网络算法的A股市场多因子选股的研究

本论文为 “2019年中国高校风险管理与控制能力挑战赛” 实际参赛作品,获得一等奖。 本论文体现了较好的数学建模思想和写作,所以将论文和完整代码进行了开源,方便与大家交流。

NetSuite SuiteTax之中国影响

这篇是还账。3个月前林师傅给的一个题目,陆陆续续的学习,一直没有弄完,直到今朝。 SuiteTax是2018年GA的一个重大功能,是NetSuite面向国际市场的一个标志动作。它将过去以美国为中心的税务功能,转向为国际市场服务。只…

xhs-xs webmsxyw分析

近期又更新了,先是改了x-s生成,然后又加上了a1校验。 后面可能会全参校验,比如再加上gid、deviceId、profileData、x-s-common、smidV2之类。 估计以后不能写xhs了,大家且看且珍惜吧。之前相关的文章都被下架了 危!…

C语言扫雷小游戏

扫雷小游戏 一,扫雷游戏的理解和整体思路1. 扫雷游戏的玩法2.写扫雷小游戏的整体思路 二,函数功能的实现模块1.宏定义2.打印菜单3.初始化雷盘4.打印雷盘5.布置雷6.玩家排雷6.1 显示该坐标有几个雷的函数6.2雷盘展开函数的实现 三.最终扫雷游戏的的实现1.…

AI绘画Stable Diffusion安装、使用教程 整合包下载

安装Stable Diffusion webui 效果图: 1.准备工作 在下载 AI 绘画工具前,电脑上需要提前下载一些运行的环境 1.下载python、git 首先本地机器最好是英伟达的 N 卡,并且至少需要 4GB 显存才能在本地运行,当然,A 卡也是…

微服务开发系列——第一篇:项目搭建(保姆级教程)

总概 A、技术栈 开发语言:Java 1.8数据库:MySQL、Redis、MongoDB、Elasticsearch微服务框架:Spring Cloud Alibaba微服务网关:Spring Cloud Gateway服务注册和配置中心:Nacos分布式事务:Seata链路追踪框架…

【CH32】| 00——开发环境搭建 | 软件安装 | 资料及工具下载

系列文章目录 TODO 文章目录 1. 简介2. 下载软件3. 安装4. 资料及工具下载4.1 芯片数据手册/参考手册4.2 评估(开发)板原理图/demo4.3 烧录软件4.3.1 isp串口一键下载4.3.2 WCH-LINK Utility烧录 4.4 WCH LINK相关资料/驱动安装4.4.1 WCH LINK相关资料4.4.2 WCH LINK驱动安装 5…

摆摊卖网红气球怎么样?

本章主要介绍一下最近网红气球: 最近看到很多摆摊的抖音视频,都在说卖气球很好,成本低,收益高,所以调研了一下,网红气球分好几种: a,飘空气球; b.手持网红气球 c.青蛙 首先介绍飘空…

初始python

初始python 缘由 ​ 对于python,相比大家并不陌生,Python是一种高级的、解释性编程语言,它具有简洁的语法和强大的内置函数,适用于广泛的应用领域,例如运维,Web开发,数据科学,人工智能等。Pyt…

JVM基础学习---1、JVM总体机制、类加载机制

1、JVM总体机制 1.1 JVM概念 JVM:Java Virtual Machine,翻译过来是Java虚拟机。 JRE:Java Runtime Environment,翻译过来是Java运行时环境。 JDK:Java Development Ki JDK:Java Development Kits&#…

Mysql字符集

1、修改MySQL5.7字符集 我们在新建数据库时(Navicat)界面时会遇到这两个选项,字符集和排序规则 在MySQL 8.0版本之前,MySQL 5.7 默认的客户端和服务器都用了 latin1 ,而latin1是不包含中文的,所以保存中文…

MetersPhere 试用

1. 功能测试 1.1 用例管理 脑图管理 样式: 脑图用例导入 1.2 用例评审 1.2.1 发起用例评审 1.2.2 关联用例: 1.2.3 用例评审 2. 接口测试 2.1 接口定义 类似postman、foxapi等主流接口管理页面 2.2 用例配置: 需要在meterphere安装节点…

路径规划算法:基于人工蜂群算法的路径规划算法- 附代码

路径规划算法:基于人工蜂群的路径规划算法- 附代码 文章目录 路径规划算法:基于人工蜂群的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要:本文主要介绍利用智能优化算法人工…

C++ [STL之vector的使用]

本文已收录至《C语言和高级数据结构》专栏! 作者:ARMCSKGT STL之vector的使用 前言正文默认成员函数普通构造拷贝构造析构函数赋值重载 迭代器正向迭代器反向迭代器const迭代器 容量类空间容量查询空间容量操作扩容操作元素数量操作缩容操作 数据访问下标…

numpy log随机产生非常奇怪的数字(np.log的大坑)

背景 有一批信号数据要送到网络里训练,训练之前为了统一量纲,首先根据方差和均值做了一次标准化,然后求了一次能量(20*log10(x)),也就是说送进网络里的其实是一个能量谱,但是训练过程中经常蹦出…