GH GambleHub

分布式锁定

1)为什么(以及何时)需要分布式锁定

分布式锁定是一种机制,可确保群集多个节点之间临界部分的互操作性。典型任务:
  • 背景任务/sheduler的领导力(领导力)。
  • 将单一执行者限制为共享资源(移动文件、迁移模式、独家支付步骤)。
  • 如果无法实现等效性/排序,则顺序处理单元(wallet/order)。
当最好不要使用锁时:
  • 如果可以进行等效的upsert,CAS(匹配和设置)或按键排队(按键排列)。
  • 如果资源允许交换操作(CRDT,计数器)。
  • 如果问题由单个存储中的事务解决。

2)威胁模型和属性

故障和复杂性:
  • 网络:延迟、分离(分区)、数据包丢失。
  • 过程:GC暂停,停止世界,锁定后崩溃。
  • 时间:时钟漂移和位移打破TTL方法。
  • 重新拥有:网络后的"僵尸"过程可能认为它仍然拥有城堡。
所需属性:
  • 安全:不超过一个有效所有者(安全)。
  • 活着:城堡在主人失灵(liveness)时被释放。
  • 正义:没有饥饿。
  • 时钟独立性:正确性独立于时钟(或由时钟调整来补偿)。

3)主要型号

3.1 Lease(租赁锁)

TTL发出了锁。业主有义务将其延长至到期(心跳/保持活力)。

优点:在裂缝中自负。
风险:如果所有者"卷曲"并继续工作,但失去了延期,则可能会出现双重所有权。

3.2 Fencing token(护栏令牌)

每次成功捕获时,都会发出单调增长的数字。资源用户(DB,队列,文件存储)检查令牌并拒绝使用旧编号的操作。
这在TTL/租赁和网络拆分中至关重要-可以防止"旧"所有者。

3.3 Quorum锁(CP系统)

使用分布式共识(Raft/Paxos;etcd/ZooKeeper/Consul),记录与共识日志相关联→在大多数节点上没有分裂中断。

另外:强大的安全保障。
减:对法定人数的敏感性(如果失去法定人数,其生存能力将减弱)。

3.4个AP锁(内存/缓存+复制)

例如,Redis群集。高可用性和速度,但没有严格的网络分离安全保证。需要在合成器侧面进行弯曲。

4)平台和模式

4.1 etcd/ZooKeeper/Consul(建议使用强锁)

短暂节点(ZK)或会话/leases(etcd):关键在会话现场时存在。
会期保持性;法定人数的损失→会议到期→锁被释放。
等候队列的序节点(ZK'EPHEMERAL_SEQUENTIAL) →公平性。

Etcd(Go)上的草图:
go cli, _:= clientv3. New(...)
lease, _:= cli. Grant(ctx, 10)            // 10s lease sess, _:= concurrency. NewSession(cli, concurrency. WithLease(lease. ID))
m:= concurrency. NewMutex(sess, "/locks/orders/42")
if err:= m. Lock(ctx); err!= nil { / handle / }
defer m. Unlock(ctx)

4.2 Redis(整洁)

经典是"SET key value NX PX ttl"。

问题:
  • 复制/传送器可以允许同时拥有者。
  • 多个实例的雷德洛克(Redlock)降低了风险,但无法消除;在网络不可靠的环境中有争议。

将Redis应用为快速协调层更安全,但始终在目标资源中补充加密令牌。

示例(Lua-unlock):
lua
-- release only if value matches if redis. call("GET", KEYS[1]) == ARGV[1] then return redis. call("DEL", KEYS[1])
else return 0 end

4.3 DB锁

PostgreSQL咨询锁:Postgres群集(过程/会话)中的锁。
好吧,当所有关键部分都处于一个DB中时。

SQL:

sql
SELECT pg_try_advisory_lock(42); -- take
SELECT pg_advisory_unlock(42); -- let go

4.4个文件/云"锁"

S3/GCS+具有"If-Match"(ETag)条件的lock对象元数据→本质上是CAS。
适合备用/迁移。

5)安全锁设计

