Heim >Backend-Entwicklung >PHP-Tutorial >问一个mysql并发问题?

问一个mysql并发问题?

WBOY
WBOYOriginal
2016-06-06 20:18:581151Durchsuche

业务逻辑是这样的:

统计某一文章每一天的获赞次数:
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条

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn