如何锁以及分布式锁

发布时间:2022-01-15 17:40:04 作者:柒染
来源:亿速云 阅读:97
# 如何锁以及分布式锁

## 目录
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()

特点: - 排他性:同一时刻只有一个线程持有锁 - 线程阻塞:获取失败时会进入休眠状态

2.2 读写锁

ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

void readData() {
    rwLock.readLock().lock();
    try {
        // 读操作
    } finally {
        rwLock.readLock().unlock();
    }
}

优势: - 读操作并发执行 - 写操作独占资源

2.3 自旋锁

while (!__sync_bool_compare_and_swap(&lock, 0, 1)) {
    // CPU空转等待
}

适用场景: - 临界区执行时间极短 - 多核CPU环境


三、分布式锁的必要性

当系统从单体架构演进为分布式架构时: 1. 多实例间的时钟不同步 2. 网络延迟和分区风险 3. 单机锁无法跨JVM工作

典型场景: - 订单超卖防护 - 定时任务调度 - 分布式计数器


四、分布式锁的实现方案

4.1 基于数据库的实现

唯一索引方案:

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)
❌ 无自动失效机制

4.2 基于Redis的实现

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();
}

4.3 基于ZooKeeper的实现

临时顺序节点方案:

[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();
}

五、分布式锁的常见问题

5.1 死锁问题

解决方案: - 设置超时时间(Redis的PX参数) - 守护线程续期(Redisson的WatchDog)

5.2 锁续期问题

def renew_lock():
    while True:
        time.sleep(lock_timeout/3)
        redis.expire(lock_key, lock_timeout)

5.3 锁误删问题

安全释放锁的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字,包含代码示例、对比表格和技术原理说明。可根据需要调整各部分篇幅,如需更详细的某个实现方案可以进一步扩展。

推荐阅读:
  1. 分布式锁介绍
  2. zookeeper(3)分布式锁

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

分布式锁

上一篇:物联网开源平台和工具有哪些

下一篇:springboot整合quartz定时任务框架的方法是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》