0
点赞
收藏
分享

微信扫一扫

Spring Boot 3.0x的Redis 分布式锁的概念和原理

Spring Boot 中的 Redis 分布式锁

在分布式系统中,多个进程同时访问共享资源时,很容易出现并发问题。为了避免这些问题,我们可以使用分布式锁来保证共享资源的独占性。Redis 是一款非常流行的分布式缓存,它也提供了分布式锁的功能。在 Spring Boot 中,我们可以很容易地使用 Redis 分布式锁来管理并发访问。

本文将介绍 Redis 分布式锁的概念和原理,并说明如何在 Spring Boot 中使用它们。


Redis 分布式锁的概念和原理

Redis 分布式锁是一种基于 Redis 的分布式锁解决方案。它的原理是利用 Redis 的原子性操作实现锁的获取和释放,从而保证共享资源的独占性。

Spring Boot 3.0x的Redis 分布式锁的概念和原理_分布式锁

spring boot 项目引入

1

2

3

4

5


<dependency>

<groupId>com.baomidou</groupId>

<artifactId>lock4j-redis-template-spring-boot-starter</artifactId>

<version>2.2.7</version>

</dependency>


在需要加分布式锁的方法上,添加注解@Lock4j

1.@Lock4j 注解的功能
获取锁超时(acquireTimeout):指定在获取锁时的等待时间,默认情况下是 3 秒。如果在这段时间内无法获取到锁,可能会抛出异常或进行相应的处理。

锁过期时间(expire):指定锁的过期时间,默认是 30 秒。如果在这段时间内锁没有被手动释放,它会自动失效。这个机制通常用于防止死锁。

SpEL 表达式支持:@Lock4j 支持使用 SpEL 表达式来动态生成锁的键(key),例如通过方法参数生成唯一的锁标识。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25


@Service

public class DemoServiceImpl {

/**

默认获取锁超时3秒,30秒锁过期

这个方法使用了 @Lock4j 注解,并且没有显式地配置任何参数。

默认行为:获取锁时的超时时间为 3 秒,锁的过期时间为 30 秒。

适用场景:在简单的场景下,simple() 方法会被锁住,直到锁被释放或超时。在锁的持有期间,其他线程无法进入该方法。

**/

@Lock4j

public void simple() {

//需要执行的方法

}

/**

完全配置,支持spel,这个方法使用了 @Lock4j 注解,并且自定义了多个参数。

配置详解:

keys = {"#user.id", "#user.name"}:使用 SpEL 表达式动态生成锁的键。这里锁的键由 user 对象的 id 和 name 属性组成,确保了锁的唯一性。

expire = 60000:指定锁的过期时间为 60 秒(即1分钟)。

acquireTimeout = 1000:指定获取锁的超时时间为 1 秒。

适用场景:当方法涉及到特定用户操作时,使用 customMethod() 来确保同一用户(根据 id 和 name 唯一确定)不会被多个线程同时操作。只有在获取到锁的情况下,才会执行方法逻辑,锁在1秒内未获取到,则会抛出异常或执行相应处理。

**/

@Lock4j(keys = {"#user.id", "#user.name"}, expire = 60000, acquireTimeout = 1000)

public User customMethod(User user) {

return user;

}

}


设置配置文件的

1

2

3

4

5


lock4j:

acquire-timeout: 3000 # 默认获取锁超时时间为3秒

expire: 30000 # 默认锁的过期时间为30秒

primary-executor: com.baomidou.lock.executor.RedisTemplateLockExecutor # 使用RedisTemplate作为默认的锁执行器

lock-key-prefix: lock4j # 锁key的前缀,默认为lock4j


配置项详解acquire-timeout: 3000

含义:指定全局默认的获取锁的超时时间,单位为毫秒。默认设置为3秒(3000毫秒)。如果在这个时间内未能获取到锁,将触发相应的处理逻辑(比如抛出异常)。
使用场景:适用于全局大多数锁的场景,可以减少在每个注解中重复配置的需要。
expire: 30000

