华为云 DCS for Redis 分布式锁实战:加锁要原子,释放要校验

举报
yd_269121110 发表于 2026/11/02 07:35:00 2026/11/02
【摘要】 一次线上事故的复盘:并发压测下订单重复扣款,查到最后发现是"假锁"惹的祸。 一、假锁比没锁更可怕分布式系统里,多实例同时操作同一资源是家常便饭:库存扣减、定时任务、幂等控制。没有分布式锁,会出现超卖和重复执行;而用错锁,比没锁还危险——你以为已经安全了,其实漏洞一直都在。最常见的错误写法是 SETNX + EXPIRE 两条命令拼接。问题在于它们不是原子操作:加锁成功、还没来得及设过期时间,...

一次线上事故的复盘:并发压测下订单重复扣款,查到最后发现是"假锁"惹的祸。

一、假锁比没锁更可怕

分布式系统里,多实例同时操作同一资源是家常便饭:库存扣减、定时任务、幂等控制。没有分布式锁,会出现超卖和重复执行;而用错锁,比没锁还危险——你以为已经安全了,其实漏洞一直都在。

最常见的错误写法是 SETNX + EXPIRE 两条命令拼接。问题在于它们不是原子操作:加锁成功、还没来得及设过期时间,实例就挂了,锁永远不释放,直接变成死锁;就算设了过期时间,业务还没跑完锁就到期,别的实例立刻拿到锁,并发照旧发生。

二、正确姿势:SET NX PX 一步到位

Redis 2.6.12 之后,SET 命令原生支持 NX 和 PX,可以把"判断 key 是否存在"和"设置过期时间"合并成一个原子操作,还支持毫秒级精度:

import redis, uuid

r = redis.Redis(host="你的DCS实例地址", port=6379,
                password="***", decode_responses=True)

def acquire(key, ttl_ms=10000):
    token = uuid.uuid4().hex          # 每个持锁者一个唯一标识
    ok = r.set(key, token, nx=True, px=ttl_ms)
    return token if ok else None

一条命令完成加锁与超时,既不会有死锁,也不会出现"忘了设 TTL"的低级错误。生产环境建议配合连接池(如 redis.ConnectionPool)复用连接——DCS 实例的连接数是有配额的,每次加锁都新建连接很容易把配额打满。

三、释放锁必须原子

释放锁时如果直接 DEL,可能误删别人的锁:A 的锁到期后 B 拿到了锁,A 执行完却把 B 的锁删掉了。正确做法是先校验 value 再删除,而且校验与删除要用 Lua 脚本原子完成,否则校验通过到删除之间仍可能被抢占:

if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
else
    return 0
end

在 Python 里调用:

UNLOCK_LUA = """
if redis.call('get', KEYS[1]) == ARGV[1] then
  return redis.call('del', KEYS[1])
else
  return 0
end
"""

def release(key, token):
    r.eval(UNLOCK_LUA, 1, key, token)

另外,Lua 脚本建议复用:先用 SCRIPT LOAD 拿到返回值 SHA,后续用 EVALSHA 调用,能省去每次传输脚本文本的开销,在高 QPS 场景下这笔优化相当可观。

四、实战避坑清单

  1. 锁的值必须唯一:用 UUID 或线程 ID,绝不能用固定字符串,否则无法区分锁的归属,误删风险极高。
  2. 想清楚锁续期:业务耗时不可控时,用后台"看门狗"线程定期续期(Redisson 的思路),或把 TTL 设得足够大,同时监控实际执行时长。
  3. 集群模式要分清:DCS 主备架构下,上述单实例方案足够稳妥;只有在主备切换会破坏互斥的场景下才考虑 Redlock,且要知道它在极端网络分区下的争议,别盲目照搬。
  4. 连接要保活:DCS 会回收长时间空闲的连接,客户端务必开启 keepalive 与自动重连,避免一次锁操作直接抛连接异常。
  5. 锁不等于超时控制:锁只保证互斥,不保证执行时长。超时逻辑必须单独设计,不能指望锁自己兜底。

五、小结

分布式锁的核心其实就两句话:加锁要原子(SET NX PX),释放要校验(Lua 比对 value)。再配上合理的 TTL 与续期机制,以及对 DCS 连接的健康管理,绝大多数并发问题都能兜住。锁用对了,线上事故能少一大半。把上面五条检查项逐一过一遍,基本能避开绝大多数与锁相关的事故。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。