你所在的位置:首 页 >> php编程 >> 详细新闻页面


php结合redis实现高并发下的抢购、秒杀功能

作者:zhirong1230 创建时间:2017-07-08 阅读次数:735


抢购、秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个:
1 高并发对数据库产生的压力
2 竞争状态下如何解决库存的正确减少("超卖"问题)
对于第一个问题,已经很容易想到用缓存来处理抢购,避免直接操作数据库,例如使用Redis。
重点在于第二个问题

常规写法:

查询出对应商品的库存,看是否大于0,然后执行生成订单等操作,但是在判断库存是否大于0处,如果在高并发下就会有问题,导致库存量出现负数

  1. $conn=mysql_connect("localhost","big","123456");    

  2. if(!$conn){    

  3.     echo "connect failed";    

  4.     exit;    

  5. }   

  6. mysql_select_db("big",$conn);   

  7. mysql_query("set names utf8");  

  8. $price=10;  

  9. $user_id=1;  

  10. $goods_id=1;  

  11. $sku_id=11;  

  12. $number=1;  

  13.   

  14. //生成唯一订单  

  15. function build_order_no(){  

  16.     return date('ymd').substr(implode(NULL, array_map('ord'str_split(substr(uniqid(), 7, 13), 1))), 0, 8);  

  17. }  

  18. //记录日志  

  19. function insertLog($event,$type=0){  

  20.     global $conn;  

  21.     $sql="insert into ih_log(event,type)   

  22.     values('$event','$type')";    

  23.     mysql_query($sql,$conn);    

  24. }  

  25. //模拟下单操作  

  26. //库存是否大于0  

  27. $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'";//解锁 此时ih_store数据中goods_id='$goods_id' and sku_id='$sku_id' 的数据被锁住(注3),其它事务必须等待此次事务 提交后才能执行  

  28. $rs=mysql_query($sql,$conn);  

  29. $row=mysql_fetch_assoc($rs);  

  30. if($row['number']>0){//高并发下会导致超卖  

  31.     $order_sn=build_order_no();  

  32.     //生成订单    

  33.     $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)   

  34.     values('$order_sn','$user_id','$goods_id','$sku_id','$price')";    

  35.     $order_rs=mysql_query($sql,$conn);        

  36.     //库存减少  

  37.     $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";  

  38.     $store_rs=mysql_query($sql,$conn);    

  39.     if(mysql_affected_rows()){    

  40.         insertLog('库存减少成功');  

  41.     }else{    

  42.         insertLog('库存减少失败');  

  43.     }   

  44. }else{  

  45.     insertLog('库存不够');  

  46. }  

  47. ?>  

    优化方案1:将库存字段number字段设为unsigned,当库存为0时,因为字段不能为负数,将会返回false

    //库存减少  

  1. $sql="update ih_store set number=number-{$number} where sku_id='$sku_id' and number>0";  

  2. $store_rs=mysql_query($sql,$conn);    

  3. if(mysql_affected_rows()){    

  4.     insertLog('库存减少成功');  

  5. }  

   优化方案2:使用MySQL的事务,锁住操作的行

 

  1. $conn=mysql_connect("localhost","big","123456");    

  2. if(!$conn){    

  3.     echo "connect failed";    

  4.     exit;    

  5. }   

  6. mysql_select_db("big",$conn);   

  7. mysql_query("set names utf8");  

  8.   

  9. $price=10;  

  10. $user_id=1;  

  11. $goods_id=1;  

  12. $sku_id=11;  

  13. $number=1;  

  14.   

  15. //生成唯一订单号  

  16. function build_order_no(){  

  17.     return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);  

  18. }  

  19. //记录日志  

  20. function insertLog($event,$type=0){  

  21.     global $conn;  

  22.     $sql="insert into ih_log(event,type)   

  23.     values('$event','$type')";    

  24.     mysql_query($sql,$conn);    

  25. }  

  26.   

  27. //模拟下单操作  

  28. //库存是否大于0  

  29. mysql_query("BEGIN");   //开始事务  

  30. $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id' FOR UPDATE";//此时这条记录被锁住,其它事务必须等待此次事务提交后才能执行  

  31. $rs=mysql_query($sql,$conn);  

  32. $row=mysql_fetch_assoc($rs);  

  33. if($row['number']>0){  

  34.     //生成订单   

  35.     $order_sn=build_order_no();   

  36.     $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)   

  37.     values('$order_sn','$user_id','$goods_id','$sku_id','$price')";    

  38.     $order_rs=mysql_query($sql,$conn);   

  39.       

  40.     //库存减少  

  41.     $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";  

  42.     $store_rs=mysql_query($sql,$conn);    

  43.     if(mysql_affected_rows()){    

  44.         insertLog('库存减少成功');  

  45.         mysql_query("COMMIT");//事务提交即解锁  

  46.     }else{    

  47.         insertLog('库存减少失败');  

  48.     }  

  49. }else{  

  50.     insertLog('库存不够');  

  51.     mysql_query("ROLLBACK");  

  52. }  

  53. ?>  

