mysql并发问题, select 50条记录,然后再update这50记录,标记这50条记录状态为已获取

踏雪听雨 2015-10-16 10:13:58
伪代码如下:


begin transaction

select * from table where state=0 limit 1, 50

update table set state=1, getuser=5 where id in (上面50条记录的id号)

comit transaction


在并发的情况下,要保证每个用户的逻辑都是正确的,上面的事务可行吗?
需求就是:获取50条未读取的记录(state=0),然后取完了并更新state=1,getuser=5(指哪个用户获取了),而这个表是所有用户都需要获取的,
就像是这个表保存了未完成的任务,等大家来获取这些任务。
...全文
410 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
LongRui888 2015-10-16
  • 打赏
  • 举报
回复
引用 11 楼 SaRoot 的回复:
比如:两个用户同时到了select,此时都 还没有执行update, 那么这个时候两个用户取的都是1-50条记录,然后用户a执行update,那么用户b再执行update时就等待, 那么此时的update语句是正确的,因为用户b执行update时影响行数为0, 但是这个时候,用户a,用户b都获取的是1-50条记录,也就是返回到前台的时候重复了。
不会出现这种情况,我已经实际验证过,第二个会话会更新接下去的50条数据,因为前50条数据已经修改过了。 这里的关键就是update 和select 合成一个sql语句,而不是分开,如果分开了,就会出现问题。
踏雪听雨 2015-10-16
  • 打赏
  • 举报
回复
比如:两个用户同时到了select,此时都 还没有执行update, 那么这个时候两个用户取的都是1-50条记录,然后用户a执行update,那么用户b再执行update时就等待, 那么此时的update语句是正确的,因为用户b执行update时影响行数为0, 但是这个时候,用户a,用户b都获取的是1-50条记录,也就是返回到前台的时候重复了。
吉普赛的歌 2015-10-16
  • 打赏
  • 举报
回复
引用 9 楼 SaRoot 的回复:
[quote=引用 7 楼 yupeigu 的回复:] 我做了实验,通过实验也证实了这一点,当并发update时,会有阻塞,等提交后,会继续更新,且第一个会话更新了前1-50条数据,后一个回话更新了50-100条记录。
但是这能保证,并发时,第一个用户取的是1-50条记录,第二个用户取的是50-100条记录吗? 因为这两次记录获取出来,也要保证用户都有对应的,且不跟其它用户重复的记录。[/quote] 你想多了, 人家的做法是对的, 不需要另外自己写事务
踏雪听雨 2015-10-16
  • 打赏
  • 举报
回复
引用 7 楼 yupeigu 的回复:
我做了实验,通过实验也证实了这一点,当并发update时,会有阻塞,等提交后,会继续更新,且第一个会话更新了前1-50条数据,后一个回话更新了50-100条记录。
但是这能保证,并发时,第一个用户取的是1-50条记录,第二个用户取的是50-100条记录吗? 因为这两次记录获取出来,也要保证用户都有对应的,且不跟其它用户重复的记录。
踏雪听雨 2015-10-16
  • 打赏
  • 举报
回复
begin transaction select * from table where state=0 limit 1, 50 update table set state=1, getuser=5 where id in (上面50条记录的id号) comit transaction 上面的select会锁表吗?
LongRui888 2015-10-16
  • 打赏
  • 举报
回复
我做了实验,通过实验也证实了这一点,当并发update时,会有阻塞,等提交后,会继续更新,且第一个会话更新了前1-50条数据,后一个回话更新了50-100条记录。
LongRui888 2015-10-16
  • 打赏
  • 举报
回复
引用 5 楼 SaRoot 的回复:
[quote=引用 4 楼 yupeigu 的回复:] [quote=引用 2 楼 SaRoot 的回复:] [quote=引用 1 楼 rucypli 的回复:] update table set state=1, getuser=5 where state=0 limit 50;
这个会不会出现,比如两个并发同时进来都获取了同样的50条记录,导致相同的50条记录被两个用户取走了。[/quote] 不会的,因为你是upadte语句,是要修改数据的,是独占锁,相同的记录只能由一个会话锁定。[/quote] 那这样,是不是两个用户并发时,会同时获取到相同的50条记录,但其中一个用户更新state成功了,另一个用户更新state失败了?[/quote] 不会失败,只是等待,因为大家都是要获取前50条记录,一个会话先获得了50条记录,并且锁住了,那么另一个回话就等待,表现为阻塞住了,当第一个会话更新完后,第二个会话就能运行了,那么他再次获取的50条记录,因为记录已经变化,所以他就获取相当于一开始的50-100的50的记录。
踏雪听雨 2015-10-16
  • 打赏
  • 举报
