返回首页

Redis Lua 队列:分区顺序和工作池

文章描述了在 Redis 上使用 Lua 脚本实现任务队列,以保证分区顺序并共享工作池。支持延迟消息、重试、性能基准测试。适用于无需 Kafka 的高负载服务。

Redis 中的 Lua 脚本:具有顺序和延迟的队列
Advertisement 728x90

Redis队列与Lua脚本:在共享工作池中确保分区内的严格顺序

在高负载系统中,消息处理需要在逻辑分区内保持严格顺序,涉及数十万键。有限的工作池必须均匀分配负载,避免被繁重分区阻塞。通过Redis与Lua脚本在smart-redis-queue库中实现了重试和延迟任务支持。

该方案利用Lua的原子性操作ZSET、HASH和STRING,无竞态条件。键以queue:{queueName}:为前缀。有序分区(代码前缀为!)通过锁定独占处理,而常规分区并行处理。

与Kafka和RabbitMQ的对比

Kafka确保主题分区内的顺序,并通过消费者组扩展,但将分区固定分配给消费者。动态工作池从任何就绪分区选取任务需要不同的语义。Kafka中的TTL无法替代精确的定时调度。

Google AdInline article slot

RabbitMQ支持TTL和死信队列,但多消费者下的严格顺序因轮询而破坏。为百万键单独设队列不可扩展。

Redis + Lua提供轻量级替代方案:每次原子操作仅需一轮往返,无需集群或复杂监控。

键结构

| 键 | 结构 | 用途 |

Google AdInline article slot

|------|-----------|------------|

| queue:{name}:partitions | SET | 活跃分区 |

| queue:{name}:partition:{code}:{priority} | ZSET | 按优先级排列的任务队列,分数 = 可用时间(毫秒) |

Google AdInline article slot

| queue:{name}:partition:{code}:priorities | ZSET | 优先级级别 |

| queue:{name}:payload:{taskId} | STRING | 负载数据 |

| queue:{name}:partition:{taskId} | STRING | 分区代码 |

| queue:{name}:consumer:{id}:tasks | HASH | 处理中的任务 |

| queue:{name}:partition:{code}:lock | STRING | 有序分区锁 |

| queue:{name}:partition:{code}:block | STRING (TTL) | 临时阻塞(速率限制) |

任务生命周期

  • 发布:幂等添加,使用SET NX,写入ZSET,分数 = 调度时间(毫秒)。分区添加到SET。
local ok = redis.call('SET', payloadKey, payload, 'NX')
if ok then
    redis.call('ZADD', partitionQueueKey, scheduled, taskId)
    redis.call('SADD', partitionsKey, partitionCode)
end
  • 获取:心跳机制,迭代分区,检查有序分区的锁/阻塞。按降序优先级(ZREVRANGE)获取,如果调度时间 <= 当前时间,则从ZSET头部取。移至消费者的HASH。
  • 确认:从HASH、负载数据和键中移除。如果有序计数器为0,则释放锁。
  • 拒绝:返回ZSET并设置新分数。对于有序分区——优先级+1,使用reject_seq打破平局。如果第一个任务被拒绝,则拒绝整个有序分区批次。

性能基准测试

测试环境:Go 1.22+、Redis 5、Intel i7-13700H。

发布(任务/秒):

  • 1个任务:17–23千
  • 批量10个:80–92千
  • 批量100个:100–117千

批处理通过单次Lua调用提高吞吐量。

单个工作器消费(任务/秒):

| 预取 | 微秒/操作 | 任务/秒 |

|----------|--------|---------|

| 1 | 80–103 µs | 9.7–12.5千 |

| 5 | 57–67 µs | 15–17.5千 |

并行处理:

  • 2个工作器:28–30千/秒
  • 8个工作器:23–26千/秒

由于Redis争用,扩展呈非线性。

故障处理

  • 心跳机制,TTL约120秒。
  • 活跃消费者通过分布式锁扫描失效消费者。
  • 失效消费者HASH中的任务返回队列(有序分区优先级增加)。

速率限制:RejectWithDelay设置带TTL的阻塞。

建议:优先级步长设为100+,避免与拒绝增量冲突。

关键要点

  • Lua原子性确保任务在ZSET和HASH间移动时无竞态条件。
  • 有序分区通过独占锁和拒绝逻辑保持顺序。
  • 发布/获取中的批处理将吞吐量提升至10万+任务/秒。
  • 工作器故障后自动恢复,无任务丢失。
  • 适用于百万分区场景,无集群开销。

— Editorial Team

Advertisement 728x90

继续阅读