在分布式系统中,多个实例可能同时访问共享资源,导致数据不一致或竞态条件问题。分布式锁是一种常用的解决方案,它可以确保同一时间只有一个实例能够访问共享资源。Redis 由于其高性能和原子操作特性,常被用于实现分布式锁。本文将详细介绍如何在 Spring Boot 中使用 Redis 实现分布式锁,包括两种常见方法:手动实现和使用 Redisson。
分布式锁需要满足以下特性:
Redis 提供了 SETNX
(SET
命令的原子性变体)和 EXPIRE
命令,可以用来实现分布式锁。通过 SETNX
确保锁的互斥性,通过 EXPIRE
设置锁的过期时间,避免死锁。
在 Spring Boot 项目中,确保已经添加了 Redis 的依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
配置 RedisTemplate
,并设置合适的序列化器:
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
return template;
}
}
创建一个工具类来实现分布式锁:
package com.example.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class RedisLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public boolean lock(String key, String value, long timeout, TimeUnit unit) {
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
return result != null && result;
}
public boolean unlock(String key, String value) {
String currentValue = redisTemplate.opsForValue().get(key);
if (currentValue != null && currentValue.equals(value)) {
redisTemplate.delete(key);
return true;
}
return false;
}
}
在业务逻辑中使用分布式锁:
package com.example.service;
import com.example.utils.RedisLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Service
public class OrderService {
@Autowired
private RedisLock redisLock;
public void createOrder() {
String lockKey = "order:lock";
String lockValue = UUID.randomUUID().toString();
try {
if (redisLock.lock(lockKey, lockValue, 10, TimeUnit.SECONDS)) {
// 执行业务逻辑
System.out.println("Order created successfully");
} else {
System.out.println("Failed to acquire lock");
}
} finally {
redisLock.unlock(lockKey, lockValue);
}
}
}
Redisson
是一个高性能的 Redis 客户端,提供了丰富的分布式锁功能,支持可重入锁、公平锁等。使用 Redisson
可以更简单地实现分布式锁。
在 pom.xml
文件中添加 Redisson
的依赖:
<dependency>
<groupId>org.redissongroupId>
<artifactId>redisson-spring-boot-starterartifactId>
<version>3.16.5version>
dependency>
在 application.yml
文件中配置 Redisson:
spring:
redis:
host: localhost
port: 6379
password: your-password
redisson:
config:
singleServerConfig:
address: redis://127.0.0.1:6379
password: your-password
创建一个服务类来使用 Redisson
的分布式锁:
package com.example.service;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class OrderService {
@Autowired
private RedissonClient redissonClient;
public void createOrder() {
RLock lock = redissonClient.getLock("order:lock");
try {
// 尝试加锁,最多等待 10 秒,锁持有时间 30 秒
boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (isLocked) {
// 执行业务逻辑
System.out.println("Order created successfully");
} else {
System.out.println("Failed to acquire lock");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
在 Spring Boot 中实现分布式锁有多种方式,手动实现基于 Redis 的锁可以提供更灵活的控制,而使用 Redisson
则可以更简单地实现分布式锁功能。根据具体需求和场景选择合适的方式。
希望本文能帮助你在 Spring Boot 项目中快速实现分布式锁。如果你在实现过程中遇到任何问题,欢迎在评论区留言,我们一起探讨!