回复
引用 4 楼 yupeigu 的回复:
[quote=引用 2 楼 SaRoot 的回复:] [quote=引用 1 楼 rucypli 的回复:] update table set state=1, getuser=5 where state=0 limit 50;
这个会不会出现,比如两个并发同时进来都获取了同样的50条记录,导致相同的50条记录被两个用户取走了。[/quote] 不会的,因为你是upadte语句,是要修改数据的,是独占锁,相同的记录只能由一个会话锁定。[/quote] 那这样,是不是两个用户并发时,会同时获取到相同的50条记录,但其中一个用户更新state成功了,另一个用户更新state失败了?
LongRui888 2015-10-16
  • 打赏
  • 举报
回复
引用 2 楼 SaRoot 的回复:
[quote=引用 1 楼 rucypli 的回复:] update table set state=1, getuser=5 where state=0 limit 50;
这个会不会出现,比如两个并发同时进来都获取了同样的50条记录,导致相同的50条记录被两个用户取走了。[/quote] 不会的,因为你是upadte语句,是要修改数据的,是独占锁,相同的记录只能由一个会话锁定。
LongRui888 2015-10-16
  • 打赏
  • 举报
回复
写出一个语句:
begin transaction
 
update table set state=1, getuser=5 where state=0  limit 1, 50
 
commit transaction
踏雪听雨 2015-10-16
  • 打赏
  • 举报
回复
引用 1 楼 rucypli 的回复:
update table set state=1, getuser=5 where state=0 limit 50;
这个会不会出现,比如两个并发同时进来都获取了同样的50条记录,导致相同的50条记录被两个用户取走了。
rucypli 2015-10-16
  • 打赏
  • 举报
回复
update table set state=1, getuser=5 where state=0 limit 50;
道玄希言 2015-10-16
  • 打赏
  • 举报
回复
主要是想实现,用户取到50条,就只更新这50条记录,并且这个用户在客户端看到的也是这50条记录。 用户B怎么办? 在用户B去更新的时候,当前用户B取到的纪录,实际上,已经被用户A修改了,你是要更新还是不再更新? 如果用户A修改了,B就不用再去修改了, 也不能改别的纪录, 那你这么写是可以的。 因为 AND state=0 条件的限制, 实际上 state 纪录值已经被A改成 1 了。 如果A修改后, B还需要对之前取到的数据进行修改, 那就不能添加 AND state=0 这部分。 更新都会给纪录加锁的, 不需要考虑单纯的 update 语句在并发时是否会冲突的问题了。 并发插入时,原则上数据表有一个读锁时,其它进程无法对此表进行更新操作,但在一定条件下,MyISAM表也支持查询和插入操作的并发进行。   MyISAM存储引擎有一个系统变量concurrent_insert,专门用以控制其并发插入的行为,其值分别可以为0、1或2。   a、当concurrent_insert设置为0时,不允许并发插入。   b、当concurrent_insert设置为1时,如果MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从表尾插入记录。这也是MySQL的默认设置。   c、当concurrent_insert设置为2时,无论MyISAM表中有没有空洞,都允许在表尾并发插入记录
踏雪听雨 2015-10-16
  • 打赏
  • 举报
回复
改成下面的代码是否有问题 ?在sqlyog中测试在不同会话中进行,第二个会员update影响行数为0行。

BEGIN trans
   SELECT * FROM table WHERE state=0 LIMIT 3  (这里取出的id=1,2,3)
   
   UPDATE table SET state = 1,getuser='aaa111' WHERE id IN (1,2,3) AND state=0
 
COMMIT trans
主要是想实现,用户取到50条,就只更新这50条记录,并且这个用户在客户端看到的也是这50条记录。 另一个用户B,也是这样,如果直接update table set state=1 where state=0 limit 50的话,那这个用户B 查询出来的是1-50条,看到的也是1-50条,但更新的即是50-100条了。
道玄希言 2015-10-16
  • 打赏
  • 举报
回复
你应该这么写: begin transaction select * from table where state=0 FOR UPDATE limit 1, 50 update table set state=1, getuser=5 where id in (上面50条记录的id号) and state=0 comit transaction FOR UPDATE 在读取行上设置一个排他锁 。阻止其他 session 读取或者写入行数据. 实现大家都可以读取到数据,但是这样的话,对这50条数据,则我先修改了, 你就不能改了。 比如仓库领料, 几个都在领料,大家都同时锁定了同一个货物, 该货物的库存只有10, 在开始显示的时候,可能大家都显示出有货10个, 但是当最先提交的领走了这10个, 后边的再提交就会提示没货了。 也跟火车抢票差不多。。。 如果只是单纯的需要修改50行字段,而不用知道到底哪50行,就是版主说的 只需要 update table set state=1, getuser=5 where state=0 limit 50; 即 标记可以更新,那就更新 50 个吧。 这里好比分果果,参与分果果的站一排,两人拿着果果,同时从一边开始来分, 分果果的两人,每人每次能分5人,那A先分了5个,B就接着A分完的位置 (6) 开始就是了, 而不用纠结A或者B到底需要分给谁。

56,679

社区成员

发帖
与我相关
我的任务
社区描述
MySQL相关内容讨论专区
社区管理员
  • MySQL
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