优化方案3:使用非阻塞的文件排他锁

  1. $conn=mysql_connect("localhost","root","123456");    

  2. if(!$conn){    

  3.     echo "connect failed";    

  4.     exit;    

  5. }   

  6. mysql_select_db("big-bak",$conn);   

  7. mysql_query("set names utf8");  

  8.   

  9. $price=10;  

  10. $user_id=1;  

  11. $goods_id=1;  

  12. $sku_id=11;  

  13. $number=1;  

  14.   

  15. //生成唯一订单号  

  16. function build_order_no(){  

  17.     return date('ymd').substr(implode(NULL, array_map('ord'str_split(substr(uniqid(), 7, 13), 1))), 0, 8);  

  18. }  

  19. //记录日志  

  20. function insertLog($event,$type=0){  

  21.     global $conn;  

  22.     $sql="insert into ih_log(event,type)   

  23.     values('$event','$type')";    

  24.     mysql_query($sql,$conn);    

  25. }  

  26.   

  27. $fp = fopen("lock.txt""w+");  

  28. if(!flock($fp,LOCK_EX | LOCK_NB)){  

  29.     echo "系统繁忙,请稍后再试";  

  30.     return;  

  31. }  

  32. //下单  

  33. $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'";  

  34. $rs=mysql_query($sql,$conn);  

  35. $row=mysql_fetch_assoc($rs);  

  36. if($row['number']>0){//库存是否大于0  

  37.     //模拟下单操作   

  38.     $order_sn=build_order_no();   

  39.     $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)   

  40.     values('$order_sn','$user_id','$goods_id','$sku_id','$price')";    

  41.     $order_rs=mysql_query($sql,$conn);   

  42.       

  43.     //库存减少  

  44.     $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";  

  45.     $store_rs=mysql_query($sql,$conn);    

  46.     if(mysql_affected_rows()){    

  47.         insertLog('库存减少成功');  

  48.         flock($fp,LOCK_UN);//释放锁  

  49.     }else{    

  50.         insertLog('库存减少失败');  

  51.     }   

  52. }else{  

  53.     insertLog('库存不够');  

  54. }  

  55. fclose($fp); 




    关键词(keywords):php结合redis实现高并发下的抢购、秒杀功能

分享到: 更多


前一篇: 做商城系统前需要准备什么?            后一篇:新手 Windows7 64位下 java完整开发环境搭建

phpchina   php爱好者   php100    中国网管联盟   LAMP兄弟连   河北联合大学   胜芳趣团网   rss 联系我们 问题反馈
版权所有@:ABCMS新闻发布系统!
建议使用ie6、ie8和 ff 浏览器进行浏览 | 建议分辨率:1024x768
地址:唐山市路北区高新技术产业园区龙华道128号 | 邮编:63000| 邮箱:zhirong1230@yeah.net