请问有必要用专门的消息队列吗? all in sqldb 是否可行?

66 天前
 Need4more

首先是 message 表:

字段名 类型及描述
id 主键
status 消息状态(待处理,进行中,成功,失败)
try_count 消费者重试次数
lock_expires 锁过期时间
created 创建时间
data 消息数据

然后消费端轮询:

select *
from message
where status in ('PENDING', 'STARTED', 'FAILED')
and try_count < max_tries
and lock_expires < now()
order by created
limit 1 for update
skip locked;

解释下 sql:

  1. 状态筛选 (status IN ('PENDING', 'STARTED', 'FAILED')) 只选取未开始或已开始但未完成的任务(可能因崩溃需重启)。
  2. 重试保护 (try_count < max_tries) 确保任务重试次数未超过允许的最大值(避免无限重试失败任务)。
  3. 锁过期检查 (lock_expires < NOW()) 任务被处理时会加锁(设置未来过期时间),此条件筛选锁已过期的任务(说明此前处理进程异常退出)。
  4. 排序与限量 (ORDER BY created LIMIT 1) 按创建时间排序后取最旧的一条任务,实现公平调度。
  5. 并发控制 (FOR UPDATE SKIP LOCKED)
    • FOR UPDATE:锁定所选行,防止其他进程修改。
    • SKIP LOCKED:跳过已被其他进程锁定的行,避免阻塞等待,提升并发效率。
    • 此语法在 postgresql 、mysql(8.0)均支持。

利用关系数据库持久化消息,支持索引,可以灵活检索,关键是无需额外引入组件,请问这种方案是否可行呢?

3234 次点击
所在节点    程序员
31 条回复
chesha1
66 天前
为啥不用 redis ,redis 做消息队列不是比 sqldb 更成熟的方案吗?
nulIptr
66 天前
看你侧重哪部分功能,简单少量数据的场景随便怎么搞都行,无需引入额外组件也确实是一个优点,古时候在金蝶做某个项目的时候真是你这种用法,除了数据库是 sqlserver 。
不过消息队列主要还是数据量大的时候为了高吞吐量,就要花点心思了。
julyclyde
66 天前
举个例子:
一个消息需要被
不定量的消费者
分别读取

就这个“不定量”,你用关系型数据库就不好搞吧
Need4more
66 天前
@julyclyde 再增加一个订阅表即可
sub_id:消费者标识
last_msg_id:最近一次消费的消息
status: 消费状态

每增加一个消费者就在这个表添加条记录
Need4more
66 天前
@chesha1 redis 不支持持久化
Need4more
66 天前
@nulIptr 是的,当你的系统达到那么大规模的时候,再招个运维也不是啥问题了。在此之前,我倾向于用最少的组件交付业务。
HENQIGUAI
66 天前
不是很理解为什么说 Redis 不支持持久化
Need4more
66 天前
@HENQIGUAI
redis 的持久化保证没有数据库强,同时 pub/sub 不可靠
codingKingKong
66 天前
1.首先看规模, 如果数据量不大, 资源竞争不严重, 我同意其他人的观点, 怎么搞都行
2.然后还是看规模, 如果都上到 sql 上, 发现性能能接受, 那其实就是合适的方案
3.如果流量再高些, 请考虑 redis, 你可以不用它的 push, pop 这些来实现, 但是它能提供比高并发下 mysql 更好的性能, 毕竟它是做缓存用的
4.如果还有些特殊需求, 请考虑上专业的延迟队列, 例如延迟消费, 自动重试, 精确消费, 至多至少消费语义等, 这些不是 sqldb 不能实现, 而是你需要自己实现, 你需要考虑性价比
最后, 是否有必须要这个问题需要你自己考虑, 不同场景下答案不一样
ratazzi
66 天前
可行啊,Rails 8 的 Solid Queue 就是这样的

https://dev.37signals.com/solid-queue-v1-0/
neilq
66 天前
恭喜你发现了 `本地消息表`

完全可以这么做
neilq
66 天前
另针对 redis 消息队列补充一句,现在 redis 5.0 以上不推荐使用 pub/sub ,pub/sub 确实不能持久化。

现在 redis 消息队列推荐使用 redis streams 。不过需要自己实现清理消息的机制。

redis 服务器压力没那么大的情况下不需要考虑持久化稳定性,尤其在你`系统没那么大规模`的情况下。
zepc007
66 天前
All In Postgres
banmuyutian
66 天前
可以呀,我司在用的 solace 就是这么实现的
moyupai
66 天前
完全可以,但完全没必要,
量不大,用啥都行
量大,你要想想数据库怎么扩喽
8355
66 天前
数据库性能难道不是比队列或者 redis 更贵吗。。。
这到底是轻量化需求还是受环境限制?
mightofcode
66 天前
业务量小 实时性要求低
其实没啥问题
bbao
66 天前
redis 从来不推荐你做消息队列使用,sql 做消息队列, 内部系统随便你;

toC 系统,HR 这个人不合适,我们启动招聘吧。
cookii
66 天前
多一个消息队列多一个维护成本,优先在已有的技术栈上进行业务实现。性能扛不住了再换到消息中间件。
唯一注意的是代码做好抽象,以便未来切换。
z1829909
66 天前
消费端从数据库获取消息 + 标记消息状态 不是原子性的, 会导致一个消息同时被多个地方执行吧

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

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

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

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

© 2021 V2EX