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数据库操作分页类
Jun 04 PHP
php截取utf-8中文字符串乱码的解决方法
Mar 29 PHP
Warning: session_destroy() : Trying to destroy uninitialized sessionq错误
Jun 16 PHP
PHP写的获取各搜索蜘蛛爬行记录代码
Aug 21 PHP
用PHP编写和读取XML的几种方式
Jan 12 PHP
Laravel中使用自己编写类库的3种方法
Feb 10 PHP
thinkPHP下ueditor的使用方法详解
Dec 26 PHP
PHP数组实例详解
Jun 26 PHP
PHP数组操作简单案例分析
Oct 15 PHP
Yii框架分页实现方法详解
May 20 PHP
PHP基于Closure类创建匿名函数的方法详解
Aug 17 PHP
详解PHP 7.4 中数组延展操作符语法知识点
Jul 19 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中XMLHttpRequest(Ajax)不能设置自定义的Referer的解决方法
2011/11/26 PHP
php微信支付之APP支付方法
2015/03/04 PHP
PHP结合jQuery.autocomplete插件实现输入自动完成提示的功能
2015/04/27 PHP
PHP获取Exif缩略图的方法
2015/07/13 PHP
php实现分页显示
2015/11/03 PHP
PHP页面输出搜索后跳转下一页的处理方法
2016/09/30 PHP
php实现根据身份证获取精准年龄
2020/02/26 PHP
一些Javascript的IE和Firefox(火狐)兼容性的问题总结及常用例子
2009/05/21 Javascript
JQuery打造PHP的AJAX表单提交实例
2009/11/03 Javascript
jQuery1.6 类型判断实现代码
2011/09/01 Javascript
jquery easyui中treegrid用法的简单实例
2014/02/18 Javascript
js中flexible.js实现淘宝弹性布局方案
2020/06/23 Javascript
Nodejs学习item【入门手上】
2016/05/05 NodeJs
jQuery实现边框动态效果的实例代码
2016/09/23 Javascript
原生JS获取元素集合的子元素宽度实例
2016/12/14 Javascript
vue App.vue中的公共组件改变值触发其他组件或.vue页面监听
2019/05/31 Javascript
非常漂亮的js烟花效果
2020/03/10 Javascript
vue 动态设置img的src地址无效,npm run build 后找不到文件的解决
2020/07/26 Javascript
Vue-Ant Design Vue-普通及自定义校验实例
2020/10/24 Javascript
Python translator使用实例
2008/09/06 Python
python小技巧之批量抓取美女图片
2014/06/06 Python
Scrapy爬虫实例讲解_校花网
2017/10/23 Python
[原创]教女朋友学Python3(二)简单的输入输出及内置函数查看
2017/11/30 Python
Python常用模块sys,os,time,random功能与用法实例分析
2020/01/07 Python
python编写俄罗斯方块
2020/03/13 Python
sklearn线性逻辑回归和非线性逻辑回归的实现
2020/06/09 Python
基于python实现复制文件并重命名
2020/09/16 Python
使用CSS3美化HTML表单的技巧演示
2016/05/17 HTML / CSS
关联、聚合(Aggregation)以及组合(Composition)的区别
2012/02/29 面试题
Java Servlet的主要功能和作用是什么
2014/02/14 面试题
自荐信的禁忌和要点
2013/10/15 职场文书
大学新生军训自我鉴定
2014/09/18 职场文书
解除施工合同协议书
2014/10/17 职场文书
2015年考研复习计划
2015/01/19 职场文书
Python初识逻辑与if语句及用法大全
2021/08/07 Python
td 内容自动换行 table表格td设置宽度后文字太多自动换行
2022/12/24 HTML / CSS