两个线程一起从表中取数据,表和 sql 如何设计呢?

141 天前
 qviqvi

一个任务表,两个线程同时每次取一条数据执行业务逻辑,然后把这条数据标记为完成或暂时放弃,暂时放弃的后面会被再次取出

要求同一条数据不能被多个线程同时取出

求优秀的设计方案

2026 次点击
所在节点    数据库
32 条回复
rekulas
141 天前
取数据的时候加行锁就行了
tclm
141 天前
主键是数值吗?确定只有两个线程吗?
如果都是的话,一个线程只处理单数,一个线程只处理双数。
echoless
141 天前
Vegetable
141 天前
1. 行内有字段能识别当前正在处理中
2. 取的时候加锁
gitdoit
141 天前
只有一个线程负责交互任务表, 其他线程再和这个线程交互
coder001
141 天前
搞个并发队列,不管是进程内还是进程外,工作线程去队列取工作项
主线程负责从数据库读出东西放进队列,工作线程拿到任务就先标记一下立即更新再开始执行,遇到放弃(需要再次执行)的状况主线程里再次拿出投进队列
test0x01
141 天前
一个单数 ID 一个双数 ID
MoYi123
141 天前
update table set status = 'work' where id = 1 and stats = 'ready'

然后看 update 的返回值是 0 还是 1 即可. 和 redis 的 setnx 差不多的做法.
rainxt
141 天前
正在做类似的需求,额外用了 redis 做消息队列,1 个任务往消息队列里写,几十个业务进程从消息队列里拉取任务,处理完回写数据库标记完成,消息队列里空的时候再去查数据库里还有没有要处理的往里加
Karte
141 天前
1. 创建一个带有并发控制所的队列.
2. 每个线程从该队列中获取数据.
3. 在获取数据时, 如果队列为空, 则由当前线程请求数据库, 批量读取数据丢入队列.
4. 数据暂时放弃时, 将数据丢入队列底部.

这样就好了. 在读取数据时通过锁做到数据获取无冲突, 而且不会有重复的数据从数据库中读取; 另一方面也降低了 IO 请求.

这个方案的缺点在获取数据时会有锁的存在, 如果数据处理比较快, 可能会遇到其他线程在等待其中一个线程读取数据库的返回.
8355
141 天前
1.最简单的单进程扫表推消息队列
2.update task set status=1 (运行中) where id = xx 根据影响行数判断是否哪个线程可以抢占执行中状态。
8355
141 天前
@8355 同 8l 少了待执行状态 一个意思
mifly
141 天前
可以利用数据库的 update 语句的锁来实现
生成唯一的 Lockkey
Update sometable ser lockkey=lockkey where ... Limit 10
Select ... From sometable where lockkey=lockkey
Karte
141 天前
方案二:

1. 创建一个对象, 对象记录一个查询位置, 查询数量.
2. 每个线程通过加锁读取该对象获取位置和数量, 并在获取后更新当前查询位置. 释放锁
3. 通过之前获取到的查询位置, 查询数量从数据库获取一段范围的数据.
4. 将数据丢入线程本地队列, 依次处理.
5. 对暂时放弃的数据 id 存入 步骤一 对象中. 采用 CopyOnWrite 方法更新对象值.

这个方案将查询 IO 放入到了每个线程, 线程只需要在获取当前处理位置时加锁就好了. 这样锁的时间十分短暂, 几乎可以做到 "无锁" 的情况. 但是需要注意线程数量, 查询数量参数; 尽可能的将线程数拉小, 查询数量拉大, 这样线程可以更专注处理数据, 而不是一直等待 IO 返回.
Karte
141 天前
方案三:
直接用现成的消息队列, 把要处理的消息丢到队列里. 消费者直接多线程从消息队列中获取数据处理. 这个开发方案是可以做到代码量最小.
CEBBCAT
141 天前
加锁哪有串行得劲,模仿 Redis 一致性哈希,加一列 int 给任务打标签,你干一三五,我干二四六,加一个 worker 捡漏。或者用 MQ 传递 ID ,让已经成熟的 MQ 来干这事


*然后发现让楼上抢答了,气😡!
nothingistrue
141 天前
楼主你要得,应该不只是「同一条数据不能被多个线程同时取出」,而且还要是,「一条数据被一个线程处理时,其他线程顺序取下一条数据处理」

@rekulas #1
@Vegetable #4
取数逻辑是未处理数据中的第一个,行锁管不了(意味着非序列级别的事务也管不了),表锁和序列级别事务又管得太多了——写回前都会阻塞另一个线程取数,这样的话无论多少线程都跟单线程没啥区别。

@MoYi123 #8 你这个其实就是变相的表锁

我这没有方案,不过可以确定单靠数据库是解决不了了。上面两个用队列的方案,看起来就星了。
yjhatfdu2
141 天前
begin;
select * from tbl for update skip locked limit 1;
# 处理逻辑
update from tbl where xxx=xxx set processed=1;
commit;
yjhatfdu2
141 天前
现在的程序员都不会开事务了吗
Vegetable
141 天前
@nothingistrue 锁是为了修改状态标记占坑,不是处理任务的时候锁着。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1051113

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX