Skip to content

大杂烩

INFO

我真的好讨厌八股 每日一道

分布式锁

目前主流的实现方式有三种:数据库、Redis、Zookeeper。

数据库实现(简单但沉重)

这是最直观的方法,利用数据库的唯一索引(Unique Index)或排他锁(FOR UPDATE)。

  1. 唯一索引

创建一个“锁表”,里面有一个 lock_name 字段并设为唯一索引。想加锁就 INSERT 一条记录,成功了就是抢到锁;释放锁就 DELETE 这条记录。

  1. 排他锁

利用 SELECT ... FOR UPDATE。这种方式依赖于数据库事务,事务不提交,锁就不释放。

  • 优点:不需要引入额外的中间件,上手快。
  • 缺点:性能极低,不适合高并发;如果数据库挂了,锁就全废了;清理超时锁(防止死锁)逻辑复杂。

Redis 实现(性能之王 - 最常用)

Redis 凭借其单线程和纯内存操作,成为了分布式锁的“头号玩家”。

核心指令:SETNX + EXPIRE

现在的写法通常是原子性的一条命令:SET lock_key unique_value NX PX 30000。NX:只有 key 不存在时才设置(加锁)。PX:设置 30 秒过期(防止由于程序崩溃导致的死锁)。

进阶:Redisson 框架

在 Java 领域,几乎没人手写 Redis 锁,而是直接用 Redisson。它解决了两个痛点:

  1. 看门狗(Watch Dog)机制:如果你的业务还没跑完,锁快过期了,Redisson 会自动帮你“续命”。
  2. 释放锁的安全校验:通过 Lua 脚本保证“只有加锁的人才能删锁”,防止 A 的锁过期后被 B 捡漏,然后 A 又跑去把 B 的锁给删了。
  • 优点:性能极高,支持超高并发。
  • 缺点:在极端情况下(如主从切换时的同步延迟)可能会出现“多个客户端同时抢到锁”的情况。

Zookeeper 实现(分布式协调的老牌选手)

如果你追求绝对的一致性,哪怕牺牲一点性能,Zookeeper(ZK)是最佳选择。 原理:利用 ZK 的临时顺序节点(Ephemeral Sequential Nodes)。

每个想加锁的客户端都在 ZK 的指定目录下创建一个临时顺序节点,客户端查看自己创建的节点是不是序号最小的。如果是,抢锁成功;如果不是,就监听(Watch)比自己序号稍小的那一个节点。

自动释放:由于是“临时节点”,一旦客户端和 ZK 断开连接(Session 超时),节点会自动消失,锁也就自动释放了。

  • 优点:高可靠性(CP 模型)。自动处理死锁,逻辑严谨。
  • 缺点:性能不如 Redis(频繁创建/删除节点较慢);管理 ZK 集群比较费劲。

分布式

分布式的核心目的:

  1. High Availability(高可用):系统不因为单点故障而崩溃。
  2. Scalability(可伸缩):系统可以通过增加资源来处理更多的请求
  3. Fault Tolerance(容错性):系统能够在部分组件失败的情况下继续运行。
  4. Consistency(一致性):分布式系统中的数据在不同节点之间保持一致。
  5. Partition Tolerance(分区容忍性):系统能够在网络分区的情况下继续运行。