分布式锁

分布式环境的新问题

随着业务增长,我们部署了多个应用服务器。

架构变化:

单机:
用户 → 应用服务器 → Redis → 数据库

分布式:
用户 → 负载均衡 → 应用服务器 1 ━━━
                  → 应用服务器 2 ━━━┳━→ Redis → 数据库
                  → 应用服务器 3 ━━━

新问题:

场景:用户快速点赞

时间线:
T1: 用户点赞,请求到达服务器 A
T2: 服务器 A 检查未点赞,准备写入
T3: 用户取消点赞,请求到达服务器 B
T4: 服务器 B 检查已点赞,准备删除
T5: 服务器 A 写入点赞记录
T6: 服务器 B 删除点赞记录

结果:
- 服务器 A:用户已点赞
- 服务器 B:用户未点赞
→ 状态不一致!

原因:多服务器并发,没有协调机制。

解决方案:分布式锁。

分布式锁原理

核心思想:

  • 在共享存储(如 Redis)中维护一个锁标记
  • 获取锁的请求才能执行操作
  • 其他请求等待或放弃

示意图:

正常情况(无锁):
服务器 A:读取 → 写入
服务器 B:读取 → 写入
→ 可能冲突

使用分布式锁:
服务器 A:获取锁 → 读取 → 写入 → 释放锁
服务器 B:等待锁...
服务器 A:释放锁
服务器 B:获取锁 → 读取 → 写入 → 释放锁
→ 串行执行,不会冲突

Redis 分布式锁实现

基础实现

改进:自动续期

Redlock 算法

问题:单点故障

单 Redis 实例的问题:

1. Redis 宕机
   - 所有服务无法获取锁
   - 系统不可用

2. 网络分区
   - 应用服务器和 Redis 断连
   - 可能导致多个锁

3. 时钟跳变
   - 服务器时间调整
   - 锁提前过期

Redlock 解决方案

原理:

  • 使用多个独立的 Redis 实例(5 个)
  • 在大多数实例上获取锁才算成功
  • 降低单点故障风险

实现:

性能对比

不同锁方案对比

方案可用性一致性性能复杂度
无锁最高
单机锁
分布式锁(单 Redis)
Redlock

性能测试

结论:锁会显著降低性能,但保证了一致性。

使用场景

适合使用分布式锁的场景

1. 强一致性要求
   - 金融交易
   - 库存扣减
   - 秒杀活动

2. 写冲突概率高
   - 投票系统
   - 限流计数
   - ID 生成

3. 资源互斥
   - 文件上传
   - 任务调度
   - 配置更新

不适合使用分布式锁的场景

1. 高并发读取
   - 文章浏览
   - 点赞查询
   - 推荐列表

2. 可容忍不一致
   - 点赞计数(最终一致)
   - 统计数据
   - 日志记录

3. 性能敏感
   - 高频操作
   - 实时性要求高
   - 用户体验优先

点赞场景分析

点赞是否需要分布式锁?

分析:
1. 写冲突概率:低(不同用户点赞)
2. 一致性要求:中(最终一致即可)
3. 性能要求:高(高并发)

结论:
- 单用户对单文章:可以用唯一索引
- 单文章计数:可以用 INCR 原子操作
- 不需要分布式锁

例外场景:
- 用户快速点赞/取消:可以用乐观锁
- 批量操作:可以用分布式锁

课后练习

练习 1

Redis 分布式锁如何防止”死锁”?

参考答案(3 个标签)
分布式锁死锁Redis

死锁场景:

场景:服务器获取锁后崩溃

时间线:
T1: 服务器 A 获取锁
T2: 服务器 A 崩溃(进程退出)
T3: 锁未释放
T4: 其他服务器无法获取锁
→ 死锁!

解决方案:

练习 2

分布式锁的”超时时间”如何设置?

参考答案(3 个标签)
分布式锁超时设置性能

超时时间设置原则:

练习 3

Redlock 算法有什么缺点?

参考答案(3 个标签)
Redlock分布式锁算法分析

Redlock 的缺点:

1. 实现复杂
   - 需要多个 Redis 实例
   - 运维成本高
   - 资源浪费

2. 性能差
   - 需要访问多个实例
   - 网络往返多
   - 响应时间长

3. 时钟依赖
   - 依赖服务器时钟同步
   - 时钟跳变可能导致问题
   - NTP 不稳定会影响

4. 故障域
   - 网络分区时可能脑裂
   - 多数派要求严格
   - 恢复时间长

5. 成本高
   - 需要更多 Redis 实例
   - 硬件成本增加
   - 维护成本增加

替代方案:

1. 使用 ZooKeeper
   - 优点:强一致性,可靠性高
   - 缺点:性能差,复杂度高

2. 使用 etcd
   - 优点:强一致性,支持租约
   - 缺点:性能差,复杂度高

3. 使用数据库
   - 优点:实现简单,可靠
   - 缺点:性能差,数据库压力大

4. 重新设计,避免使用锁
   - 优点:性能最好
   - 缺点:设计复杂

练习 4

如何实现”可重入”的分布式锁?

参考答案(3 个标签)
分布式锁可重入锁并发

可重入锁实现:

练习 5

分布式锁如何实现”公平锁”?

参考答案(3 个标签)
分布式锁公平锁调度算法

公平锁实现:

思考题

  1. 如果 Redis 主从切换,分布式锁会出问题吗?如何解决?

  2. 如何实现”读写锁”(多个读,一个写)?

  3. 分布式锁和”乐观锁”如何选择?

💡 提示:这些问题没有标准答案,建议结合实际情况深入思考。