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解决抢购、秒杀、抢楼、抽奖等阻塞式高并发库存防控超量的思路方法
如今在电商行业里,秒杀抢购活动已经是商家常用促销手段.但是库存数量有限,而同时下单人数超过了库存量,就会导致商品超卖甚至库存变负数的问题. 又比如:抢购火车票.论坛抢楼.抽奖乃至爆红微博评论等也会引发 ...
随机推荐
- hibernate整合进spring后的事务处理
单独使用hibernate处理事务 本来只用hibernate开发,从而可以省了DAO层实现数据库访问和跨数据库,也可以对代码进行更好的封装,当我们web中单独使用hibernate时,我们需要单独的 ...
- jspSmartUpload使用初步
jsp中使用SmartUpload上传文件 1.下载jspSmartUpload组件后,解压缩.把com目录复制到应用程序的WEB-INF\classes目录下. 2.尤其注意 (1)文件上传表单要添 ...
- Q_UNUSED() 方法的使用
Q_UNUSED() 没有实质性的作用,用来避免编译器警告 //比如说 int testFunc(int a, int b, int c, int d) { int e; return a+b+c; ...
- mac下配置android环境变量
下面我将一下mac环境下的配置步骤: 1.在本地目录(home directory)中创建文件.bash_profile2.在文件中写入以下内容:export PATH=${PATH}:/Users/ ...
- 【HTML】div居中显示
方法1: .parent { width:800px; height:500px; border:2px solid #000; position:relative; } .child { width ...
- SpagoBI 教程 Lesson 2: OLAP with JPIVOT
SpagoBI Lesson 2: OLAP with JPIVOT Online Analytical Processing Online Analytical Processing (OLAP) ...
- CentOS 7 之前好好的,突然一天启动时黑屏,没有登陆界面了(配置 network-scripts 连网)
原因: 百度大神说是Gnome(一套纯粹自由的计算机软件,运行在操作系统上,提供图形桌面环境)不行了. 解决方法: 1. 重启系统,ctrl + alt + F2 进入命令行界面. 2. sudo s ...
- JDBC创建数据库实例
在本教程将演示如何在JDBC应用程序中创建数据库. 在执行以下示例之前,请确保您已经准备好以下操作: 具有数据库管理员权限,以在给定模式中创建数据库. 要执行以下示例,需要用实际用户名和密码替换这里用 ...
- e823. 创建JSplitPane
A split pane divides its space between two components. The split pane contains a divider that allows ...
- e802. 创建一个位置大小的JProgressBar组件
A progress bar with an unknown maximum typically displays an animation until the task is complete. N ...