Redis 业务实践

Written by with ♥ on in Redis

延时队列

根据时间戳设置任务执行的时间,不断取出当前时间需要执行的任务,内部使用 ZSet 实现,核心接口:

  • 往队列添加任务
  • 删除队列任务
  • 从队列取出任务

添加任务

添加任务就是添加一个正常的 ZSet 元素,只不过 Score 的值为任务执行时间对应的时间戳。

// 任务执行的时间(一分钟后)
now := time.Now().Unix() + 60
// 任务相关消息
msg := "..."

// 添加任务
cache.ZAdd(key, redis.Z{
    Score: now, // Score 设置为时间
    Member: msg, // 消息体
})

删除任务

直接删除元素。

redis.ZRem(key, msg)

取出任务

取出任务比较复杂,如果在并发环境下,同时取出任务,需要保证一个任务只被取出一次,不会重复执行。

主要使用 ZRangeByScore 命令,以当前时间戳作为范围,取出一个到期的任务。

ele, err := cache.ZRangeByScore(key, redis.ZRangeBY{
    Min: 0,
    Max: time.Now().Unix(), // 时间范围 0 - now
    Count: 1, // 取出一个元素
    Offset: 0,
}).Result

// 没有元素
if len(ele) == 0 {
    return "暂无任务"
}

此时很多并发实例会取出同一个任务,如何保证只被一个实例取出?使用 ZRem 命令,只有一个并发实例能成功删除任务,删除成功返回 1,删除失败返回 0,因此只有在返回 1 时,才认为取出任务成功。

// 判断是否拿到任务
success, err := cache.ZRem(key, ele[0]).Result()

// 抢到任务
if success == 1 {
    return ele[0]
} else {
    return "没抢到任务"
}

去重队列

使用 Set