5.1所有者身份

存储"owner_id"(节点#process #pid#start_time)+unlock时用于对账的随机令牌。
重复解锁不应拆除别人的锁。

5.2 TTL和延期

TTL <T_fail_detect(故障检测时间)和≥ p99操作关键部分×库存。
扩展-定期(例如,每个"TTL/3"),具有截止日期。

5.3 Fencing token on shinka

改变外部资源的部分必须传递"fencing_token"。

Sink(DB/缓存/存储)存储"last_token"并拒绝较小的:
sql
UPDATE wallet
SET balance = balance +:delta, last_token =:token
WHERE id =:id AND:token > last_token;

5.4队列等待和公平

在ZK-"EPHEMERAL_SEQUENTIAL"和观察者中:客户正在等待释放最近的前任。
在etcd中,是具有修订版/发行版的钥匙;根据"mod_revision"排序。

5.5分裂大脑的行为

CP方法:没有法定人数,就无法锁定-停机时间比打破安全性更好。
AP方法:允许在分离的岛屿上取得进展→需要进行调整。

6)领导力(领导力)

在etcd/ZK中-"领导者"是独家的epemer键;其余的订阅更改。
领导者写心跳;损失-连任。
所有领导者操作都伴随着fencing token(时代/修订号)。

7)错误及其处理

客户拿起城堡,但在→规范之前没有人会受到伤害。TTL/会议将腾空。

城堡在工作的中间到期:
  • 看门狗是强制性的:如果延期失败,则中断关键部分并回滚/补偿。
  • 没有"下车":没有锁,关键部分就无法继续。

一个漫长的停顿(GC/stop-the-world)→扩展没有发生,另一个接管了城堡。工作流必须检测所有权的损失(保持通道)并中断。

8)Dedlocks,优先事项和倒置

在分布式世界中,Dedlocks很少见(锁通常是一个锁定),但是如果锁有几个锁定,则坚持单个取回顺序(锁定命令)。
优先级倒置:低优先级所有者在高优先级等待时保留资源。解决方案:TTL限制,提前(如果业务允许),共享资源。
禁食:使用等待队列(ZK子顺序节点)实现公平。

9)可观察性

度量标准:
  • `lock_acquire_total{status=ok|timeout|error}`
  • `lock_hold_seconds{p50,p95,p99}`
  • "fencing_token_valu"(单调)
  • `lease_renew_fail_total`
  • 'split_brain_prevented_total'(由于缺乏法定人数而拒绝了多少次尝试)
  • `preemptions_total`, `wait_queue_len`
Logi/Tracing:
  • `lock_name`, `owner_id`, `token`, `ttl`, `attempt`, `wait_time_ms`, `path` (для ZK), `mod_revision` (etcd).
  • 带有结果的"acquire → critical section → release"。
Alerts:
  • "lease_renew_fail_total"的增长。
  • `lock_hold_seconds{p99}` > SLO.
  • "Orfen"锁(没有心跳)。
  • 等待队列膨胀。

10)实例

10.1安全的Redis锁定与fencing(伪)

1.将令牌计数器存储在可靠的堆栈中(例如Postgres/etcd)。
2.成功的"SET NX PX"会读取/嵌入令牌,并且所有资源更改都将通过DB/服务中的令牌验证来完成。

python acquire token = db. next_token ("locks/orders/42") # monotone ok = redis. set("locks:orders:42", owner, nx=True, px=ttl_ms)
if not ok:
raise Busy()

critical op guarded by token db. exec("UPDATE orders SET... WHERE id=:id AND:token > last_token",...)
release (compare owner)

10.2 etcd Mutex + watchdog (Go)

go ctx, cancel:= context. WithCancel(context. Background())
sess, _:= concurrency. NewSession(cli, concurrency. WithTTL(10))
m:= concurrency. NewMutex(sess, "/locks/job/cleanup")
if err:= m. Lock(ctx); err!= nil { /... / }

// Watchdog go func() {
<-sess. Done ()//loss of session/quorum cancel ()//stop working
}()

