← 返回题目列表

分布式读写锁适合什么场景?

困难 第 26 / 26 题 更新于 2026/10/01
分布式锁读写锁公平性一致性
学习 AI 实战项目

简化版

分布式读写锁允许多个读者并发、写者独占,适合读操作较长且必须阻止配置切换、资源迁移等写操作的场景。若普通读取可依靠 MVCC、快照或版本校验,就不应为它增加远程锁成本。

详细版

实现需维护读者集合/计数、写 owner 和等待状态:无写者及无优先等待写者时才能加读锁,读者清零后才能授予写锁。必须处理读者会话过期、写者饥饿、读锁升级死锁与批量唤醒,通常采用写优先或阶段公平策略。

读写锁并不让数据自动一致,所有访问者必须遵守协议,且资源侧仍需版本或 fencing。适用前比较 MVCC、不可变快照和乐观读;监控活跃读者、最老读租约、写等待 P99、升级失败和会话清理时间。

完整版教学

一、它解决的并发关系

互斥锁把读与读也串行,而读写锁允许 R-R 并行、禁止 R-W 和 W-W。收益只在读临界区较长且并发读很多时明显;若读取几毫秒,远程获取租约的 RTT 可能超过节省的时间。

心法:先证明“读必须锁且读读可并行”,再考虑分布式读写锁。

二、服务端状态模型

锁服务至少保存 writer owner、reader owners/leases 和等待队列。新读请求不能只看当前无 writer;若已有写者等待仍不断放行读者,写者会永久饥饿。每个读者需独立租约,否则一个进程宕机会让计数永远不归零。

FREE -> READERS(n) -> FREE -> WRITER -> FREE
          |                         ^
          +-- new reader? ----------+ 取决于公平策略

三、三种调度策略

读优先吞吐高但可能饿死写者;写优先在出现写等待后阻止新读者,可控地排空现有读者;公平队列按权威入队顺序切换读写批次,复杂度最高。选择应由写等待 SLO 与读吞吐共同决定。

策略读吞吐写等待风险
读优先高不确定写饥饿
写优先中较可控读长尾
阶段公平中高较稳定队列复杂

四、升级与降级为什么危险

两个读者都想升级为写锁,各自等待其他读者退出,就会互相等待。安全做法通常是释放读锁、进入写队列后重新读取并校验版本;若必须原子升级,服务端只能允许一个升级候选,其余失败。写锁降级为读锁则需原子完成,避免中间窗口被其他写者插入。

升级后不能沿用旧读快照做写决策,必须再次验证资源版本。

五、数字判断是否值得

假设远程加解锁共 2ms,读取本身 1ms,给每次读加锁会把成本扩大约 3 倍。若读取持有 500ms 且同时有 50 个读者,互斥锁需约 25 秒才能完成一批,而读锁理论上约 0.5 秒,此时收益明显。

计算还要加入锁服务 QPS、续期流量和读者集合内存,不能只比较业务临界区。

六、替代方案与故障边界

数据库 MVCC 可让读者读取一致快照而不阻塞写者;配置发布可写新版本后原子切换指针;读后写可用版本 CAS 检测变化。这些方案通常比跨进程维护读者集合简单。

若确实使用,读者失联靠租约清理,写者操作携带 fencing token。所有脚本、后台任务和服务都必须遵守协议,否则锁只是局部幻觉。

读锁租约过期后的旧读者还可能继续依据旧快照做决策。若决策最终会写数据,提交时必须重新校验版本或获取写锁,不能把曾经持有读锁当作持续有效的证明。

缓存读取通常也不适合分布式读锁:版本化缓存、失效通知和短暂旧读的成本往往更低,应先明确业务是否真的要求读写互斥。

七、常见误区与追问

  • 误区:读多写少就一定适合读写锁。 读本可无锁时,远程协调是纯成本。
  • 误区:当前没有写者就可放行新读者。 等待写者可能因此饥饿。
  • 误区:读锁直接升级最方便。 多个升级者会形成死锁。
  • 误区:读写锁保证数据库一致性。 它只约束遵守协议的调用者。
  • 追问:如何避免写饥饿? 有写等待后关闭读闸门,排空现有读者。
  • 追问:读者宕机怎么办? 每个读 owner 使用会话或租约自动清理。
  • 追问:何时用 MVCC 替代? 读取只需稳定快照且可用版本处理冲突时。

八、加强记忆

分布式读写锁的判断链是“读是否必须阻写—读是否足够长—写等待是否有界”。实现上维护独立读租约、写 owner 与公平队列,谨慎处理升级;工程上先比较 MVCC、不可变快照和 CAS。即使用锁,迟到写仍要资源侧版本或 fencing 兜底。