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新建文件自动编号的思路与实现
Jun 27 PHP
PHP提取字符串中的图片地址[正则表达式]
Nov 12 PHP
PHP小教程之实现链表
Jun 09 PHP
php CI框架插入一条或多条sql记录示例
Jul 29 PHP
php采用curl模仿登录人人网发布动态的方法
Nov 07 PHP
PDO预处理语句PDOStatement对象使用总结
Nov 20 PHP
php的sso单点登录实现方法
Jan 08 PHP
laravel学习教程之存取器
Jul 30 PHP
PHP实现微信商户支付企业付款到零钱功能
Sep 30 PHP
PHP按一定比例压缩图片的方法
Oct 12 PHP
PHP 实现链式操作
Mar 09 PHP
thinkphp5 路由分发原理
Mar 18 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
php5 non-thread-safe和thread-safe这两个版本的区别分析
2010/03/13 PHP
与文件上传有关的php配置参数总结
2013/06/14 PHP
php遍历CSV类实例
2015/04/14 PHP
php实现的pdo公共类定义与用法示例
2017/07/19 PHP
070823更新的一个[消息提示框]组件 兼容ie7
2007/08/29 Javascript
Ajax,UTF-8还是GB2312 eval 还是execScript
2008/11/13 Javascript
深入理解javascript变量声明
2014/11/20 Javascript
生成二维码方法汇总
2014/12/26 Javascript
jquery图片轮播特效代码分享
2020/04/20 Javascript
深入解析JavaScript中函数的Currying柯里化
2016/03/19 Javascript
Nodejs中的this详解
2016/03/26 NodeJs
JS事件添加和移出的兼容写法示例
2016/06/20 Javascript
详解node中创建服务进程
2017/05/09 Javascript
微信小程序实时聊天WebSocket
2018/07/05 Javascript
解决vue-cli脚手架打包后vendor文件过大的问题
2018/09/27 Javascript
一文读懂ES7中的javascript修饰器
2019/05/06 Javascript
vue props 单项数据流实例分享
2020/02/16 Javascript
小程序点餐界面添加购物车左右摆动动画
2020/09/23 Javascript
[02:18]DOTA2英雄基础教程 育母蜘蛛
2014/01/20 DOTA
[45:56]Ti4正赛第一天 VG vs NEWBEE 3
2014/07/19 DOTA
用Python制作在地图上模拟瘟疫扩散的Gif图
2015/03/31 Python
Python 专题六 局部变量、全局变量global、导入模块变量
2017/03/20 Python
快速解决PyCharm无法引用matplotlib的问题
2018/05/24 Python
python实现爬取图书封面
2018/07/05 Python
pandas 使用均值填充缺失值列的小技巧分享
2019/07/04 Python
浅谈pycharm使用及设置方法
2019/09/09 Python
Python 输出详细的异常信息(traceback)方式
2020/04/08 Python
PyTorch之nn.ReLU与F.ReLU的区别介绍
2020/06/27 Python
Bibloo罗马尼亚网站:女装、男装、童装及鞋子和配饰
2019/07/20 全球购物
MATCHESFASHION澳大利亚/亚太地区:英国时尚奢侈品电商
2020/01/14 全球购物
反邪教宣传工作方案
2014/05/07 职场文书
四查四看自我剖析材料
2014/09/19 职场文书
党的群众路线教育实践活动对照检查材料(四风)
2014/09/27 职场文书
森马旗舰店双十一营销方案
2014/09/29 职场文书
分析Python list操作为什么会错误
2021/11/17 Python
Python OpenCV之常用滤波器使用详解
2022/04/07 Python