本文档风哥主要介绍Redis分布式锁高可用实战,包括分布式锁概念、分布式锁实现原理、分布式锁特点、分布式锁使用场景、分布式锁选择策略、高可用性设计、基于Redis的分布式锁实现、分布式锁使用示例、分布式锁监控与告警以及实战案例等内容,风哥教程参考Redis官方文档等内容编写,适合DBA人员和开发人员在生产环境中使用。
Part01-基础概念与理论知识
1.1 分布式锁概念
分布式锁是一种在分布式系统中用于协调多个进程或线程对共享资源访问的机制。在分布式环境中,由于多个节点独立运行,需要一种全局的锁机制来确保对共享资源的互斥访问。
- 互斥性:同一时间只能有一个线程持有锁
- 可重入性:同一线程可以多次获取同一把锁
- 超时释放:避免死锁,当持有锁的线程崩溃时,锁可以自动释放
- 高可用性:锁服务应该高可用,避免单点故障
- 性能:获取和释放锁的操作应该快速
1.2 分布式锁实现原理
基于Redis的分布式锁实现原理:
- 获取锁:使用Redis的SET命令,设置键值对,并设置过期时间,确保原子性操作
- 释放锁:使用Lua脚本,确保原子性地删除锁
- 超时处理:设置合理的过期时间,避免死锁
- 可重入性:在锁中存储线程标识,实现可重入
1.3 分布式锁特点
Redis分布式锁的特点:
- 高性能:Redis的单线程模型和内存操作,使得获取和释放锁的操作非常快速
- 可靠性:使用SET命令的NX和EX选项,确保原子性操作
- 灵活性:可以根据业务需求设置不同的过期时间
- 高可用性:可以部署Redis集群,提高锁服务的可用性
- 简单易用:实现简单,使用方便
更多视频教程www.fgedu.net.cn
Part02-生产环境规划与建议
2.1 分布式锁使用场景
- 秒杀系统:防止超卖,确保同一商品只能被一个用户购买
- 订单系统:防止重复下单,确保同一用户只能下一个订单
- 库存管理:防止库存超卖,确保库存数量的一致性
- 分布式任务调度:确保同一任务只被一个节点执行
- 数据同步:确保数据的一致性,避免并发更新导致的数据冲突
2.2 分布式锁选择策略
## 1. 基于Redis的分布式锁
– 优点:高性能、简单易用、可靠性高
– 缺点:需要额外的Redis服务
– 适用场景:高并发场景,对性能要求高的场景
## 2. 基于ZooKeeper的分布式锁
– 优点:可靠性高、支持顺序锁
– 缺点:性能相对较低
– 适用场景:对可靠性要求高的场景
## 3. 基于数据库的分布式锁
– 优点:不需要额外的服务
– 缺点:性能低、容易死锁
– 适用场景:并发量低的场景
## 4. 选择建议
– 高并发场景:使用Redis分布式锁
– 高可靠性场景:使用ZooKeeper分布式锁
– 简单场景:使用数据库分布式锁
2.3 高可用性设计
## 1. Redis高可用方案
– 主从复制:提供数据备份和读写分离
– 哨兵模式:自动故障转移
– 集群模式:数据分片和高可用
## 2. 分布式锁高可用设计
– 使用Redis集群:确保锁服务的高可用
– 设置合理的过期时间:避免死锁
– 实现重试机制:处理获取锁失败的情况
– 监控锁的状态:及时发现和处理异常
## 3. 容错设计
– 降级方案:当Redis不可用时,使用备用方案
– 超时处理:设置合理的超时时间,避免长时间等待
– 异常处理:捕获和处理异常,确保系统的稳定性
学习交流加群风哥QQ113257174
Part03-生产环境项目实施方案
3.1 基于Redis的分布式锁实现
## 1. 基本实现
// 获取锁
public boolean tryLock(String key, String value, long expireTime, TimeUnit timeUnit) {
return redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, timeUnit);
}
// 释放锁
public boolean unlock(String key, String value) {
String script = “if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end”;
Object result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(key), value);
return result != null && Long.valueOf(1).equals(result);
}
## 2. 可重入锁实现
public class RedisReentrantLock {
private RedisTemplate
private String lockKey;
private String lockValue;
private int lockCount = 0;
private long expireTime;
private TimeUnit timeUnit;
public RedisReentrantLock(RedisTemplate
this.redisTemplate = redisTemplate;
this.lockKey = lockKey;
this.lockValue = UUID.randomUUID().toString();
this.expireTime = expireTime;
this.timeUnit = timeUnit;
}
public boolean lock() {
if (lockCount > 0) {
lockCount++;
return true;
}
boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, expireTime, timeUnit);
if (locked) {
lockCount = 1;
}
return locked;
}
public boolean unlock() {
if (lockCount == 0) {
return false;
}
lockCount–;
if (lockCount == 0) {
String script = “if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end”;
Object result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(lockKey), lockValue);
return result != null && Long.valueOf(1).equals(result);
}
return true;
}
}
## 3. 分布式锁工具类
public class RedisLockUtil {
private static RedisTemplate
public static void setRedisTemplate(RedisTemplate
RedisLockUtil.redisTemplate = redisTemplate;
}
public static boolean tryLock(String key, String value, long expireTime, TimeUnit timeUnit) {
return redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, timeUnit);
}
public static boolean unlock(String key, String value) {
String script = “if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end”;
Object result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(key), value);
return result != null && Long.valueOf(1).equals(result);
}
public static RedisReentrantLock getReentrantLock(String lockKey, long expireTime, TimeUnit timeUnit) {
return new RedisReentrantLock(redisTemplate, lockKey, expireTime, timeUnit);
}
}
3.2 分布式锁使用示例
## 1. 基本使用
public void doSomething() {
String lockKey = “lock:resource:123”;
String lockValue = UUID.randomUUID().toString();
try {
boolean locked = RedisLockUtil.tryLock(lockKey, lockValue, 5, TimeUnit.SECONDS);
if (locked) {
// 执行业务逻辑
System.out.println(“获取锁成功,执行业务逻辑”);
} else {
System.out.println(“获取锁失败”);
}
} finally {
RedisLockUtil.unlock(lockKey, lockValue);
}
}
## 2. 可重入锁使用
public void doSomethingWithReentrantLock() {
RedisReentrantLock lock = RedisLockUtil.getReentrantLock(“lock:resource:123”, 5, TimeUnit.SECONDS);
try {
if (lock.lock()) {
// 执行业务逻辑
System.out.println(“获取锁成功,执行业务逻辑”);
// 嵌套调用
doSomethingNested(lock);
} else {
System.out.println(“获取锁失败”);
}
} finally {
lock.unlock();
}
}
private void doSomethingNested(RedisReentrantLock lock) {
if (lock.lock()) {
try {
System.out.println(“嵌套获取锁成功,执行业务逻辑”);
} finally {
lock.unlock();
}
}
}
## 3. 重试机制
public void doSomethingWithRetry() {
String lockKey = “lock:resource:123”;
String lockValue = UUID.randomUUID().toString();
int maxRetries = 3;
int retryCount = 0;
boolean locked = false;
while (retryCount < maxRetries && !locked) { locked = RedisLockUtil.tryLock(lockKey, lockValue, 5, TimeUnit.SECONDS); if (!locked) { retryCount++; try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } try { if (locked) { // 执行业务逻辑 System.out.println("获取锁成功,执行业务逻辑"); } else { System.out.println("多次尝试后获取锁失败"); } } finally { if (locked) { RedisLockUtil.unlock(lockKey, lockValue); } } }
3.3 分布式锁监控与告警
## 1. 监控锁的状态
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 keys lock:*
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 ttl lock:resource:123
## 2. 监控锁的使用情况
# 编写监控脚本
#!/bin/bash
# lock_monitor.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: `http://www.fgedu.net.cn`
REDIS_CLI=”/redis/app/bin/redis-cli”
HOST=”192.168.1.100″
PORT=”6379″
PASSWORD=”fgedu@2026″
# 监控锁的数量
LOCK_COUNT=$($REDIS_CLI -h $HOST -p $PORT -a $PASSWORD keys lock:* | wc -l)
echo “当前锁的数量: $LOCK_COUNT”
# 监控过期时间
$REDIS_CLI -h $HOST -p $PORT -a $PASSWORD keys lock:* | xargs -I {} $REDIS_CLI -h $HOST -p $PORT -a $PASSWORD ttl {}
## 3. 告警机制
# 设置告警规则
– 锁的数量超过阈值
– 锁的过期时间异常
– 获取锁失败率过高
## 4. 使用Prometheus监控
# 配置Prometheus监控Redis
– job_name: ‘redis’
static_configs:
– targets: [‘192.168.1.100:9121’]
# 配置Grafana面板,监控锁的使用情况
风哥提示:Redis接口限流是保护系统的重要机制,合理的限流策略可以防止系统过载,确保系统的稳定性和可用性。在实际应用中,需要根据具体业务场景和数据特点,选择合适的限流算法和策略。
Part04-生产案例与实战讲解
4.1 秒杀系统中的分布式锁
## 1. 场景描述
– 秒杀商品,高并发访问
– 防止超卖,确保同一商品只能被一个用户购买
## 2. 解决方案
– 使用Redis分布式锁,确保同一商品的购买操作互斥
– 设置合理的过期时间,避免死锁
– 实现重试机制,提高用户体验
## 3. 实战操作
# 秒杀服务代码
@Service
public class FlashSaleService {
@Autowired
private RedisTemplate
@Autowired
private ProductService productService;
@Autowired
private OrderService orderService;
public boolean flashSale(String productId, String userId) {
String lockKey = “lock:flashsale:product:” + productId;
String lockValue = UUID.randomUUID().toString();
try {
// 获取锁,设置5秒过期时间
boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS);
if (!locked) {
return false; // 获取锁失败,返回
}
// 检查库存
int stock = productService.getStock(productId);
if (stock <= 0) {
return false; // 库存不足,返回
}
// 扣减库存
boolean success = productService.decreaseStock(productId);
if (!success) {
return false; // 扣减库存失败,返回
}
// 创建订单
orderService.createOrder(productId, userId);
return true; // 秒杀成功
} finally {
// 释放锁
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(lockKey), lockValue);
}
}
}
## 4. 测试
$ ab -n 1000 -c 100 http://localhost:8080/flash-sale?productId=2001&userId=1001
# 输出示例
Concurrency Level: 100
Time taken for tests: 2.345 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 123456 bytes
HTML transferred: 12345 bytes
Requests per second: 426.45 [#/sec] (mean)
Time per request: 234.50 [ms] (mean)
Time per request: 2.345 [ms] (mean, across all concurrent requests)
Transfer rate: 51.67 [Kbytes/sec] received
Connection Times (ms):
min mean[+/-sd] median max
Connect: 0 0 0.1 0 1
Processing: 50 234 56.7 230 350
Waiting: 50 234 56.7 230 350
Total: 50 234 56.7 230 350
# 验证库存
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get product:2001:stock
# 输出示例
“0”
4.2 订单系统中的分布式锁
## 1. 场景描述
– 用户下单,防止重复下单
– 确保同一用户只能下一个订单
## 2. 解决方案
– 使用Redis分布式锁,确保同一用户的下单操作互斥
– 设置合理的过期时间,避免死锁
– 实现幂等性设计,防止重复下单
## 3. 实战操作
# 订单服务代码
@Service
public class OrderService {
@Autowired
private RedisTemplate
@Autowired
private UserService userService;
@Autowired
private ProductService productService;
public String createOrder(String userId, String productId, int quantity) {
String lockKey = “lock:order:user:” + userId;
String lockValue = UUID.randomUUID().toString();
try {
// 获取锁,设置5秒过期时间
boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS);
if (!locked) {
throw new RuntimeException(“请勿重复下单”);
}
// 检查用户是否存在
boolean userExists = userService.exists(userId);
if (!userExists) {
throw new RuntimeException(“用户不存在”);
}
// 检查商品是否存在
boolean productExists = productService.exists(productId);
if (!productExists) {
throw new RuntimeException(“商品不存在”);
}
// 检查库存
int stock = productService.getStock(productId);
if (stock < quantity) {
throw new RuntimeException("库存不足");
}
// 扣减库存
boolean success = productService.decreaseStock(productId, quantity);
if (!success) {
throw new RuntimeException("扣减库存失败");
}
// 创建订单
String orderId = UUID.randomUUID().toString();
Order order = new Order();
order.setOrderId(orderId);
order.setUserId(userId);
order.setProductId(productId);
order.setQuantity(quantity);
order.setStatus("created");
order.setCreateTime(new Date());
// 保存订单
saveOrder(order);
return orderId; // 返回订单ID
} finally {
// 释放锁
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(lockKey), lockValue);
}
}
}
## 4. 测试
$ curl -X POST http://localhost:8080/order/create -d “userId=1001&productId=2001&quantity=1”
# 输出示例
{“orderId”:”123e4567-e89b-12d3-a456-426614174000″,”status”:”success”}
# 重复下单测试
$ curl -X POST http://localhost:8080/order/create -d “userId=1001&productId=2001&quantity=1”
# 输出示例
{“status”:”error”,”message”:”请勿重复下单”}
4.3 库存管理中的分布式锁
## 1. 场景描述
– 库存管理,防止库存超卖
– 确保库存数量的一致性
## 2. 解决方案
– 使用Redis分布式锁,确保库存操作的互斥性
– 设置合理的过期时间,避免死锁
– 实现库存扣减的原子性操作
## 3. 实战操作
# 库存服务代码
@Service
public class InventoryService {
@Autowired
private RedisTemplate
public boolean decreaseStock(String productId, int quantity) {
String lockKey = “lock:inventory:product:” + productId;
String lockValue = UUID.randomUUID().toString();
try {
// 获取锁,设置5秒过期时间
boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS);
if (!locked) {
return false; // 获取锁失败,返回
}
// 获取当前库存
String stockKey = “product:” + productId + “:stock”;
String stockStr = redisTemplate.opsForValue().get(stockKey);
if (stockStr == null) {
return false; // 库存不存在,返回
}
int stock = Integer.parseInt(stockStr);
if (stock < quantity) {
return false; // 库存不足,返回
}
// 扣减库存
int newStock = stock - quantity;
redisTemplate.opsForValue().set(stockKey, String.valueOf(newStock));
return true; // 扣减成功
} finally {
// 释放锁
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(lockKey), lockValue);
}
}
public boolean increaseStock(String productId, int quantity) {
String lockKey = “lock:inventory:product:” + productId;
String lockValue = UUID.randomUUID().toString();
try {
// 获取锁,设置5秒过期时间
boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS);
if (!locked) {
return false; // 获取锁失败,返回
}
// 获取当前库存
String stockKey = “product:” + productId + “:stock”;
String stockStr = redisTemplate.opsForValue().get(stockKey);
int stock = 0;
if (stockStr != null) {
stock = Integer.parseInt(stockStr);
}
// 增加库存
int newStock = stock + quantity;
redisTemplate.opsForValue().set(stockKey, String.valueOf(newStock));
return true; // 增加成功
} finally {
// 释放锁
String script = “if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end”;
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(lockKey), lockValue);
}
}
}
## 4. 测试
# 设置初始库存
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 set product:2001:stock “100”
# 扣减库存
$ curl -X POST http://localhost:8080/inventory/decrease -d “productId=2001&quantity=10”
# 输出示例
{“status”:”success”}
# 验证库存
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get product:2001:stock
# 输出示例
“90”
# 增加库存
$ curl -X POST http://localhost:8080/inventory/increase -d “productId=2001&quantity=5”
# 输出示例
{“status”:”success”}
# 验证库存
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get product:2001:stock
# 输出示例
“95”
更多学习教程公众号风哥教程itpux_com
Part05-风哥经验总结与分享
5.1 最佳实践
Redis分布式锁高可用实战最佳实践:
- 使用SET命令:使用Redis的SET命令的NX和EX选项,确保原子性操作,学习交流加群风哥微信: itpux-com
- 设置合理的过期时间:避免死锁,当持有锁的线程崩溃时,锁可以自动释放
- 使用Lua脚本释放锁:确保原子性地删除锁,避免误释放
- 实现重试机制:处理获取锁失败的情况,提高用户体验
- 使用Redis集群:确保锁服务的高可用,避免单点故障
- 监控锁的状态:及时发现和处理异常情况
- 实现可重入锁:支持同一线程多次获取同一把锁
- 容错设计:当Redis不可用时,使用备用方案
5.2 常见问题
- 死锁:设置合理的过期时间,避免死锁
- 误释放锁:使用Lua脚本释放锁,确保只释放自己的锁
- 锁竞争:实现重试机制,处理获取锁失败的情况
- 单点故障:使用Redis集群,确保锁服务的高可用
- 性能问题:优化锁的粒度,减少锁的持有时间
- 超时问题:设置合理的超时时间,避免长时间等待
5.3 优化技巧
## 1. 锁粒度优化
– 减小锁的粒度,只锁定需要保护的资源
– 避免锁定整个方法或整个对象
– 合理设计锁的范围,提高并发性能
## 2. 锁持有时间优化
– 减少锁的持有时间,尽快释放锁
– 将耗时操作放在锁外执行
– 避免在锁内执行网络请求或IO操作
## 3. 重试机制优化
– 设置合理的重试次数和重试间隔
– 使用指数退避算法,避免重试风暴
– 对不同的业务场景设置不同的重试策略
## 4. 高可用性优化
– 使用Redis集群,确保锁服务的高可用
– 实现降级方案,当Redis不可用时,使用备用方案
– 监控锁的状态,及时发现和处理异常情况
## 5. 性能优化
– 使用Redis的Pipeline批量操作,减少网络往返时间
– 优化Redis配置,提高性能
– 合理使用连接池,减少连接建立的开销
通过本文档的学习,您应该掌握了Redis分布式锁高可用实战,能够在生产环境中实施有效的分布式锁方案,确保对共享资源的互斥访问。在实际应用中,需要根据具体业务场景和数据特点,选择合适的分布式锁实现方案,确保系统的稳定性和性能。
风哥提示:Redis分布式锁是分布式系统中常用的协调机制,合理的使用可以确保对共享资源的互斥访问。在实际应用中,需要根据具体业务场景和数据特点,选择合适的分布式锁实现方案。
from Redis视频:www.itpux.com
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
