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详细彻底学习Smarty
Mar 27 PHP
php mysql索引问题
Jun 07 PHP
解决了Ajax、MySQL 和 Zend Framework 的乱码问题
Mar 03 PHP
destoon会员注册提示“数据校验失败(2)”解决方法
Jun 21 PHP
thinkphp使用phpmailer发送邮件的方法
Nov 24 PHP
php实现两表合并成新表并且有序排列的方法
Dec 05 PHP
php在数组中查找指定值的方法
Mar 17 PHP
帝国CMS留言板回复后发送EMAIL通知客户
Jul 06 PHP
[原创]ThinkPHP中SHOW_RUN_TIME不能正常显示运行时间的解决方法
Oct 10 PHP
php连接微软MSSQL(sql server)完全攻略
Nov 27 PHP
PHP的简单跳转提示的实现详解
Mar 14 PHP
phpStudy vscode 搭建debug调试的教程详解
Jul 28 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
php GeoIP的使用教程
2011/03/09 PHP
thinkphp中空模板与空模块的用法实例
2014/11/26 PHP
Smarty最简单实现列表奇偶变色的方法
2015/07/01 PHP
php+jQuery递归调用POST循环请求示例
2016/10/14 PHP
YII2框架中ActiveDataProvider与GridView的配合使用操作示例
2020/03/18 PHP
jquery api参考 visualjquery 中国线路 速度快
2007/11/30 Javascript
JavaScript获取指定元素位置的方法
2015/04/08 Javascript
7个有用的jQuery代码片段分享
2015/05/19 Javascript
jQuery检测返回值的数据类型
2015/07/13 Javascript
js+css实现超简洁的二级下拉菜单效果代码
2015/09/07 Javascript
javascript的 {} 语句块详解
2016/02/27 Javascript
Ionic如何实现下拉刷新与上拉加载功能
2016/06/03 Javascript
JavaScript常用代码书写规范的超全面总结
2016/09/11 Javascript
微信小程序picker组件简单用法示例【附demo源码下载】
2017/12/05 Javascript
JS中的事件委托实例浅析
2018/03/22 Javascript
Vue.js@2.6.10更新内置错误处机制Fundebug同步支持相应错误监控
2019/05/13 Javascript
vue将后台数据时间戳转换成日期格式
2019/07/31 Javascript
Vue.js自定义指令学习使用详解
2019/10/19 Javascript
Python遍历目录的4种方法实例介绍
2015/04/13 Python
使用Python进行二进制文件读写的简单方法(推荐)
2016/09/12 Python
分享6个隐藏的python功能
2017/12/07 Python
快速查询Python文档方法分享
2017/12/27 Python
python学生信息管理系统
2018/03/13 Python
python中for用来遍历range函数的方法
2018/06/08 Python
python编写简易聊天室实现局域网内聊天功能
2018/07/28 Python
Python获取对象属性的几种方式小结
2020/03/12 Python
Python 实现自动完成A4标签排版打印功能
2020/04/09 Python
Python 执行矩阵与线性代数运算
2020/08/01 Python
Python结合Window计划任务监测邮件的示例代码
2020/08/05 Python
python自动提取文本中的时间(包含中文日期)
2020/08/31 Python
自学考试自我鉴定范文
2013/09/26 职场文书
小学综治宣传月活动总结
2014/07/02 职场文书
学前教育专业求职信
2014/09/02 职场文书
升学宴学生答谢词
2015/01/05 职场文书
酒店圣诞节活动总结
2015/05/06 职场文书
穷人该怎么创业?谨记以下几点
2019/07/11 职场文书