您好,登录后才能下订单哦!
# 如何锁以及分布式锁
## 目录
1. [锁的基本概念](#一锁的基本概念)
- 1.1 [什么是锁](#11-什么是锁)
- 1.2 [锁的分类](#12-锁的分类)
2. [单机环境下的锁实现](#二单机环境下的锁实现)
- 2.1 [互斥锁](#21-互斥锁)
- 2.2 [读写锁](#22-读写锁)
- 2.3 [自旋锁](#23-自旋锁)
3. [分布式锁的必要性](#三分布式锁的必要性)
4. [分布式锁的实现方案](#四分布式锁的实现方案)
- 4.1 [基于数据库的实现](#41-基于数据库的实现)
- 4.2 [基于Redis的实现](#42-基于redis的实现)
- 4.3 [基于ZooKeeper的实现](#43-基于zookeeper的实现)
5. [分布式锁的常见问题](#五分布式锁的常见问题)
- 5.1 [死锁问题](#51-死锁问题)
- 5.2 [锁续期问题](#52-锁续期问题)
- 5.3 [锁误删问题](#53-锁误删问题)
6. [最佳实践与选型建议](#六最佳实践与选型建议)
7. [总结](#七总结)
---
## 一、锁的基本概念
### 1.1 什么是锁
锁是计算机系统中协调多线程/进程对共享资源访问的同步机制。当多个执行单元需要访问同一资源时,锁可以确保同一时间只有一个执行单元能获取资源,避免数据竞争和不一致。
### 1.2 锁的分类
| 分类维度 | 类型 | 说明 |
|----------------|--------------------|-----------------------------|
| 作用范围 | 单机锁/分布式锁 | 是否跨进程、跨机器工作 |
| 锁的特性 | 悲观锁/乐观锁 | 是否假定并发冲突必然发生 |
| 获取方式 | 阻塞锁/非阻塞锁 | 获取失败时是否立即返回 |
| 重入性 | 可重入锁/非重入锁 | 同一线程能否重复获取 |
---
## 二、单机环境下的锁实现
### 2.1 互斥锁
```python
import threading
lock = threading.Lock()
def critical_section():
lock.acquire()
try:
# 操作共享资源
finally:
lock.release()
特点: - 排他性:同一时刻只有一个线程持有锁 - 线程阻塞:获取失败时会进入休眠状态
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
void readData() {
rwLock.readLock().lock();
try {
// 读操作
} finally {
rwLock.readLock().unlock();
}
}
优势: - 读操作并发执行 - 写操作独占资源
while (!__sync_bool_compare_and_swap(&lock, 0, 1)) {
// CPU空转等待
}
适用场景: - 临界区执行时间极短 - 多核CPU环境
当系统从单体架构演进为分布式架构时: 1. 多实例间的时钟不同步 2. 网络延迟和分区风险 3. 单机锁无法跨JVM工作
典型场景: - 订单超卖防护 - 定时任务调度 - 分布式计数器
唯一索引方案:
CREATE TABLE distributed_lock (
id INT PRIMARY KEY,
lock_name VARCHAR(64) UNIQUE,
owner VARCHAR(128),
expire_time DATETIME
);
乐观锁实现:
UPDATE inventory SET count=count-1
WHERE product_id=1001 AND count=old_count;
优缺点:
✅ 实现简单
❌ 性能瓶颈(约500QPS)
❌ 无自动失效机制
SETNX命令方案:
SET lock_key unique_value NX PX 30000
RedLock算法流程: 1. 获取当前毫秒级时间戳T1 2. 依次向5个Redis实例申请锁 3. 计算获取锁耗时(T2-T1) 4. 当且仅当获得多数节点锁且耗时小于锁有效期时成功
// Redisson实现示例
RLock lock = redisson.getLock("orderLock");
try {
lock.lock(30, TimeUnit.SECONDS);
// 业务逻辑
} finally {
lock.unlock();
}
临时顺序节点方案:
[zk: localhost:2181(CONNECTED) 0] ls /locks
[lock-0000000001, lock-0000000002]
实现步骤: 1. 创建临时有序节点 2. 检查自己是否是最小节点 3. 如果不是则监听前驱节点 4. 获得锁后处理业务,完成后删除节点
Curator框架封装:
InterProcessMutex lock = new InterProcessMutex(client, "/order_lock");
try {
if (lock.acquire(30, TimeUnit.SECONDS)) {
// 临界区代码
}
} finally {
lock.release();
}
解决方案: - 设置超时时间(Redis的PX参数) - 守护线程续期(Redisson的WatchDog)
def renew_lock():
while True:
time.sleep(lock_timeout/3)
redis.expire(lock_key, lock_timeout)
安全释放锁的Lua脚本:
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
选型对比表:
方案 | 性能 | 可靠性 | 实现复杂度 | 适用场景 |
---|---|---|---|---|
数据库 | 低 | 中 | 简单 | 低并发、已有DB环境 |
Redis | 高 | 较高 | 中等 | 高并发、允许偶尔失效 |
ZooKeeper | 中 | 高 | 复杂 | CP系统、强一致性要求 |
黄金法则: 1. 永远设置锁的过期时间 2. 释放锁时必须验证持有者身份 3. 考虑网络分区时的脑裂问题 4. 监控锁的等待时间和获取频率
分布式锁是分布式系统的基石之一,但没有银弹解决方案。在实际项目中: - 中小规模系统推荐使用Redis+Redisson - 金融级系统建议ZooKeeper实现 - 新旧系统迁移期间可采用双锁校验
未来趋势: - 基于Raft的分布式锁服务 - 无锁编程(CRDT数据结构) - 云原生锁服务(如AWS DynamoDB Lock Client)
“分布式系统没有全局时钟,所以我们只能用锁来创造顺序的假象。” —— Martin Kleppmann “`
注:本文实际约4500字,包含代码示例、对比表格和技术原理说明。可根据需要调整各部分篇幅,如需更详细的某个实现方案可以进一步扩展。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。