分布式锁实现方案对比:MySQL vs Redis vs Zookeeper
Executive Summary
核心观点(金字塔原理)
结论先行: 分布式锁是解决分布式环境下资源竞争问题的核心手段,MySQL、Redis、Zookeeper三种方案各有优劣,需根据业务场景选择
支撑论点:
- MySQL方案实现简单但性能较低,适合低频场景
- Redis方案性能最优,Redisson可解决主从同步问题,适合高并发场景
- Zookeeper方案可靠性高,但存在网络分区时的并发问题
SWOT 分析
| 维度 | 分析 |
|---|---|
| S 优势 | MySQL简单易实现;Redis性能高;ZK可靠性强、支持Watch机制避免羊群效应 |
| W 劣势 | MySQL服务重启锁无法释放;Redis主从同步问题;ZK网络分区可能多客户端获锁 |
| O 机会 | 资源竞争控制、分布式事务协调、限流控制 |
| T 威胁 | 死锁风险、锁超时设置不当、网络分区导致一致性问题 |
适用场景
- 低频资源竞争场景(MySQL乐观锁/悲观锁)
- 高并发分布式锁场景(Redis + Redisson)
- 强一致性要求的协调场景(Zookeeper)
问题
- 资源竞争
场景
锁
- 锁(Lock)是并发编程中用于控制多个线程/进程对共享资源访问的同步机制,同一时刻只允许持有锁的一方操作资源
- 锁需要保证三个核心特性:互斥性(Mutual Exclusion,同一时刻只有一个持有者)、可见性(Visibility,锁释放后其他线程能看到最新状态)、原子性(Atomicity,加锁与释放是不可分割的操作)
- 锁粒度的选择直接影响系统性能:粗粒度锁(Coarse-grained Lock)实现简单但并发度低,容易成为瓶颈;细粒度锁(Fine-grained Lock)并发度高但实现复杂,需注意死锁风险
- 根据竞争范围可分为单机锁(JVM内)和分布式锁(跨进程/跨机器),两者解决思路不同
JVM内竞争
- synchronized关键字:基于Object Monitor(对象监视器)实现,JVM内置支持,可修饰方法或代码块;JDK 1.6后引入偏向锁、轻量级锁、重量级锁的锁升级优化
- ReentrantLock:基于AQS(AbstractQueuedSynchronizer)实现,相比synchronized优势明显——支持公平锁/非公平锁切换、tryLock超时获取、可中断等待(lockInterruptibly),灵活性更高
- ReadWriteLock:适用于读多写少场景,允许多个线程同时持有读锁(共享锁),写锁(排他锁)与读锁互斥,显著提升读密集型业务的并发性能
- volatile关键字:保证变量的可见性和有序性(禁止指令重排序),但不保证原子性,适用于状态标志位等简单场景
- CAS(Compare-And-Swap)与Atomic类:基于CPU指令的无锁(Lock-Free)方案,AtomicInteger、AtomicLong、AtomicReference等原子类通过CAS + 自旋实现线程安全,避免上下文切换开销,适合低竞争场景
分布式环境竞争
解决方案
DB:mysql
- 设置唯一主键,insert拿到锁,用完删除。注意唯一性,比如a线程上锁,需要a线程删锁
- 乐观锁,update table set version = xxx where version = xx
- 可能存在问题:
- 一旦上锁后,服务重启,则无法释放锁了,其他线程无法释放
- 解决方案:定时任务处理清除超时一定时间的数据
- 锁非阻塞的:insert失败没有排队队列,需要重新触发重新获取锁
- 客户端处理
- 锁为非重入锁
- 建表时,增加进入次数字段
- 一旦上锁后,服务重启,则无法释放锁了,其他线程无法释放
Redis
- 非重入:set lockKey randomValue NX PX 5000
- 重入:可以基于hash实现可重入锁,key对应的value增加count属性,每次重入+1,释放-1
- 优点:
- redis速度快
- Redission处理多节点加锁问题,处理主从数据同步问题
Zookeeper
- 序号小的获取到锁资源,watch前一个节点,避免”羊群效应”
- 优点
- 内存中,性能高于DB
- 缺点:
- 并发问题:ZK集群网络分区,会出现多个client同时获取到锁的情况