数据库并发处理
使用 Apache Bench 来模拟并发,使用如下命令:
$ ab -c 100 -n 1000 http://127.0.0.1/a.php
-c 100 -n 1000
表示一共有1000个请求,每次并发请求100个。
Mysql
我们模拟一次投票,表结构只有一个vote字段,用来记录投票数,每次运行脚本该字段加1:
Mysql的update语句可以保证并发数据的一致性,代码如下:
mysql_query('UPDATE table1 SET vote=vote+1 WHERE id=1',$con);
然后使用 Apache Bench
模拟并发:
$ ab -c 100 -n 1000 http://127.0.0.1/a.php
查看数据库结果:
可以看到数据为1000,和我们的请求数 -n 1000
一致
接下来我们先读取vote值然后再调用update语句进行加1操作:
$res=mysql_query('SELECT vote FROM table1 WHERE id=1',$con);
$res=mysql_fetch_array($res);
mysql_query('update table1 set vote='.$res['vote'].'+1 where id=1',$con);
用ab模拟并发后查看数据:
结果很明显,使用这种方式在并发下数据的不一致性就发生了。
解决这个问题的一种方法是使用 事务
+ InnoDB的行锁
, InnoDB的行锁有两种方式: 读共享锁
和 写独占锁
读共享锁是通过下面这样的SQL获得的:
SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;
如果事务A获得了先获得了读共享锁,那么事务B之后仍然可以读取加了读共享锁的行数据,但必须等事务A commit或者roll back之后才可以更新或者删除加了读共享锁的行数据。
写独占锁是通过SELECT...FOR UPDATE获得:
SELECT counter_field FROM child_codes FOR UPDATE; UPDATE child_codes SET counter_field = counter_field + 1;
如果事务A先获得了某行的写独占锁,那么事务B就必须等待事务A commit或者roll back之后才可以访问行数据。
这里说明一下,MyISAM只支持表锁,而InnoDB支持表锁和行锁。
所以要解决这个问题,我们使用写独占锁,代码如下:
mysql_query("SET AUTOCOMMIT=0",$con);
mysql_query('BEGIN',$con);
$res=mysql_query('SELECT vote FROM table1 WHERE id=1 FOR UPDATE',$con);
$res=mysql_fetch_array($res);
mysql_query('UPDATE table1 SET vote="'.($res['vote']+1).'" WHERE id=1',$con);
mysql_query('COMMIT',$con);
并发后结果为1000,说明正确:
当然,也可以使用表锁:
mysql_query("SET AUTOCOMMIT=0",$con);
mysql_query('BEGIN',$con);
mysql_query('LOCK TABLES table1 WRITE',$con);
$res=mysql_query('SELECT vote FROM table1 WHERE id=1',$con);
$res=mysql_fetch_array($res);
mysql_query('UPDATE table1 SET vote="'.($res['vote']+1).'" WHERE id=1',$con);
mysql_query('UNLOCK TABLES',$con);
mysql_query('COMMIT',$con);
其他参考:
《为家而战》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/14939.html