含义:指定全局默认的锁过期时间,单位为毫秒。默认设置为30秒(30000毫秒)。在这个时间内,如果锁没有被释放,它将自动失效。
使用场景:适用于防止死锁的全局场景,确保锁在一定时间内自动释放,避免持有锁的线程因故障而导致锁无法释放。
primary-executor: com.baomidou.lock.executor.RedisTemplateLockExecutor

含义:指定默认使用的分布式锁执行器。这个配置定义了使用哪种方式来实现锁的逻辑,常见的选项包括 Redisson, RedisTemplate, 和 Zookeeper。
使用场景:这里指定使用 RedisTemplateLockExecutor 来实现分布式锁逻辑,适用于大多数基于Redis的分布式系统。
lock-key-prefix: lock4j

含义:为锁的键(key)指定一个全局前缀。这个前缀会被添加到所有的锁键之前,以确保锁键的唯一性。
使用场景:适用于多个应用共享一个Redis实例时,通过设置前缀来防止不同应用之间的锁键冲突。

用法

1

2

3

4


@Lock4j(executor = RedissonLockExecutor.class)

public Boolean test() {

return "true";

}


自定义锁key生成器,默认的锁key生成器为 com.baomidou.lock.DefaultLockKeyBuilder

1

2

3

4

5

6

7

8

9


@Component

public class DynamicLockKeyBuilder extends DefaultLockKeyBuilder {

@Override

public String buildKey(MethodInvocation invocation, String[] definitionKeys) {

String key = super.buildKey(invocation, definitionKeys);

//需要执行的方法

return key;

}

}


自定义锁获取失败策略,默认的锁获取失败策略为 com.baomidou.lock.DefaultLockFailureStrategy

1

2

3

4

5

6

7


@Component

public class MyLockFailureStrategy implements LockFailureStrategy {

@Override

public void onLockFailure(String key, long acquireTimeout, int acquireCount) {

//需要执行的方法或者业务代码

}

}


手动上锁或手动解锁

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21


@Service

@AllArgsConstructor

public class SysPaymentInfoServiceImpl extends ServiceImpl<SysPaymentInfoMapper, SysPaymentInfo> implements SysPaymentInfoService {

private final LockTemplate lockTemplate;

public void programmaticLock(String userId) {

// 执行查询业务操作 不上锁

// 业务区域

// 获取锁

final LockInfo lockInfo = lockTemplate.lock(userId, 30000L, 5000L, RedissonLockExecutor.class);

if (null == lockInfo) {

throw new RuntimeException("业务处理中,请稍后再试!");

}

// 获取锁成功,处理相关业务

try {

Console.log("执行简单方法 , 当前线程:" + Thread.currentThread().getName() + " , counter:" + (counter++));

} finally {

//释放锁

lockTemplate.releaseLock(lockInfo);

}

//结束

}


指定时间内不释放锁(限流)

1

2

3

4

5


// 用户在5秒内只能访问1次

@Lock4j(keys = {"#user.id"}, acquireTimeout = 0, expire = 5000, autoRelease = false)

public Boolean test(User user) {

return "true";

}


1.@Lock4j(keys = {“#user.id”})
含义:使用 SpEL 表达式 #user.id 生成锁的键。锁的键与传入的 user.id 关联,确保锁的唯一性。这样可以保证每个用户(根据其 id)都会有一个唯一的锁。
2.acquireTimeout = 0
含义:获取锁的超时时间设置为 0,表示立即尝试获取锁,不等待。如果无法获取锁,则直接放弃操作。
使用场景:当你希望方法调用立即失败而不等待时,这个配置很有用。适用于需要快速响应的场景。
3.expire = 5000
含义:锁的过期时间设置为 5000 毫秒(5 秒)。锁在 5 秒后自动失效,释放锁资源。
使用场景:这个配置用于控制用户只能在 5 秒内访问一次该方法,5 秒后锁自动失效,用户可以再次访问。
4.autoRelease = false
含义:设置 autoRelease = false 表示锁不会在方法执行完毕后自动释放。通常,默认情况下,锁在方法执行完毕后会自动释放,但在这里显式关闭了这个功能。
使用场景:这种配置可能用于场景中需要手动控制锁的释放,而不是依赖方法执行结束后自动释放的情况。例如,你可能希望锁在一定时间后自然过期,而不是方法执行完毕就立即释放。

举报

相关推荐

0 条评论