1. 分布式锁简介

1.1 什么是分布式锁

在单机应用中,可以使用 Java 内置的锁机制(如 synchronized、ReentrantLock 等)来实现线程间的同步。但在分布式环境下,由于应用部署在多台服务器上,传统的单机锁无法满足需求,这时就需要分布式锁。

分布式锁是一种跨 JVM、跨服务器的锁机制,它能够在分布式系统中对共享资源进行互斥访问控制,确保在同一时间只有一个客户端可以获得锁并执行操作。

1.2 分布式锁应用场景

  1. 防止重复下单:在电商系统中,防止用户重复提交订单
  2. 秒杀系统:控制商品库存的并发访问,避免超卖
  3. 定时任务:确保集群环境下,定时任务只被一个节点执行
  4. 数据一致性保护:保护跨系统的数据一致性操作

1.3 分布式锁的核心要求

  1. 互斥性:在任何时刻,只有一个客户端能持有锁
  2. 可重入性:同一个客户端可以多次获取同一把锁
  3. 防死锁:即使客户端崩溃,锁也应该在一定时间后自动释放
  4. 高可用:分布式锁服务不应成为系统的单点故障
  5. 性能:锁操作应该具备高性能、低延迟的特性

2. Redisson 简介

2.1 什么是 Redisson

Redisson 是一个在 Redis 基础上实现的 Java 驻内存数据网格(In-Memory Data Grid)。它提供了分布式和可扩展的 Java 数据结构,包括分布式锁、分布式集合、分布式对象等功能。

2.2 Redisson 与 Jedis、Lettuce 对比

  • Jedis:Redis 的 Java 客户端,提供了 Redis 命令的基本封装,API 简单直观,但功能相对基础
  • Lettuce:高级 Redis 客户端,基于 Netty,支持异步操作,性能优于 Jedis
  • Redisson:不仅提供了 Redis 客户端功能,还提供了分布式锁、分布式集合等更高级的分布式特性,对分布式开发更加友好

2.3 Redisson 的主要功能

  • 分布式锁和同步器:分布式锁、读写锁、信号量、闭锁等
  • 分布式集合:Map、Set、List 等数据结构的分布式实现
  • 分布式服务:远程服务、实时对象服务等
  • 分布式执行服务:分布式执行服务、调度任务服务等

3. Redisson 分布式锁的实现原理

3.1 基于 Redis 的锁实现

Redisson 的分布式锁基于 Redis 的EVAL命令(执行 Lua 脚本)实现。它使用了一个 Redis 键值对来表示锁,键是锁的名称,值包含锁的持有者信息和过期时间。

基本流程:

  1. 获取锁:通过 Lua 脚本尝试在 Redis 中设置一个键值对,如果键不存在则获取成功
  2. 锁的持有:为该键设置过期时间(避免死锁)
  3. 锁的释放:通过执行 Lua 脚本删除对应的键
  4. 锁的续期:通过看门狗机制延长锁的过期时间

3.2 锁的实现方案

Redisson 提供了多种锁的实现方案:

  1. 普通可重入锁(RLock):最基本的分布式锁实现,支持可重入
  2. 公平锁(RFairLock):按照请求顺序获取锁
  3. 读写锁(RReadWriteLock):读锁共享,写锁独占
  4. 多重锁(RedissonMultiLock):可以组合多个锁为一个锁
  5. 红锁(RedissonRedLock):基于 Redis 集群的高可靠性锁实现,可以抵御部分节点故障

3.3 锁的获取和释放流程

锁的获取

-- KEYS[1]是锁的key,ARGV[1]是线程标识,ARGV[2]是过期时间
if (redis.call('exists', KEYS[1]) == 0) or (redis.call('hexists', KEYS[1], ARGV[1]) == 1) then
redis.call('hincrby', KEYS[1], ARGV[1], 1);
redis.call('pexpire', KEYS[1], ARGV[2]);
return nil;
end;
return redis.call('pttl', KEYS[1]);

锁的释放

-- KEYS[1]是锁的key,ARGV[1]是线程标识
if (redis.call('hexists', KEYS[1], ARGV[1]) == 0) then
return nil;
end;
local counter = redis.call('hincrby', KEYS[1], ARGV[1], -1);
if (counter > 0) then
return 0;
else
redis.call('del', KEYS[1]);
return 1;
end;

