在目前这家公司做的第一个项目抽奖项目,要求每人每天可以有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经典问题,防止库存把控不足问题的更多相关文章

  1. 10 个 MySQL 经典错误【转】

    Top 1:Too many connections(连接数过多,导致连接不上数据库,业务无法正常进行) 问题还原 mysql> show variables like '%max_connec ...

  2. 数据库MySQL经典面试题之SQL语句

    数据库MySQL经典面试题之SQL语句 1.需要数据库表1.学生表Student(SID,Sname,Sage,Ssex) --SID 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学 ...

  3. 【不断更新】mysql经典50道题自我练习

    mysql经典50道题自我练习 测试数据和练习题均转载自CSDN博主@启明星的指引的文章sql语句练习50题(Mysql版),用于mysql的每日自我练习 表名和字段 –1.学生表 Student(s ...

  4. MYSQL经典练习题,熟悉DQL

    MYSQL经典练习题 (本练习题可让你熟悉DQL,快速的上手DQL) 首先,先在数据库中建立基本数据库以及表项: DROP DATABASE IF EXISTS `test`; CREATE DATA ...

  5. Mysql 经典案例总结(学习之前需要有Mysql基础)01

    Sql 经典案例 gb 4.2 ** 1 检索记录 (1) 主要介绍 Sql的基本SELECT查询语句 使用 SELECT * from 表 查询数据 查询该表的每一列数据 * 代表所有的意思 也可以 ...

  6. 经典书籍---MySQL经典书籍下载

    以下是一些经典的MySQL书籍电子版,括号内为提取码,若需自取. 欢迎阅读纸质版,尊重作者版权 高性能MySQL_中文版 [ hre3 ] 高性能MySQL_英文版[ m2xj ] MySQL技术内幕 ...

  7. oracle/mysql经典电子书籍pdf下载

    Oracle LZ写的书,深入结合oracle设计.优化/SQL优化.应用层架构与优化.大量生产案例,敬请期待... Oracle编程艺术 深入理解数据库体系结构(第3版) 链接:https://pa ...

  8. 这十个MySQL经典错误

    今天就给大家列举 MySQL 数据库中,最经典的十大错误案例,并附有处理问题的解决思路和方法,希望能给刚入行,或数据库爱好者一些帮助,今后再遇到任何报错,我们都可以很淡定地去处理.学习任何一门技术的同 ...

  9. PHP解决抢购、秒杀、抢楼、抽奖等阻塞式高并发库存防控超量的思路方法

    如今在电商行业里,秒杀抢购活动已经是商家常用促销手段.但是库存数量有限,而同时下单人数超过了库存量,就会导致商品超卖甚至库存变负数的问题. 又比如:抢购火车票.论坛抢楼.抽奖乃至爆红微博评论等也会引发 ...

随机推荐

  1. Git 执行更改

    Jerry 克隆库,他决定实现基本字符串操作.于是,他创建文件string.c,在添加内容到 string.c 会这个样子. #include <stdio.h> int my_strle ...

  2. Entity Framework Code First - Change Tracking

    In this post we will be discussing about change tracking feature of Entity Framework Code First. Cha ...

  3. 关于Unity中物理检测的准备

    1.要确定每个物体的碰撞类型,是有碰撞效果的碰撞还是没有碰撞效果的碰撞(is trigger),带不带刚体. 2.给每个物体分层,再设置哪些层会发生碰撞,哪些完全不产生碰撞. 3.给每个物体设置标记, ...

  4. Chrome驱动安装问题

    1.org.openqa.selenium.WebDriverException: unknown error: call function result missing 'value' 原因:浏览器 ...

  5. Mac和Linux下pip更换源

    cd ~mkdir .pip vim .pip/pip.conf 在pip.conf中写入 [global]timeout = 6000index-url = https://pypi.tuna.ts ...

  6. 【Centos】【Python3】yum install 报错

    运行yum install 安装时报错 File "/usr/libexec/urlgrabber-ext-down", line 28 except OSError, e: Sy ...

  7. moodle中的完形填空题的文本编写方法

    moodle中的完形填空题的文本编写方法 [完形填空题]考题把一段文字挖去一些空,让考生根据上下文正确地完成这些填空.完型填空题中的一段短文可以包括各种题目,如选择,填空,和数字题等. 题目的编辑是在 ...

  8. struts2系列(三):struts2配置详解

    原文链接:http://www.cnblogs.com/fmricky/archive/2010/05/20/1740479.html 1.<include> 利用include标签,可以 ...

  9. Windows命令处理进程

    1.  查看进程命令 tasklist 2.以翻页模式查看进程 tasklist  |  more 3.搜索关键字查找进程 tasklist  |  findstr  "cmd" ...

  10. 配置防盗链 访问控制Directory 访问控制FilesMatch