PHP使用数据库的并发问题
作者:网络转载 发布时间:[ 2015/8/21 13:52:51 ] 推荐标签:数据库
在并行系统中并发问题永远不可忽视。尽管PHP语言原生没有提供多线程机制,那并不意味着所有的操作都是线程安全的。尤其是在操作诸如订单、支付等业务系统中,更需要注意操作数据库的并发问题。
接下来我通过一个案例分析一下PHP操作数据库时并发问题的处理问题。
首先,我们有这样一张数据表:
1 mysql> select * from counter;
2 +----+-----+
3 | id | num |
4 +----+-----+
5 | 1 | 0 |
6 +----+-----+
7 1 row in set (0.00 sec)
这段代码模拟了一次业务操作:
1 <?php
2 function dummy_business() {
3 $conn = mysqli_connect('127.0.0.1', 'public', 'public') or die(mysqli_error());
4 mysqli_select_db($conn, 'test');
5 for ($i = 0; $i < 10000; $i++) {
6 mysqli_query($conn, 'UPDATE counter SET num = num + 1 WHERE id = 1');
7 }
8 mysqli_close($conn);
9 }
10
11 for ($i = 0; $i < 10; $i++) {
12 $pid = pcntl_fork();
13
14 if($pid == -1) {
15 die('can not fork.');
16 } elseif (!$pid) {
17 dummy_business();
18 echo 'quit'.$i.PHP_EOL;
19 break;
20 }
21 }
22 ?>
上面的代码模拟了10个用户同时并发执行一项业务的情况,每次业务操作都会使得num的值增加1,每个用户都会执行10000次操作,终num的值应当是100000。
运行这段代码,num的值和我们预期的值是一样的:
1 mysql> select * from counter;
2 +----+--------+
3 | id | num |
4 +----+--------+
5 | 1 | 100000 |
6 +----+--------+
7 1 row in set (0.00 sec)
这里不会出现问题,是因为单条UPDATE语句操作是原子的,无论怎么执行,num的值终都会是100000。
然而很多情况下,我们业务过程中执行的逻辑,通常是先查询再执行,并不像上面的自增那样简单:
1 <?php
2 function dummy_business() {
3 $conn = mysqli_connect('127.0.0.1', 'public', 'public') or die(mysqli_error());
4 mysqli_select_db($conn, 'test');
5 for ($i = 0; $i < 10000; $i++) {
6 $rs = mysqli_query($conn, 'SELECT num FROM counter WHERE id = 1');
7 mysqli_free_result($rs);
8 $row = mysqli_fetch_array($rs);
9 $num = $row[0];
10 mysqli_query($conn, 'UPDATE counter SET num = '.$num.' + 1 WHERE id = 1');
11 }
12 mysqli_close($conn);
13 }
14
15 for ($i = 0; $i < 10; $i++) {
16 $pid = pcntl_fork();
17
18 if($pid == -1) {
19 die('can not fork.');
20 } elseif (!$pid) {
21 dummy_business();
22 echo 'quit'.$i.PHP_EOL;
23 break;
24 }
25 }
26 ?>

sales@spasvo.com