一个高并发架构问题,求指点

2020-03-07 12:17:45 +08:00
 Jinnrry

目前我们在做一个审核后台,每天早上运营需要领取任务。领任务逻辑大致这样

// 1、先取数据
select * from data where status = xxx limit 50


// 2、写入任务表
insert into task .......



// 3、修改数据状态

update data set status = xxx where xxxx

但是!这样有个缺陷,如果多个人同时点领取,那么可能导致多个人领到同一条任务。目前想到的解决办法: 1、把这个操作写成一个事务,然后使用 serializable 隔离级别,保证每次只执行一个 2、把分任务的操作单独出来做出一个服务,使用单线程实现,保证每次只处理一个人的领取

但是这 2 种方法好像都有点影响性能,虽然我们后台没什么关系,但是本着对技术的追求,想来问问各位大佬,有没有什么更好的解决方案?

4931 次点击
所在节点    问与答
45 条回复
Comdex
2020-03-07 13:21:36 +08:00
一般运营领任务很少见高并发,直接 select for update
PDX
2020-03-07 13:46:32 +08:00
这种问题我都是往 redis 里塞个 key 开控制并发
watzds
2020-03-07 13:50:31 +08:00
这 50 是啥,同时领 50 个任务?
horryq
2020-03-07 13:57:32 +08:00
起一个分配任务的线程来分配任务, 领任务的人插到队列里,分配线程消费这个队列, mpsc
ferock
2020-03-07 14:17:04 +08:00
@Jinnrry #6 msyql 下,了解一下 last_insert_id() 这个方法
Jinnrry
2020-03-07 14:18:18 +08:00
@watzds #23 一次领 50 条数据进行审核
ferock
2020-03-07 14:18:25 +08:00
另外,处理领任务,这本来就是队列机制的本分啊?为啥不用呢?
mcfog
2020-03-07 14:22:02 +08:00
虽然各种 db 内外的锁机制都解决这个问题,但还是建议考虑数据结构是否可以设计得更好

比如为什么不让 status 变成(新增) xxx 的同时就建立 task ? 解耦 task 和 data 使得以后有其他 datum foo bar 业务表也可以复用 task 的结构和逻辑?

当 task 是单纯的工单,自然领取任务这样的业务就都是单纯的单表操作,直接 update asignee where limit 就行

“分配任务”这个例程还要读 data 这样的业务表就不合理,更别说去锁里面的数据了
firefox12
2020-03-07 14:47:49 +08:00
就是不会用事务,才会有这种问题。以现在 db 的速度,什么性能问题,不存在的。
kaneg
2020-03-07 14:47:55 +08:00
最近做了个项目,里面有类似的需求:从任务表中取出一批到期要执行的任务,然后存入下次执行的时间。在单机环境下工作没问题,但在 HA 模式下,会出现多个机器拿到同一批数据的问题。
鉴于我们的数据量不大,采取的方式是最简单的 select for update。目前没发现什么问题,不知道这种方案是不是 best practice。
sadfQED2
2020-03-07 15:39:55 +08:00
@mcfog 数据生产和数据审核,有可能是两个不同的团队负责的,不一定能随便改生产流程
ferock
2020-03-07 15:46:30 +08:00
@kaneg #30 当然不是啊
dovme
2020-03-07 17:05:13 +08:00
你把任务的步骤改一下,变成 132 不就解决了所有的问题??
dovme
2020-03-07 17:06:29 +08:00
@dovme #33 好像还是不行(▼皿▼#)。
zgzhang
2020-03-07 18:02:19 +08:00
低频操作我理解用 Redis 做一把全局锁就可以了
Lock.lock();
doSomeThing();
Lock.unlock();
lewis89
2020-03-07 18:05:32 +08:00
解耦 task 就好,看业务 应该是 data 里面一行数据 会对应 task 里面一行数据,应该在建立 data 一行的时候 建立相关联的一行 task,然后操作 data 的时候 发消息去更改 task 的状态,这样 task 天然就能做到幂等,即使多个业务同时操作数据 task 也状态也只会从 0 -> 1 转换一次
npe
2020-03-07 18:21:40 +08:00
1.队列
2.内存锁
3.db 行锁
GoLand
2020-03-07 18:42:59 +08:00
这个还需要锁吗....如果 task 表有 data 表的主键字段,给 task 表加上 data_id 的 unique key 不就行了吗。data 业务上可以重复取,但是一个 data 只能被一个人领取(对应 task 的 unique ),task 表写成功了继续更新 data 表的 status,unique key 冲突了表示任务被领取了,返回失败就可以了。
22yune
2020-03-07 19:10:46 +08:00
高并发一般瓶颈在共享资源。应先分析业务过程中哪些是共享的,哪些是可以并发的。
做到高并发的重点在把共享资源的抢占尽量减少。
建议在领任务前把任务分好批次,一个批次 50 个。加大了共享资源的粒度,使单次抢占做更多有效'功'。
zjsxwc
2020-03-07 20:59:08 +08:00
不用数据库锁就队列加回调

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

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

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

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

© 2021 V2EX