V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
Need4more
V2EX  ›  程序员

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

  •  1
     
  •   Need4more · 7 天前 · 2862 次点击

    首先是 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)均支持。

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

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

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

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

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

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

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

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

    toC 系统,HR 这个人不合适,我们启动招聘吧。
    cookii
        19
    cookii  
       6 天前
    多一个消息队列多一个维护成本,优先在已有的技术栈上进行业务实现。性能扛不住了再换到消息中间件。
    唯一注意的是代码做好抽象,以便未来切换。
    z1829909
        20
    z1829909  
       6 天前
    消费端从数据库获取消息 + 标记消息状态 不是原子性的, 会导致一个消息同时被多个地方执行吧
    z1829909
        21
    z1829909  
       6 天前
    超时时间也不是很合理, 因为对于不同的任务, 能忍受的长度不一样, 感觉还是约定一个心跳间隔, 只要消息还在被处理, 就定时更新这条记录某个字段的时间. 这样当这个字段超过心跳间隔没有更新, 视为处理的进程已经无了.
    Need4more
        22
    Need4more  
    OP
       6 天前
    select for update 会锁住记录,确保只会被单个地方消费,skip locked 确保不会阻塞消费者
    @z1829909
    z1829909
        23
    z1829909  
       6 天前
    @Need4more GET, 那你更新 status 和 select 是在一个事务里对么
    kaffka
        24
    kaffka  
       6 天前
    可以考虑添加成熟的 PG mq 拓展
    spritecn
        25
    spritecn  
       6 天前
    没 redis leftPop+rightPush 好用,还有 3/5/10/15 秒后重试你这个不太好实现,fro update 如果消息处理时间稍长,一直占着连接是个问题
    fionasit007
        26
    fionasit007  
       6 天前
    @HENQIGUAI #7 用的阿里云 redis ,六七年的数据都还在,不过因为数据量太大不知道有没有丢数据的情况
    spritecn
        27
    spritecn  
       6 天前
    @fionasit007 阿里 1G 一年 500 块,我好像 10 分之 1 都用不到
    fionasit007
        28
    fionasit007  
       6 天前
    @spritecn #27 个人的话我是肯定不用,挺贵的,不过这样的云服务用着就是省心,阿里云 redis 反正这么多次事故好像都幸免于难
    mightybruce
        29
    mightybruce  
       6 天前
    如果就是简单的消息队列需要落盘, 不如采用 redis + rocksdb 持久化的项目 kvrocks
    https://github.com/apache/kvrocks

    https://kvrocks.apache.org/docs/supported-commands/ 支持 redis 消息操作。
    darrenxyli
        30
    darrenxyli  
       5 天前
    我们现在就是这么干的,做过压测,瓶颈全在数据库能够支持的 QPS 上面
    Need4more
        31
    Need4more  
    OP
       5 天前
    @darrenxyli 性能能抗住吗?
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5474 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 02:09 · PVG 10:09 · LAX 19:09 · JFK 22:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.