> 백엔드 개발 > PHP 튜토리얼 > 问一个mysql并发问题?

问一个mysql并发问题?

WBOY
풀어 주다: 2016-06-06 20:18:58
원래의
1156명이 탐색했습니다.

业务逻辑是这样的:

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

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