doCritical (ctx )//must respond to ctx. Done()
_ = m. Unlock(context. Background())
_ = sess. Close()

10.3 ZK领导(Java, Curator)

java
LeaderSelector selector = new LeaderSelector(client, "/leaders/cron", listener);
selector. autoRequeue();
selector. start(); // listener. enterLeadership() с try-finally и heartbeat

10.4 Postgres advisory lock with dedline (SQL+app)

sql
SELECT pg_try_advisory_lock(128765); -- attempt without blocking
-- if false --> return via backoff + jitter

11)花花公子(Game Days)

失去法定人数:禁用1-2个etcd节点→尝试取锁不应通过。
GC暂停/停止世界:人为地延迟所有者的流动,→检查看门狗正在中断工作。
Split-brain:模彷城堡所有者和看门狗之间的网络鸿沟→新主人获得更高的折腾,旧的-被合成器拒绝。
Clock skew/Drift:从所有者那里移走手表(对于Redis/lease) →确保通过令牌/检查确保正确性。
发布前崩溃:TTL/会话释放 →锁的过程下降。

12)反模式

干净的TTL锁,当访问外部资源时,不带扣。
依靠本地时间来正确无误(没有HLC/fencing)。
通过一个Redis向导在带有操纵器的环境中分发锁,而无需确认副本。
无限关键部分(TTL"世纪")。
在没有"owner_id"/token对账的情况下拆除"外星人"锁。
缺少backoff+Jitter →尝试的"风暴"。
一站式全球锁-一袋冲突;在钥匙上缝合更好。

13)实施支票

  • 定义了资源的类型以及是否可以通过CAS/队列/幂等来实现。
  • 选择了一种机制:etcd/ZK/Consul for CP;Redis/缓存-仅带调试。
  • 实现:"owner_id",TTL+扩展,看门狗,正确解锁。
  • 外部资源会检查fencing token(单调性)。
  • 有领导和失败的策略。
  • 设置指标、异常值、令牌和修订版本。
  • 在acquire上提供backoff+jitter和taymout。
  • 举行比赛日:法定人数,分裂大脑,GC暂停,时钟跳跃。
  • 关于采取多个锁的顺序的文件(如果需要)。
  • 退化计划(brownout):在无法进入城堡的情况下该怎么办。

14) FAQ

Q: Redis锁定是否足够"SET NX PX"?
A:只有当资源检查了fencing token时。否则,在网络共享中,两个所有者是可能的。

Q: 默认情况下选择什么?

答:对于严格的保证是etcd/ZooKeeper/Consul (CP)。对于一个DB内的轻量级任务-advisory locks Postgres。Redis只是一个骗局。

问:什么是TTL上市?
答:"TTL ≥ p99的关键部分持续时间× 2",并且足够短,可以快速清理"僵尸"。扩展-每个"TTL/3"。

问:如何避免饥饿?
A:顺序等待队列(ZK序列)或公平算法;尝试限制和公平规划。

Q: 是否需要时间同步?

答:要正确无误,请使用fencing。对于可操作的可预测性-是的(NTP/PTP),但不要依赖锁逻辑中的墙时钟。

15)结果

强大的分布式锁定是在具有lease+keepalive的法定支架(etcd/ZK/Consul)上构建的,并且必须通过在可变资源级别上的锁定令牌来补充。任何没有围栏的TTL/Redis方法都是破裂的风险。首先考虑低成本和偶然性,在没有低成本的情况下使用锁定,测量,测试故障模式-并且"关键部分"仅在意义上而不是事件数量上仍然至关重要。

Contact

联系我们

如需任何咨询或支持,请随时联系我们。我们随时准备提供帮助!

开始集成

Email — 必填。Telegram 或 WhatsApp — 可选

您的姓名 可选
Email 可选
主题 可选
消息内容 可选
Telegram 可选
@
如果填写 Telegram,我们也会在 Telegram 回复您。
WhatsApp 可选
格式:+国家代码 + 号码(例如:+86XXXXXXXXX)。

点击按钮即表示您同意数据处理。