问题描述
假设一个情况,这里只是假设,真实的情况可能不会这样设计,但是假如真的发生了....
铁老大有一张这样的ticket表,用来存放北京到上海的票。
id |
uid |
start_addr |
end_addr |
book_time |
1 |
1300009860 |
上海 |
北京 |
13866660321 |
2 |
0 |
上海 |
北京 | |
3 |
0 |
上海 |
北京 | |
4 |
0 |
上海 |
北京 |
uid 不等于0 并且 book_time 不等于0,则表示已售出,不许再更改。
年底了,大家开始抢了,当某一个人开始抢的时候,铁老大这样做了,当A开始抢的时候,从表中所有uid=0的记录中随机取出一条,然后修改当前记录的uid为A的uid,book_time为当前时间。
那么好了,这个时候并发产生了,10000个人同时抢最后剩下的10张票,有2个人A,B同时读取了uid=0 and id=2的记录,然后A把uid修改为自己的,book_time修改timeA,系统通知A抢票成功 紧接着,B同样修改了这条记录为自己的,那么这个时候A的票就失效了。
这里就考虑使用锁机制了,如果在A读取了这条记录后,并把这条记录加上写锁,那么别人就无法再读取加锁,也就不会修改多次了。
实验结果
实验了一下for update 加锁,然后发现了一些问题。
1、当我们使用主键查询去加锁的时候,可以对这条记录加行锁
打开一个终端:执行一下代码:
1
2
3
|
mysql>
begin
;
Query OK, 0
rows
affected (0.00 sec)
mysql>
select
*
from
ticket
where
id = 2
for
update
;
|
那么 id=2这个记录就会被加行写锁。
令一个终端将无法读取该记录,但是却可以读取其他记录
1
2
3
4
|
#可以读取加锁
mysql>
select
*
from
ticket
where
id = 3
for
update
;
#不可以读取
mysql>
select
*
from
ticket
where
id = 2
for
update
;
|
2、但是当我们按非主键查询会怎样呢
1
2
3
|
mysql>
begin
;
Query OK, 0
rows
affected (0.00 sec)
mysql>
select
*
from
ticket
where
uid = 0 limit 1
for
update
;
|
那么这个时候,这个表会被锁住,任何记录都不能再读取加锁
1
2
3
4
|
//不可读取,等待释放锁
mysql>
select
*
from
ticket
where
id = 1
for
update
;
//不可读取等待释放锁
mysql>
select
*
from
ticket
where
uid = 0 limit 1
for
update
;
|
但是表锁会减少并发。
结论
有没有一种方法,可以在非主键字段查询一条记录时上实现加行写锁,期待高手支招。
本文转自shayang8851CTO博客,原文链接:http://blog.51cto.com/janephp/1352679,如需转载请自行联系原作者