php多进程模拟并发事务产生的问题小结


Posted in PHP onDecember 07, 2018

前言

本文通过实例代码给大家介绍了关于php多进程模拟并发事务产生的一些问题,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧


drop table if exists `test`;
create table if not exists `test` (
 id int not null auto_increment , 
 count int default 0 , 
 primary key `id` (`id`)
) engine=innodb character set utf8mb4 collate = utf8mb4_bin comment '测试表';

insert into test (`count`) values (100);

php 代码

// 进程数量
$pro_count = 100;
$pids = [];
for ($i = 0; $i < $pro_count; ++$i)
{
 $pid = pcntl_fork();
 if ($pid < 0) {
  // 主进程
  throw new Exception('创建子进程失败: ' . $i);
 } else if ($pid > 0) {
  // 主进程
  $pids[] = $pid;
 } else {
  // 子进程
  try {
   $pdo = new PDO(...);
   $pdo->beginTransaction();
   $stmt = $pdo->query('select `count` from test');
   $count = $stmt->fetch(PDO::FETCH_ASSOC)['count'];
   $count = intval($count);
   if ($count > 0) {
    $count--;
    $pdo->query('update test set `count` = ' . $count . ' where id = 2');
   }
   $pdo->commit();
  } catch(Exception $e) {
   $pdo->rollBack(); 
   throw $e;
  }
  // 退出子进程
  exit;
 }
}

期望的结果

期望 count 字段减少的量超过 100,变成负数!也就是多减!

实际结果

并发 200 的情况下,运行多次后的结果分别如下:

1. count = 65
2. count = 75
3. count = 55
4. count = 84
...

与期望结果相差甚远!为什么会出现这样的现象呢?

解释

首先清楚下目前的程序运行环境,并发场景。何为并发,几乎同时执行,称之为并发。具体解释如下:

进程 过程 获取 更新
1-40 同时创建并运行 100 99
41-80 同时创建并运行 99 98
81 - 100 同时创建并运行 98 97

对上述第一行做解释,第 1-40 个子进程的创建几乎同时,运行也几乎同时:

进程 1 获取 count = 100,更新 99
进程 2 获取 count = 100,更新 99
...
进程 40 获取 count = 100,更新 99

所以,实际上这些进程都做了一致的操作,并没有按照预期的那样:进程1 获取 count=100,更新 99;进程 2 获取进程1更新后的结果 count=99,更新98;...;进程 99 获取进程 98更新后的结果count=1,更新0
,产生的现象就是少减了!!

结论

采用上述做法实现的程序,库存总是 >= 0。

疑问

那要模拟超库存的场景该如何设计程序呢?

仍然采用上述代码,将以下代码:

if ($count > 0) {
 $count--;
 $pdo->query('update test set `count` = ' . $count . ' where id = 2');
}

修改成下面这样:

if ($count > 0) {
 $pdo->query('update test set `count` = `count` - 1 where id = 2');
}

结果就会出现超库存!!

库存 100,并发 200,最终库存减少为 -63。为什么会出现这样的情况呢?以下描述了程序运行的具体过程

进程 1 获取库存 100,更新 99
进程 2 获取库存 100,更新 98(99 - 1)
进程 3 获取库存 100,更新 97(98 - 1)
....
进程 168 获取库存 1 ,更新 0(1-1)
进程 169 获取库存 1 ,更新 -1(0 - 1)
进程 170 获取库存 1 ,更新 -2(-1 - 1)
....
进程 200 获取库存 1,更新 -63(-62 - 1)

现在看来很懵逼,实际就是下面这条语句导致的:

$pdo->query('update test set `count` = `count` - 1 where id = 2');

这边详细阐述 进程 1,简称 a;进程 2,简称 b 他们具体的执行顺序:

