Maison >développement back-end >tutoriel php >问一个mysql并发问题?

问一个mysql并发问题?

WBOY
WBOYoriginal
2016-06-06 20:18:581151parcourir

业务逻辑是这样的:

统计某一文章每一天的获赞次数:
tb(id,time,num)
id:文章ID
time:2016-1-31

id,time是复合主键

业务流程:

<code>sql_1:select * from tb where id=$id and time=$t
if (sql_1)
    sql_2:updata from tb set num = num + 1 where id=$id and time=$t
else
    sql_2_:INSERT INTO tb (id,time,num) values ($id,$t,1)
</code>

就是这样的很简单逻辑,本来没有什么问题,可是最近老是出错,原因是因为插入重复的复合主键,我才知道这样设计有并发的问题(两个进程同时判断通过,就会导致sql_2_执行两次而出错);

网上有很多讲这样的知识的,乐观锁,悲观锁等,但是都是将一些理论知识,一遇到实际问题,就没人理你了,书本上也基本是理论知识,没有实际问题的解决方法,我没有经验,不知道怎么解决这个问题,不想用乐观锁,需要要建一个version字段觉得不爽,程序多个地方用到时还不好控制。

意识到这个问题之后,我发现我之前所有的代码其实都会有这个问题,我相信上面这个逻辑是个非常基本的逻辑,我没想到会有这样的问题,好恐怖;

如果需要用到事务,我就把表全改为innodb的;

希望高人指点,就我上面的例子说实际的解决办法,不要找一些理论知识我看了。

谢谢大家了。

回复内容:

业务逻辑是这样的:

统计某一文章每一天的获赞次数:
tb(id,time,num)
id:文章ID
time:2016-1-31

id,time是复合主键

业务流程:

<code>sql_1:select * from tb where id=$id and time=$t
if (sql_1)
    sql_2:updata from tb set num = num + 1 where id=$id and time=$t
else
    sql_2_:INSERT INTO tb (id,time,num) values ($id,$t,1)
</code>

就是这样的很简单逻辑,本来没有什么问题,可是最近老是出错,原因是因为插入重复的复合主键,我才知道这样设计有并发的问题(两个进程同时判断通过,就会导致sql_2_执行两次而出错);

网上有很多讲这样的知识的,乐观锁,悲观锁等,但是都是将一些理论知识,一遇到实际问题,就没人理你了,书本上也基本是理论知识,没有实际问题的解决方法,我没有经验,不知道怎么解决这个问题,不想用乐观锁,需要要建一个version字段觉得不爽,程序多个地方用到时还不好控制。

意识到这个问题之后,我发现我之前所有的代码其实都会有这个问题,我相信上面这个逻辑是个非常基本的逻辑,我没想到会有这样的问题,好恐怖;

如果需要用到事务,我就把表全改为innodb的;

希望高人指点,就我上面的例子说实际的解决办法,不要找一些理论知识我看了。

谢谢大家了。

实名反对以上答案!!!
mysql:
1.ON DUPLICATE KEY UPDATE语句
ID,time复合主键

<code>INSERT INTO tb (id,time,num) values ($id,$t,1) ON DUPLICATE KEY UPDATE num=num+1;</code>

2.悲观锁for update

<code>select * from tb where id=$id and time=$t for update
if (sql_1)
    sql_2:updata from tb set num = num + 1 where id=$id and time=$t
else
    sql_2_:INSERT INTO tb (id,time,num) values ($id,$t,1)</code>

没人回答么?

这个可以在转发mysql请求,使用memcache独占锁,

最简单的办法是将任务全部放到队列中去,然后使用单线程不断处理这个任务队列中的任务,这样解决了并发问题,但是效率就变低了。

我在用多线程写文件的时候也遇到过多线程同时写文件的问题,这个时候用线程锁(Python):

<code>import threading
lock = threading.Lock()
with lock:
    # done something</code>

使用redis实现分布式锁,锁的位置如下
lock
sql_1:select * from tb where id=$id and time=$t
if (sql_1)

<code>sql_2:updata from tb set num = num + 1 where id=$id and time=$t</code>

else

<code>sql_2_:INSERT INTO tb (id,time,num) values ($id,$t,1)</code>

unlock

我说过 我去年看过的一解决办法,我举个不切实际(ps:但是我觉得这个解决办法还是蛮好的,你可以去看一下《redis实战》这本书)的个例子,你创建100条数据,关联一篇新的文章,然后如果有更新,随机的更新1-100的数据 +1 。等到了一个合适的时候,把这100条合并成1条

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn