PHP mysql经典问题,防止库存把控不足问题
在目前这家公司做的第一个项目抽奖项目,要求每人每天可以有20次抽奖机会,抽奖机会可以通过多种方式获取,那么就要求每次入库增加抽奖机会的时候检测当前拥有的抽奖机会是否达到了20次,如果达到了,就不再增加机会。这里需要两个步骤
<?php
$count=query_sql("select count(*) as num from table where user_id=用户id");//统计拥有了多少机会
if($count>=20)
{
die('不增加机会');
}
query_sql("insert into table (……)");#增加一次抽奖机会
?>
很显然,先获取用户有了多少条记录,如果达到上限,不再增加,如果没达到,则增加。很简单的逻辑,可是在测试的过程中,发现并发高的情况下,总是能插入22条 23条等等,超过20条记录的。
这个问题在很多业务逻辑中都遇到了,比如商品库存控制中,获取库存数量如果数量>0则能够扣库存,然后就对库存自减,可是多个并发的情况下,就把库存扣为负数了。当然也是不合理的,说明大家都下单成功了,按道理讲不应该出现超卖的情况的。可是这样的代码总是会出现
<?php
$stock_num=query_sql("select stock_num from goods where id=商品id");//获取库存数量
if($stock_num>$buy_num)
{
query_sql("update goods set stock_num=stock_num-$buy_num where id=商品id");//数量进行扣库存
}
屡见不鲜的代码,我想各位读者也遇到过这样的逻辑的,毕竟资源总是有限的,我们肯定要多做一些判断,然而如果过分依赖这种读取的数量比较,就会造成超卖的问题。
当多个进程同时读取数据库得到的是极限的边缘的时候,大家都走进了判断分支条件中,然后发现我们都满足条件,于是大家一起入库或者操作数据库扣库存,然后呢?很明显,就超卖了!
这个问题在我的同事做的项目中也遇到了,本来每天100个库存的,前几天都是在100这个边缘就结束了很正常,结果某一天出现了121条记录,显然,大家突破了这个限制,这就让人很尴尬了,库存控制问题,非常严重,会给公司带来巨大的损失。非实物道具能够撤回或者处理,实物商品可以拒绝发货,可是仍然免不了被投诉。
这个问题如此严重,那么后来是怎么解决的呢?
我在做抽奖项目的时候,因为数据库担心并发问题,采用了redis记录获取的次数,每获取一次,redis+1,然后到达20次的时候,入库之前,先判断redis中的数量 再判断数据库中的数量,两者有一个不满足,则立即告知不可添加记录。这样就是等于把统计数量这个逻辑从数据库的统计交给了redis的统计,因为redis在原子性的操作,可以实现计数器功能应对并发的问题,这也是一个办法,只是来说减轻了数据库的责任,降低风险。
然而真的数据库层面就没有办法了吗?
我想起了平台组组长曾经说过的update语句中加where条件
<?php
$stock_num=query_sql("select stock_num from goods where id=商品id");//获取库存数量
if($stock_num>$buy_num)
{
query_sql("update goods set stock_num=stock_num-$buy_num where id=商品id and stock_num>=$buy_num");//数量进行扣库存
}
使用where子句进行对库存数量的控制,避免达到负数,这种方式能够使得SQL语句在库存不够的情况下返回false,这样就能正确的得到下单失败了。然而,对于count出来的数据如何在SQL语句中实现查询之后再更新呢?
这个问题就复杂了很多,毕竟我从来没实现过在update的语句中使用select count语句,这样的语句令人匪夷所思,但是SQL语句之所以强大,就是强大在这里,子句可以有很强大的分析功能,帮我们实现。
采用where设定子句,将子句设为表达式,避免理解为select语句,试试看
<?php
query_sql("update table set 要改字段='要改的值' where id=要改的记录id and (select count(*) from table where 条件语句)<10");//在子查询中设定某个查询条件记录总数小于上限则进行更新
?>
代码虽然可以这样写,但是会报错 ERROR 1093 (HY000): You can't specify target table for update in FROM clause
就是说你在用update的时候不能用这样的子查询作为条件,那么我们还有其他的方法解决。给这个子查询换一个临时表不就行了吗?
update table set 要改字段='要改的值' where id=要改的记录id and (select count(*) from (select * from table table where 条件语句) as tmp)<10
这样就不会被立即为更新当前表的时候查询当前表了,因为被临时表进行了转换。或者采用网友的写法
update table,(select count(*) as num from table where 条件) as b set 要改字段='要改的值' where id=要改的记录id and b.num<10
这种方式可能更加清晰一点,能够容易理解一些,不过我还是比较喜欢我自己写出的那种方式,通过子句作为条件进行理解。
PHP mysql经典问题,防止库存把控不足问题的更多相关文章
- 10 个 MySQL 经典错误【转】
Top 1:Too many connections(连接数过多,导致连接不上数据库,业务无法正常进行) 问题还原 mysql> show variables like '%max_connec ...
- 数据库MySQL经典面试题之SQL语句
数据库MySQL经典面试题之SQL语句 1.需要数据库表1.学生表Student(SID,Sname,Sage,Ssex) --SID 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学 ...
- 【不断更新】mysql经典50道题自我练习
mysql经典50道题自我练习 测试数据和练习题均转载自CSDN博主@启明星的指引的文章sql语句练习50题(Mysql版),用于mysql的每日自我练习 表名和字段 –1.学生表 Student(s ...
- MYSQL经典练习题,熟悉DQL
MYSQL经典练习题 (本练习题可让你熟悉DQL,快速的上手DQL) 首先,先在数据库中建立基本数据库以及表项: DROP DATABASE IF EXISTS `test`; CREATE DATA ...
- Mysql 经典案例总结(学习之前需要有Mysql基础)01
Sql 经典案例 gb 4.2 ** 1 检索记录 (1) 主要介绍 Sql的基本SELECT查询语句 使用 SELECT * from 表 查询数据 查询该表的每一列数据 * 代表所有的意思 也可以 ...
- 经典书籍---MySQL经典书籍下载
以下是一些经典的MySQL书籍电子版,括号内为提取码,若需自取. 欢迎阅读纸质版,尊重作者版权 高性能MySQL_中文版 [ hre3 ] 高性能MySQL_英文版[ m2xj ] MySQL技术内幕 ...
- oracle/mysql经典电子书籍pdf下载
Oracle LZ写的书,深入结合oracle设计.优化/SQL优化.应用层架构与优化.大量生产案例,敬请期待... Oracle编程艺术 深入理解数据库体系结构(第3版) 链接:https://pa ...
- 这十个MySQL经典错误
今天就给大家列举 MySQL 数据库中,最经典的十大错误案例,并附有处理问题的解决思路和方法,希望能给刚入行,或数据库爱好者一些帮助,今后再遇到任何报错,我们都可以很淡定地去处理.学习任何一门技术的同 ...
- PHP解决抢购、秒杀、抢楼、抽奖等阻塞式高并发库存防控超量的思路方法
如今在电商行业里,秒杀抢购活动已经是商家常用促销手段.但是库存数量有限,而同时下单人数超过了库存量,就会导致商品超卖甚至库存变负数的问题. 又比如:抢购火车票.论坛抢楼.抽奖乃至爆红微博评论等也会引发 ...
随机推荐
- centos7配置kerberos服务,并使用JAAS登录
准备两个虚拟机:192.168.1.101.192.168.1.102,101作为kerberos的server端,102作为kerberos的client端.开启88端口. 1.安装kerberos ...
- VS Code 如何直接在浏览器中预览页面
VS Code 预览html页面的时候,默认需要在资源管理器中显示,再在浏览器中预览.今天介绍一下如何直接预览html页面. 方法一:自己配置快捷键 1.ctrl + shift + p 或者 F1 ...
- jquery学习心得:一个很好的css和js函数调用的例子
统一目录下的资源结构图: <html><head> <link rel="stylesheet" href="gallery.css&quo ...
- 关于1KB病毒的清除
以前中过很多次的1KB病毒,这种病毒来源一般都是U盘,就是去打印店插个U盘,回来插自己电脑发现U盘中毒了. 中毒特征就是根目录下的所有文件夹都变成快捷方式,都只有1KB大小,而根目录下的文件和压缩包不 ...
- git使用情景2:commit之后,想撤销commit
================实际操作============================== 提交到git上撤回 git reset head^1 提交到本地 撤回 git reset --s ...
- mysql数据库性能测试报告
网上有写的好的模板, 直接复链接了: 1. http://blog.csdn.net/mituan1234567/article/details/45247989 2. https://msdn.mi ...
- Android O seLinux 编译错误
编译android O源码,遇到错误 FAILED: out/target/product/hon450/obj/ETC/sepolicy_intermediates/sepolicy /bin/ba ...
- python numpy logic_and
>>> import numpy as np >>> np.logical_and(True, False) False >>> np.logic ...
- 【转载】C#反射机制详解
反射的定义:审查元数据并收集关於它的类型信息的能力,元数据(编辑后的基本数据单元)就是一大堆表,编译器会创建一个类定义表,一个字段定义表,一个方法定义表等,System.Reflection命名空间包 ...
- SqlException with message "Caught java.io.CharConversionException." and ERRORCODE=-4220
Technote (troubleshooting) Problem(Abstract) When an application uses the IBM Data Server Driver for ...