1. a 查询到库存 100
2. b 查询到库存 100
3. a 更新库存为 99(100 - 1),这个应该秒懂
4. b 更新库存为 98(99 - 1)
- b 在执行更新操作的时候拿到的是 a 更新后的库存!
- 为什么会这样?因为更新语句是 `update test set count = count - 1 where id = 2`

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
php 无法加载mysql的module的时候的配置的解决方案引发的思考
Jan 27 PHP
php中判断文件空目录是否有读写权限的函数代码
Aug 07 PHP
php二维数组用键名分组相加实例函数
Nov 06 PHP
php通过数组实现多条件查询实现方法(字符串分割)
May 06 PHP
20个2014年最优秀的PHP框架回顾
Oct 22 PHP
php实现上传图片保存到数据库的方法
Feb 11 PHP
php使用cookie实现记住登录状态
Apr 27 PHP
PHP常见数组函数用法小结
Mar 21 PHP
php实现头像上传预览功能
Apr 27 PHP
浅谈PHP中的面向对象OOP中的魔术方法
Jun 12 PHP
完美的php分页类
Oct 24 PHP
php常用日期时间函数实例小结
Jul 04 PHP
Ubuntu 16.04中Laravel5.4升级到5.6的步骤
Dec 07 #PHP
PHP ajax+jQuery 实现批量删除功能实例代码小结
Dec 06 #PHP
PHP实现简易计算器功能
Aug 28 #PHP
laravel5实现微信第三方登录功能
Dec 06 #PHP
PHP实现简单计算器小程序
Aug 28 #PHP
ThinkPHP 3.2.3实现加减乘除图片验证码
Dec 05 #PHP
php实现算术验证码功能
Dec 05 #PHP
You might like
使用VisualStudio开发php的图文设置方法
2010/08/21 PHP
php中使用addslashes函数报错问题的解决方法
2013/02/06 PHP
PHP防止post重复提交数据的简单例子
2014/06/07 PHP
PHP中使用BigMap实例
2015/03/30 PHP
PHP对称加密算法(DES/AES)类的实现代码
2017/11/14 PHP
Javascript日期对象的dateAdd与dateDiff方法
2008/11/18 Javascript
js实现对table动态添加、删除和更新的方法
2015/02/10 Javascript
jQuery插件Skippr实现焦点图幻灯片特效
2015/04/12 Javascript
jQuery实现鼠标双击Table单元格变成文本框及输入内容后更新到数据库的方法
2015/11/25 Javascript
JavaScript中解决多浏览器兼容性23个问题的快速解决方法
2016/05/19 Javascript
JS检测页面中哪个HTML标签触发点击事件的方法
2016/06/17 Javascript
JavaScript DOM节点操作方法总结
2016/08/23 Javascript
谈谈JS中常遇到的浏览器兼容问题和解决方法
2016/12/17 Javascript
解决Vue动态加载本地图片问题
2019/10/09 Javascript
vue2.0 获取从http接口中获取数据,组件开发,路由配置方式
2019/11/04 Javascript
js实现详情页放大镜效果
2020/10/28 Javascript
[01:39]2014DOTA2国际邀请赛 Newbee经理CU专访队伍火力全开
2014/07/15 DOTA
[00:37]DOTA2上海特级锦标赛 OG战队宣传片
2016/03/03 DOTA
[55:32]2018DOTA2亚洲邀请赛 4.4 淘汰赛 EG vs LGD 第二场
2018/04/05 DOTA
举例讲解Python中的list列表数据结构用法
2016/03/12 Python
python+opencv实现动态物体追踪
2018/01/09 Python
python检索特定内容的文本文件实例
2018/06/05 Python
Python Selenium截图功能实现代码
2020/04/26 Python
Python抖音快手代码舞(字符舞)的实现方法
2021/02/07 Python
利用纯css3实现的文字亮光特效的代码演示
2014/11/27 HTML / CSS
用CSS3的box-reflect来制作倒影效果
2016/11/15 HTML / CSS
到底Java是如何传递参数的?是by value或by reference?
2012/07/13 面试题
点菜员岗位职责范本
2014/02/14 职场文书
汇源肾宝广告词
2014/03/20 职场文书
班级年度安全计划书
2014/05/01 职场文书
化妆品活动策划方案
2014/05/23 职场文书
求职信范文大全
2014/05/26 职场文书
机械工程及其自动化专业求职信
2014/08/08 职场文书
2014物价局民主生活会对照检查材料思想汇报
2014/09/24 职场文书
法院个人总结
2015/03/03 职场文书
html5调用摄像头截图功能
2022/01/18 Javascript