大杂烩
INFO
我真的好讨厌八股 每日一道
分布式锁
目前主流的实现方式有三种:数据库、Redis、Zookeeper。
数据库实现(简单但沉重)
这是最直观的方法,利用数据库的唯一索引(Unique Index)或排他锁(FOR UPDATE)。
- 唯一索引
创建一个“锁表”,里面有一个 lock_name 字段并设为唯一索引。想加锁就 INSERT 一条记录,成功了就是抢到锁;释放锁就 DELETE 这条记录。
- 排他锁
利用 SELECT ... FOR UPDATE。这种方式依赖于数据库事务,事务不提交,锁就不释放。
- 优点:不需要引入额外的中间件,上手快。
- 缺点:性能极低,不适合高并发;如果数据库挂了,锁就全废了;清理超时锁(防止死锁)逻辑复杂。
Redis 实现(性能之王 - 最常用)
Redis 凭借其单线程和纯内存操作,成为了分布式锁的“头号玩家”。
核心指令:SETNX + EXPIRE
现在的写法通常是原子性的一条命令:SET lock_key unique_value NX PX 30000。NX:只有 key 不存在时才设置(加锁)。PX:设置 30 秒过期(防止由于程序崩溃导致的死锁)。
进阶:Redisson 框架
在 Java 领域,几乎没人手写 Redis 锁,而是直接用 Redisson。它解决了两个痛点:
- 看门狗(Watch Dog)机制:如果你的业务还没跑完,锁快过期了,Redisson 会自动帮你“续命”。
- 释放锁的安全校验:通过 Lua 脚本保证“只有加锁的人才能删锁”,防止 A 的锁过期后被 B 捡漏,然后 A 又跑去把 B 的锁给删了。
- 优点:性能极高,支持超高并发。
- 缺点:在极端情况下(如主从切换时的同步延迟)可能会出现“多个客户端同时抢到锁”的情况。
Zookeeper 实现(分布式协调的老牌选手)
如果你追求绝对的一致性,哪怕牺牲一点性能,Zookeeper(ZK)是最佳选择。 原理:利用 ZK 的临时顺序节点(Ephemeral Sequential Nodes)。
每个想加锁的客户端都在 ZK 的指定目录下创建一个临时顺序节点,客户端查看自己创建的节点是不是序号最小的。如果是,抢锁成功;如果不是,就监听(Watch)比自己序号稍小的那一个节点。
自动释放:由于是“临时节点”,一旦客户端和 ZK 断开连接(Session 超时),节点会自动消失,锁也就自动释放了。
- 优点:高可靠性(CP 模型)。自动处理死锁,逻辑严谨。
- 缺点:性能不如 Redis(频繁创建/删除节点较慢);管理 ZK 集群比较费劲。
分布式
分布式的核心目的:
- High Availability(高可用):系统不因为单点故障而崩溃。
- Scalability(可伸缩):系统可以通过增加资源来处理更多的请求
- Fault Tolerance(容错性):系统能够在部分组件失败的情况下继续运行。
- Consistency(一致性):分布式系统中的数据在不同节点之间保持一致。
- Partition Tolerance(分区容忍性):系统能够在网络分区的情况下继续运行。