Discourse 广泛使用 Redis 锁作为同步机制。Discourse 的 Redis 锁实现(如下所示)可以优化,以减少所需的 Redis 命令数量,从而降低往返时间和 Redis 执行这些命令的成本。
我们如何改进它
lock 和 unlock 都使用了 Redis 事务。lock 过程可以简化为以下代码:
WATCH key
GET key # 判断键是否已过期
MULTI
SET key
EXPIRE key, expire_time + 1
使用 Redis 事务的原因(我推测)似乎是:在真正设置键之前,先检查该键是否已过期。
但我觉得我们可以直接使用 Redis 提供的 SETEX 命令,该命令可以设置一个带过期时间的键。事实上,Redis SETEX 文档 也将其作为用 SETEX 替代 SET 和 EXPIRE 的示例。
以下是我们应进行替换的理由:
- 将值设置为过期时间并在设置键之前进行检查是不必要的。TTL 机制本身已足以确保键正确过期。
- 即使我们决定将过期时间作为值,也不需要任何事务。因为在
GET(上述代码第 2 行)与MULTI和EXEC之间提供原子性并无实际意义。这是因为如果锁未获取成功,系统会进行重试。
历史备注
我查阅了 Git 提交历史以获取更多信息。看起来在 Redis 锁被 引入 时,我们并未使用 Redis 提供的 TTL 功能。TTL 特性是在 很久之后 才加入的。
因此,我认为我们可以更进一步,完全移除 Redis 事务。