4. Redisson 分布式锁的使用

4.1 Maven 依赖配置

<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.23.3</version>
</dependency>

4.2 基本配置

@Configuration
public class RedissonConfig { @Value("${spring.data.redis.host}")
private String host; @Value("${spring.data.redis.port}")
private int port; @Value("${spring.data.redis.password}")
private String password; @Value("${spring.data.redis.database}")
private int database; @Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://" + host + ":" + port)
.setDatabase(database);
if (password != null && !password.isEmpty()) {
config.useSingleServer().setPassword(password);
}
return Redisson.create(config);
}
}

4.3 基本锁的使用

@Service
public class LockService { @Resource
private RedissonClient redissonClient; public void doSomething() {
RLock lock = redissonClient.getLock("myLock"); try {
// 尝试获取锁,最多等待100秒,锁有效期为30秒
boolean isLocked = lock.tryLock(100, 30, TimeUnit.SECONDS); if (isLocked) {
// 业务处理
System.out.println("执行业务逻辑");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 释放锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}

4.4 不同类型锁的使用

  1. 可重入锁 (RLock)
RLock lock = redissonClient.getLock("myLock");
lock.lock();
try {
try {
lock.lock();
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
  1. 公平锁 (RFairLock)
RLock fairLock = redissonClient.getFairLock("myFairLock");
fairLock.lock();
try {
// 业务处理
} finally {
fairLock.unlock();
}
  1. 读写锁 (RReadWriteLock)
RReadWriteLock rwLock = redissonClient.getReadWriteLock("myRWLock");

// 读锁(共享)
RLock readLock = rwLock.readLock();
readLock.lock();
try {
// 读取操作
} finally {
readLock.unlock();
} // 写锁(排他)
RLock writeLock = rwLock.writeLock();
writeLock.lock();
try {
// 写入操作
} finally {
writeLock.unlock();
}
  1. 多重锁 (RedissonMultiLock)
RLock lock1 = redissonClient.getLock("lock1");
RLock lock2 = redissonClient.getLock("lock2");
RLock lock3 = redissonClient.getLock("lock3"); // 组合多个锁
RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2, lock3);
multiLock.lock();
try {
// 业务处理
} finally {
multiLock.unlock();
}

5. 看门狗机制详解

5.1 什么是看门狗机制

看门狗(Watchdog)机制是 Redisson 为分布式锁提供的一种自动续期功能。它能够在客户端持有锁期间,自动延长锁的有效期,防止因为执行时间过长导致锁过期被其他客户端获取,从而破坏互斥性。

5.2 看门狗的工作原理

  1. 当客户端调用lock()方法获取锁时(不设置过期时间),Redisson 会默认设置一个 30 秒的锁有效期
  2. 同时,它会启动一个定时任务,默认每 10 秒检查一次(锁有效期的 1/3 时间)
  3. 如果客户端仍然持有锁,定时任务会自动刷新锁的有效期为 30 秒
  4. 这个过程会一直持续,直到客户端主动释放锁,或者客户端崩溃(此时看门狗停止工作,锁会在 30 秒后自动释放)

5.3 看门狗的关键源码分析

Redisson 中看门狗的核心实现在RedissonLock.java类中:

// 锁的自动续期逻辑
private void scheduleExpirationRenewal(long threadId) {
ExpirationEntry entry = new ExpirationEntry();
ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);
if (oldEntry != null) {
oldEntry.addThreadId(threadId);
} else {
entry.addThreadId(threadId);
renewExpiration();
}
} // 续期定时任务
private void renewExpiration() {
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
// 续期逻辑
// ...
// 每internalLockLeaseTime/3时间后,重新检查并续期
commandExecutor.getConnectionManager().newTimeout(this,
internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
}

5.4 看门狗机制的启用和关闭

启用看门狗机制(默认):

// 不指定过期时间,默认启用看门狗机制
lock.lock();

禁用看门狗机制

// 明确指定过期时间,看门狗机制将被禁用
lock.lock(10, 30, TimeUnit.SECONDS);

5.5 看门狗配置参数

可以通过配置修改看门狗的默认行为:

// 设置锁的默认过期时间(看门狗续期间隔为该值的1/3)
Config config = new Config();
config.setLockWatchdogTimeout(30000); // 30秒
RedissonClient redisson = Redisson.create(config);

6. 分布式锁的最佳实践

6.1 合理使用看门狗机制

  • 对于执行时间不确定的任务,推荐使用看门狗机制
  • 对于执行时间确定且较短的任务,可以明确设置过期时间,关闭看门狗

6.2 锁的粒度选择

  • 尽量降低锁的粒度,例如对特定对象加锁,而不是整个方法
  • 使用不同的锁名称来区分不同的业务操作

6.3 锁的释放保证

  • 始终在 finally 块中释放锁
  • 释放前检查当前线程是否持有锁(isHeldByCurrentThread)
try {
// 业务逻辑
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}

6.4 处理锁的获取失败

  • 设置合理的等待时间
  • 实现重试机制
  • 提供降级策略
int retryCount = 3;
while (retryCount > 0) {
boolean locked = lock.tryLock(5, TimeUnit.SECONDS);
if (locked) {
try {
// 业务逻辑
return result;
} finally {
lock.unlock();
}
}
retryCount--;
Thread.sleep(1000);
}
// 降级处理
return fallbackMethod();

7. Redisson 分布式锁与其他实现的对比

7.1 与 Redis 原生命令实现对比

Redis 原生命令(SETNX + EXPIRE):

  • 优点:实现简单,不依赖额外库
  • 缺点:原子性保证困难,无法解决锁过期问题,不支持可重入

Redisson:

  • 优点:实现了可重入、自动续期、公平锁等高级特性
  • 缺点:额外的依赖,有一定的学习成本

7.2 与 Zookeeper 实现对比

Zookeeper:

  • 优点:强一致性保证,临时节点机制自动释放,有序性支持
  • 缺点:性能较低,适合高可靠低频操作

Redisson:

  • 优点:性能高,功能丰富,适合高频操作
  • 缺点:在某些极端情况下一致性不如 Zookeeper

7.3 各种实现的适用场景

  • Redisson:适合高性能场景,对一致性要求不是极高但要求低延迟
  • Zookeeper:适合高可靠性场景,对性能要求不高但要求强一致性
  • 数据库锁:适合与数据操作紧密结合的场景
  • etcd:适合对可靠性和一致性都有较高要求的中等性能场景

8. 常见问题及解决方案

8.1 锁的误删除问题

问题:一个客户端释放了其他客户端持有的锁

解决方案

  • Redisson 通过锁值内存储线程标识,保证只有持有锁的线程才能释放
  • 释放时通过isHeldByCurrentThread()方法检查

8.2 锁的过期问题

问题:业务执行时间超过锁的有效期

解决方案

  • 使用看门狗机制自动续期
  • 合理评估业务执行时间,设置足够的锁有效期

8.3 缓存崩溃和恢复

问题:Redis 服务器故障或重启

解决方案

  • 使用 Redis 集群提高可用性
  • 在关键业务使用 RedissonRedLock(红锁)
  • 实现业务补偿机制

8.4 性能优化

  • 减小锁粒度
  • 适当设置锁等待超时时间
  • 避免长时间持有锁
  • 使用读写锁分离读写操作

微服务架构的守护者:Redisson 分布式锁与看门狗机制实战指南的更多相关文章

  1. .netcore 微服务快速开发框架 Anno&Viper -分布式锁是个什么鬼

    1.什么是锁 锁是为了解决多线程或者多进程资源竞争的问题. 同一进程的多个线程资源竞争可以用lock解决. lock 关键字可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区. 如果其他线 ...

  2. Spring Cloud构建微服务架构(四)分布式配置中心(续)

    先来回顾一下,在前文中我们完成了什么: 构建了config-server,连接到Git仓库 在Git上创建了一个config-repo目录,用来存储配置信息 构建了config-client,来获取G ...

  3. Spring Cloud构建微服务架构(四)分布式配置中心

    Spring Cloud Config为服务端和客户端提供了分布式系统的外部化配置支持.配置服务器为各应用的所有环境提供了一个中心化的外部配置.它实现了对服务端和客户端对Spring Environm ...

  4. Spring Cloud构建微服务架构(二)分布式配置中心

     注:此文不适合0基础学习者直接阅读,请先完整的将作者关于微服务的博文全部阅读一遍,如果还有疑问,可以再来阅读此文,地址:http://blog.csdn.net/sosfnima/article/d ...

  5. Spring Cloud构建微服务架构

    Dalston版本 由于Brixton和Camden版本的教程已经停止更新,所以笔者计划在2017年上半年完成Dalston版本的教程编写(原计划完成Camden版本教程,但由于写了两篇Dalston ...

  6. 学习一下 SpringCloud (一)-- 从单体架构到微服务架构、代码拆分(maven 聚合)

    一.架构演变 1.系统架构.集群.分布式系统 简单理解 (1)什么是系统架构? [什么是系统架构?] 系统架构 描述了 在应用程序内部,如何根据 业务.技术.灵活性.可扩展性.可维护性 等因素,将系统 ...

  7. Spring Cloud构建微服务架构(三)消息总线

     注:此文不适合0基础学习者直接阅读,请先完整的将作者关于微服务的博文全部阅读一遍,如果还有疑问,可以再来阅读此文,地址:http://blog.csdn.net/sosfnima/article/d ...

  8. 云享专家倪升武:微服务架构盛行的时代,你需要了解点 Spring Boot

    [摘要] 微服务架构的本质在于分布式.去中心化. 随着互联网的高速发展,庞大的用户群体和快速的需求变化已经成为了传统架构的痛点. 在这种情况下,如何从系统架构的角度出发,构建出灵活.易扩展的系统来快速 ...

  9. 《Spring Cloud构建微服务架构》系列博文示例

    SpringCloud-Learning   源码下载地址:http://download.csdn.net/detail/k21325/9650968     本项目内容为Spring Cloud教 ...

  10. 微服务架构 | 2.2 Alibaba Nacos 的统一配置管理

    目录 前言 1. Nacos 配置中心基础知识 1.1 Nacos 在配置中心中的功能 1.2 Nacos 配置管理 Data ID 的构成 1.3 Nacos 配置的回滚机制 1.4 Nacos 配 ...

随机推荐

  1. vs code 添加jquery的智能提示

    1.安装node.js 2.新建VsCodeTestApp文件夹,用vs code打开这个文件夹 3.打开cmd,进入TestApp文件夹所在盘符,然后cd进入VsCodeTestApp C:\Use ...

  2. o3 发布了,摔碎了码农的饭碗

    大家好,我是汤师爷~ 在 2024 年底,OpenAI 发布了最新推理模型 o3.o3模型相当炸裂,在世界级编程比赛中拿下第 175 名,打败 99.9% 的参赛者.AI 写代码都赶上顶级程序员了,程 ...

  3. SpringBoot 集成腾讯云(对象存储、短信)

    https://developer.aliyun.com/article/831473 https://blog.csdn.net/weixin_45626288/article/details/11 ...

  4. 防止SQL注入的五种方法

    1.首先看一下下面两个sql语句的区别: <select id="selectByNameAndPassword" parameterType="java.util ...

  5. Kotlin基础语法

  6. Python内存管理机制和垃圾回收机制的简单理解

    一.内存管理机制 1.由c开发出来的cpython 2.include / objests 3.需要下载python源码包 4.Pyobject:float PyVarObject: 5.在pytho ...

  7. Q:oracle备份表语句

    oracle备份还原表语句 方法1.sql语句(同一数据库服务器) 备份 create table xxx_temp as select * from xxx; 还原 truncate table x ...

  8. USACO24DEC 2D Conveyer Belt S [ 蓝 ] [ 图论建模 ] [ Flood Fill ]

    2D Conveyer Belt:图论建模+洪水填充妙妙题.质量极高. 观察 首先面对这种不断往图里面加点或者边,且满足前面的答案可以由加边后推出的题,第一个想到的就得是倒序枚举加边过程的 trick ...

  9. datawhale-leetcode打卡:038~050题

    两数相加(leetcode 002) # Definition for singly-linked list. # class ListNode: # def __init__(self, val=0 ...

  10. kali linux脚本小子速成

    $如果你耐心看十分钟,你会惊奇的发现我讲的是一堆废话,别急.kali linux博大精深,绝对不是十分钟就能学的完,真正的好东西永远都是夹在屎里,想学你想要的,拿出你的决心来. kali linux用 ...