🎓博主介绍:Java、Python、js全栈开发 “多面手”,精通多种编程语言和技术,痴迷于人工智能领域。秉持着对技术的热爱与执着,持续探索创新,愿在此分享交流和学习,与大家共进步。
📖DeepSeek-行业融合之万象视界(附实战案例详解100+)
📖全栈开发环境搭建运行攻略:多语言一站式指南(环境搭建+运行+调试+发布+保姆级详解)
👉感兴趣的可以先收藏起来,希望帮助更多的人
从零到百万级并发:SpringBoot+Redis分布式锁实战演进之路
一、引言
在当今互联网高并发的时代,系统面临着海量请求的挑战。分布式系统中,为了保证数据的一致性和正确性,避免多个进程或线程同时访问和修改共享资源而引发的数据冲突问题,分布式锁成为了必不可少的技术手段。本文将详细介绍如何使用Spring Boot结合Redis实现分布式锁,并逐步演进以应对从零到百万级的并发场景。
二、基础概念
2.1 分布式锁的定义
分布式锁是一种在分布式系统环境下用于控制多个进程或线程对共享资源进行互斥访问的机制。与传统的单机锁不同,分布式锁需要考虑多个节点之间的协调和同步。
2.2 Redis简介
Redis是一个开源的高性能键值对存储数据库,它支持多种数据结构,如字符串、哈希表、列表、集合等。由于其高性能、原子操作和支持分布式部署的特点,Redis成为了实现分布式锁的首选工具之一。
2.3 Spring Boot简介
Spring Boot是一个简化Spring应用开发的框架,它提供了自动配置、快速搭建项目等功能,使得开发者可以更高效地开发Spring应用。
三、Spring Boot集成Redis
3.1 添加依赖
在pom.xml文件中添加Spring Boot和Redis的相关依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
3.2 配置Redis连接
在application.properties文件中配置Redis的连接信息:
spring.redis.host=127.0.0.1
spring.redis.port=6379
3.3 编写Redis配置类
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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
}
四、简单分布式锁的实现
4.1 实现思路
使用Redis的SETNX(SET if Not eXists)命令来实现简单的分布式锁。SETNX命令只有在键不存在时才会设置成功,利用这个特性可以实现锁的互斥性。
4.2 代码实现
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 SimpleRedisLock {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public boolean lock(String key, String value, long expireTime) {
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
return result != null && result;
}
public void unlock(String key) {
redisTemplate.delete(key);
}
}
4.3 使用示例
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SimpleLockController {
@Autowired
private SimpleRedisLock simpleRedisLock;
@GetMapping("/simpleLock")
public String simpleLock() {
String lockKey = "simpleLockKey";
String lockValue = "simpleLockValue";
long expireTime = 10;
if (simpleRedisLock.lock(lockKey, lockValue, expireTime)) {
try {
// 模拟业务处理
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
simpleRedisLock.unlock(lockKey);
}
return "Success";
} else {
return "Failed";
}
}
}
4.4 存在的问题
- 锁过期问题:如果业务处理时间超过了锁的过期时间,会导致其他线程提前获取到锁,从而引发数据不一致的问题。
- 锁释放问题:如果持有锁的线程在执行过程中出现异常,没有正常释放锁,会导致死锁。
五、优化分布式锁:解决锁过期和释放问题
5.1 实现思路
- 自动续期:使用Redis的
EXPIRE命令结合定时任务来实现锁的自动续期,确保在业务处理过程中锁不会过期。 - 异常处理:使用
try-finally块确保锁在任何情况下都会被释放。
5.2 代码实现
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Component
public class OptimizedRedisLock {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private Map<String, Long> lockExpireMap = new HashMap<>();
public boolean lock(String key, String value, long expireTime) {
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
if (result != null && result) {
lockExpireMap.put(key, System.currentTimeMillis() + expireTime * 1000);
return true;
}
return false;
}
public void unlock(String key) {
redisTemplate.delete(key);
lockExpireMap.remove(key);
}
@Scheduled(fixedRate = 5000)
public void renewLockExpire() {
long currentTime = System.currentTimeMillis();
for (Map.Entry<String, Long> entry : lockExpireMap.entrySet()) {
String key = entry.getKey();
Long expireTime = entry.getValue();
if (expireTime - currentTime < 3000) {
redisTemplate.expire(key, 10, TimeUnit.SECONDS);
lockExpireMap.put(key, System.currentTimeMillis() + 10 * 1000);
}
}
}
}
5.3 使用示例
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OptimizedLockController {
@Autowired
private OptimizedRedisLock optimizedRedisLock;
@GetMapping("/optimizedLock")
public String optimizedLock() {
String lockKey = "optimizedLockKey";
String lockValue = "optimizedLockValue";
long expireTime = 10;
if (optimizedRedisLock.lock(lockKey, lockValue, expireTime)) {
try {
// 模拟业务处理
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
optimizedRedisLock.unlock(lockKey);
}
return "Success";
} else {
return "Failed";
}
}
}
六、应对百万级并发:Redis集群和Redisson
6.1 Redis集群
在百万级并发场景下,单个Redis节点可能无法承受高并发的压力,需要使用Redis集群来提高系统的可用性和性能。Redis集群采用分片机制,将数据分散存储在多个节点上。
6.2 Redisson简介
Redisson是一个基于Redis的Java驻内存数据网格(In-Memory Data Grid),它提供了分布式和可扩展的Java数据结构,如分布式锁、分布式集合等。Redisson内部使用了Netty框架,性能非常高,并且支持Redis集群。
6.3 集成Redisson
6.3.1 添加依赖
在pom.xml文件中添加Redisson的依赖:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.1</version>
</dependency>
6.3.2 配置Redisson
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
6.3.3 使用Redisson实现分布式锁
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class RedissonLockController {
@Autowired
private RedissonClient redissonClient;
@GetMapping("/redissonLock")
public String redissonLock() {
String lockKey = "redissonLockKey";
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
// 模拟业务处理
Thread.sleep(5000);
return "Success";
} else {
return "Failed";
}
} catch (InterruptedException e) {
e.printStackTrace();
return "Error";
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
七、总结
通过本文的介绍,我们从简单的分布式锁实现开始,逐步演进到使用Redis集群和Redisson来应对百万级并发场景。在实际开发中,需要根据系统的并发量、业务需求和性能要求来选择合适的分布式锁实现方案。同时,要注意锁的过期时间、释放问题和异常处理,确保系统的稳定性和数据的一致性。

2185

被折叠的 条评论
为什么被折叠?



