码迷,mamicode.com
首页 > 数据库 > 详细

php当数据库update遇到并发,一个小坑

时间:2014-09-26 21:23:08      阅读:250      评论:0      收藏:0      [点我收藏+]

标签:blog   http   io   os   使用   ar   java   strong   文件   

有一个答题的小项目,表的字段如下:

id 用户id
times 答题次数
questions 回答的问题id,这是一个php serialize()的字符串

当用户答完题像后端提交结果时,构建的post包如下:

{
    ‘id‘:1,
    ‘questionid‘:38
}

后台逻辑如下:

$user = User::findFirst("id = ?1",array(‘id‘=>$_POST[‘id‘]));//取得用户模型
$questions = unserialize($user->questions);//反序列化
$questionid = (int)$_POST[‘questionid‘];//题目id
if ( ! in_array($questionid,$questions)) {//判断用户是否答过,没有答过则将题目id添加到questions字段里
    $questions[] = $questionid;
    $user->times += 1;//统计答题次数
    $user->questions = serialize($questions);//序列化questions
    $user->save();//update
} else {
    exit(‘对不起,这个题目你已经答过‘);
}

看上去流程没有问题啊,用户每次提交的时候会先读取自己的数据,检测是否答过这个题目,答过则直接退出。

但是出问题了:
当活动推出的第二天,后台数据发现有个用户的times答题次数和questions记录数不匹配,表现为 times=120 count(unserialize(questions))=17。
当时很奇怪,代码正常流程是没问题的,卡了半天,一直没想出来用户如何造成这种数据的,直到刚才,终于开窍,正如题目中所说,问题出在并发上,这个用户可能自己写脚本或者借助第三方工具,模拟post请求,并使用多线程等技术造成并发请求,我们来模拟一下并发过程:

用户同时发起两个请求,php也就分配了两个进程来处理,当着两个进程几乎同时执行了这行代码:

$user = User::findFirst("id = ?1",array(‘id‘=>$_POST[‘id‘]));//取得用户模型

此时在两个进程中的$user数据是一样的,我们假设一下,此时$user->questions 的值为 {1,2}。

假设两个请求构建的post数据包分别为:

{
    ‘id‘:1,
    ‘questionid‘:3
}

{
    ‘id‘:1,
    ‘questionid‘:4
}

那么当代码执行到:

in_array($questionid,$questions)

的时候实际上返回的都是 false
他们相继发起了 $user->save() ,分别希望将 3 和 4 加进questions,注意,这时候questions在数据库中的值还是{1,2},而sql语句是一条条执行的,也就是说数据库按顺序执行了两次update操作,先是将questions的值更改为 {1,2,3},随即第二个save()执行又将其改为了{1,2,4},两次times都增加1,所以最终的结果时:
times questions
4 {1,2,4}

这时候如果用户继续提交 ‘questionid‘:3 ,则又变更为:
times questions
5 {1,2,3,4}

这是两个并发的情况,会造成times比questions的数量多 1
如果模拟10个并发呢?不想算了。

问题既然已经出现了,还是得解决啊,首先想到用innodb的行级锁,很遗憾,表是myisam…

最终的解决方案是通过操作系统文件排他锁实现的,屏幕前的朋友如果有兴趣可以看:
http://my.oschina.net/cxz001/blog/281130

php当数据库update遇到并发,一个小坑

标签:blog   http   io   os   使用   ar   java   strong   文件   

原文地址:http://my.oschina.net/cxz001/blog/321061

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!