redis原子操作命令
Redis 原子操作命令、应用场景与实例
Redis 基于单线程串行执行命令的模型,所有单个命令都具备天然原子性。通常所说的「原子操作命令」,特指能一步完成「读取 - 修改 - 写入」完整逻辑、天然解决并发竞态问题的命令,以及用于保障多条命令原子执行的机制。
一、原生单命令原子操作(按数据结构分类)
这类命令直接在 Redis 服务端完成完整的读写改逻辑,彻底避免并发下「GET + 计算 + SET」的竞态问题。
1. String 字符串类型(最常用)
表格
| 命令 | 功能说明 | 典型场景 |
|---|---|---|
INCR key | 键值原子 + 1,键不存在则初始化为 0 再加 1 | 页面访问量、点赞数、订单号自增 |
DECR key | 键值原子 - 1 | 库存扣减、在线人数递减 |
INCRBY key increment | 原子增加指定整数 | 批量补库存、积分累加 |
DECRBY key decrement | 原子减少指定整数 | 批量扣减库存 |
INCRBYFLOAT key increment | 原子增加浮点数 | 金额、权重等浮点型计数 |
SET key value NX EX seconds | 原子设置:仅键不存在时生效,同时设置过期时间 | 分布式锁(替代 SETNX+EXPIRE 非原子组合) |
GETSET key value | 原子设置新值并返回旧值 | 数据幂等校验、状态更替 |
示例:
# 初始化商品1001的库存为100
SET goods:1001:stock 100
# 高并发下原子扣减1件库存
DECR goods:1001:stock
同类命令对比(上面的goods:1001:stock就是key)
- DECR key:自减 1
- DECRBY key N:一次性扣减 N,例
DECRBY goods:1001:stock 5一次性减 5 - INCR / INCRBY:增加库存
2. Hash 哈希类型
针对哈希表内单个字段做原子增减,适合多维度聚合计数场景。
| 命令 | 功能说明 | 典型场景 |
|---|---|---|
HINCRBY key field increment | 对哈希指定字段原子增减整数 | 购物车商品数量、用户多维度积分 |
HINCRBYFLOAT key field increment | 对哈希指定字段原子增减浮点数 | 用户余额、商品折扣计算 |
示例:
# 用户1001的购物车中,商品2001的数量+1
HINCRBY cart:1001 goods:2001 1
3. List 列表类型
原子完成列表间的元素转移,保障消息 / 任务的可靠性。
表格
| 命令 | 功能说明 | 典型场景 |
|---|---|---|
RPOPLPUSH source destination | 原子从源列表右端弹出,推入目标列表左端 | 可靠消息队列、任务队列重试 |
BRPOPLPUSH source destination timeout | 阻塞版 RPOPLPUSH,无数据时等待超时 | 异步任务消费,避免轮询空转 |
BLPOP/BRPOP key timeout | 阻塞式弹出列表元素 | 简单消息队列消费 |
示例:
# 原子领取任务:从待处理队列弹出,移入处理中队列
# 防止消费过程中服务宕机导致消息丢失
RPOPLPUSH task:pending task:processing
4. Set / ZSet 集合类型
表格
| 命令 | 功能说明 | 典型场景 |
|---|---|---|
SPOP key count | 原子随机弹出集合中元素 | 抽奖、随机选人 |
SADD key member | 原子添加元素,自动去重 | 用户标签、UV 去重计数 |
ZINCRBY key increment member | 原子增减有序集合成员的分数 | 排行榜积分更新、内容热度排名 |
示例:
# 帖子1001获得1次点赞,排行榜分数+1
ZINCRBY post:hot:rank 1 1001
二、多命令原子保障方案
当业务逻辑需要多条命令组合执行(比如「先判断库存是否充足,再扣减」),单命令无法满足,需要以下方案保障原子性。
1. MULTI / EXEC 事务 + WATCH 乐观锁
- 原理:将多条命令加入队列,
EXEC触发后一次性串行执行,执行期间不会插入其他客户端命令。 - 局限性:不支持回滚,语法错误会导致整体不执行,但运行时错误(如类型不匹配)会继续执行后续命令;无法在命令间做逻辑判断。
- WATCH:监控指定键,若
EXEC前键被其他客户端修改,事务整体放弃执行,实现乐观锁。
适用场景:简单多键批量更新、冲突概率低的场景。
示例:乐观锁实现账户转账
# 监控两个账户余额,防止转账期间被修改
WATCH account:A account:B
# 开启事务
MULTI
DECRBY account:A 100
INCRBY account:B 100
# 执行事务,若监控的键被修改,返回nil
EXEC
2. Lua 脚本(EVAL / EVALSHA)
Redis 2.6 版本支持,整个 Lua 脚本会作为一个整体在 Redis 主线程中原子执行,是最灵活、最常用的多命令原子方案。
- 优势:支持条件判断、循环等复杂逻辑,性能优于事务 + WATCH;
- 注意:脚本中不能执行耗时操作,避免阻塞 Redis 主线程。
适用场景:带条件的库存扣减、分布式锁安全释放、复合业务逻辑原子执行。
示例 1:原子扣减库存(库存不足则不扣减,返回执行结果)
-- 返回1:扣减成功;返回0:库存不足
local stock = redis.call('GET', KEYS[1])
if not stock or tonumber(stock) < tonumber(ARGV[1]) then
return 0
end
redis.call('DECRBY', KEYS[1], ARGV[1])
return 1
执行命令:
EVAL "local stock = redis.call('GET', KEYS[1]) if not stock or tonumber(stock) < tonumber(ARGV[1]) then return 0 end redis.call('DECRBY', KEYS[1], ARGV[1]) return 1" 1 goods:1001:stock 2
示例 2:原子释放分布式锁(仅锁的持有者能删除)
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
end
return 0
三、经典业务场景完整实例
场景 1:固定窗口接口限流
需求:限制单个用户 1 分钟内最多调用接口 100 次。 方案:原子计数 + 过期时间,避免并发下计数错乱。
# 访问时原子计数+1
INCR user:1001:api:limit
# 仅首次访问设置60秒过期,避免重置有效期
EXPIRE user:1001:api:limit 60
# 业务逻辑:返回值 > 100 则触发限流
场景 2:分布式互斥锁
需求:多服务实例下,同一任务同一时间只有一个实例执行。 方案:SET ... NX EX 原子加锁 + Lua 原子解锁
NX:保证只有一个客户端能加锁成功EX 30:自动过期,防止服务宕机后锁永久不释放- 唯一 request_id:解锁时校验持有者,防止误删他人的锁
加锁命令:
SET lock:order:task "uuid-xxx-123" NX EX 30
解锁命令(Lua 原子执行):
EVAL "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) end return 0" 1 lock:order:task "uuid-xxx-123"
场景 3:商品超卖防护
需求:高并发秒杀场景下,绝对不能出现负库存。 方案:Lua 脚本原子执行「库存校验 → 库存扣减」,彻底避免超卖。
单纯使用
DECR会直接扣成负数,无法满足业务要求;校验 + 扣减必须原子执行,因此 Lua 是最优解。
场景 4:可靠任务队列
需求:消费者领取任务后,若宕机任务不丢失,支持重试。 方案:RPOPLPUSH 原子转移任务
- 待处理队列:
task:pending - 处理中队列:
task:processing
消费者领取任务:
RPOPLPUSH task:pending task:processing
任务处理完成后,从处理中队列删除:
LREM task:processing 0 "task_1001"
若消费者宕机,可定时扫描处理中队列的超时任务,重新放回待处理队列。
常见误区
- SETNX + EXPIRE 是原子的:错误,两条命令之间若服务宕机,锁会永久不过期,必须使用
SET ... NX EX单命令原子设置。 - Redis 事务支持回滚:错误,Redis 事务无回滚机制,仅语法错误会整体放弃,运行时错误会继续执行后续命令。
- Lua 脚本可以写复杂长逻辑:错误,Lua 执行期间会阻塞 Redis,耗时过长会影响所有请求,脚本必须简短高效。
基于 Spring Boot + Spring Data Redis,使用通用性最强的 StringRedisTemplate,默认已完成 Redis 基础配置,所有代码可直接复用。
一、基础原子命令 Java 实现
Redis 单命令天然原子,直接调用 Spring 封装的 API 即可,无需额外处理并发。
1. 字符串自增 / 自减(计数、库存场景)
// 原子+1,返回自增后的值
Long viewCount = stringRedisTemplate.opsForValue().increment("page:view:1001");
// 原子增加指定数值(补货10件库存)
Long stock = stringRedisTemplate.opsForValue().increment("goods:1001:stock", 10);
// 原子-1(扣减1件库存)
Long remainStock = stringRedisTemplate.opsForValue().decrement("goods:1001:stock");
2. 原子加锁(SET NX EX,分布式锁核心)
注意:必须使用带过期时间的重载方法,保证「设值 + 过期」单命令原子,禁止分开调用 setIfAbsent + expire
// 返回 true=加锁成功,false=锁已存在
Boolean lockFlag = stringRedisTemplate.opsForValue()
.setIfAbsent("lock:order:1001", "request-uuid-xxx", 30, TimeUnit.SECONDS);
3. 其他常用原子命令
// GETSET:原子设置新值并返回旧值
String oldStatus = stringRedisTemplate.opsForValue().getAndSet("user:1001:status", "online");
// Hash:原子增减指定字段(购物车商品数量+1)
Long cartNum = stringRedisTemplate.opsForHash().increment("cart:1001", "goods:2001", 1);
// List:原子转移元素(可靠队列,从待处理移到处理中)
String task = stringRedisTemplate.opsForList().rightPopAndLeftPush("task:pending", "task:processing");
// ZSet:原子增减成员分数(排行榜热度+1)
Double postScore = stringRedisTemplate.opsForZSet().incrementScore("post:hot:rank", "1001", 1);
二、事务 + WATCH 乐观锁实现
适用于多命令、低冲突的场景,通过 WATCH 监控键实现乐观锁,冲突则事务自动放弃。 必须保证 WATCH、MULTI、EXEC 在同一个 Redis 连接中,因此使用 SessionCallback 绑定连接
/**
* 账户转账:原子扣减A账户、增加B账户,乐观锁防止并发修改
* @param amount 转账金额
* @return true=转账成功,false=并发冲突/余额不足
*/
public boolean transfer(int amount) {
List<Object> result = stringRedisTemplate.execute(new SessionCallback<List<Object>>() {
@Override
public List<Object> execute(RedisOperations operations) throws DataAccessException {
// 1. 监控两个账户键,执行前被修改则事务放弃
operations.watch(Arrays.asList("account:A", "account:B"));
// 2. 业务校验:余额是否充足
String balanceA = (String) operations.opsForValue().get("account:A");
if (balanceA == null || Integer.parseInt(balanceA) < amount) {
operations.unwatch();
return Collections.emptyList();
}
// 3. 开启事务,命令入队
operations.multi();
operations.opsForValue().decrement("account:A", amount);
operations.opsForValue().increment("account:B", amount);
// 4. 执行事务:被修改则返回空集合
return operations.exec();
}
});
return !result.isEmpty();
}
三、Lua 脚本原子执行(复杂逻辑首选)
Lua 脚本整体在 Redis 主线程原子执行,支持条件判断、循环,是生产环境最常用的多命令原子方案。Spring 提供 RedisScript 封装,自动复用脚本(EVALSHA 优化性能)。
示例 1:原子扣减库存(库存不足不扣减,防超卖)
@Component
public class StockService {
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* Lua脚本:库存校验+扣减
* KEYS[1] = 库存key
* ARGV[1] = 扣减数量
* 返回:1=扣减成功,0=库存不足
*/
private static final String DEDUCT_SCRIPT =
"local stock = redis.call('GET', KEYS[1]) " +
"if not stock or tonumber(stock) < tonumber(ARGV[1]) then " +
" return 0 " +
"end " +
"redis.call('DECRBY', KEYS[1], ARGV[1]) " +
"return 1";
// 预编译脚本,指定返回类型
private final RedisScript<Long> deductScript = new DefaultRedisScript<>(DEDUCT_SCRIPT, Long.class);
/**
* 原子扣减库存
*/
public boolean deductStock(Long goodsId, int buyNum) {
String stockKey = "goods:" + goodsId + ":stock";
Long res = stringRedisTemplate.execute(
deductScript,
Collections.singletonList(stockKey),
String.valueOf(buyNum)
);
return res != null && res == 1;
}
}
示例 2:原子释放分布式锁(仅持有者可删除)
@Component
public class RedisDistributedLock {
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* Lua脚本:校验锁持有者后删除
* KEYS[1] = 锁key
* ARGV[1] = 锁唯一标识(请求ID)
* 返回:1=释放成功,0=非持有者
*/
private static final String UNLOCK_SCRIPT =
"if redis.call('GET', KEYS[1]) == ARGV[1] then " +
" return redis.call('DEL', KEYS[1]) " +
"end " +
"return 0";
private final RedisScript<Long> unlockScript = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);
/**
* 尝试加锁
*/
public boolean tryLock(String lockKey, String requestId, long expireSeconds) {
Boolean flag = stringRedisTemplate.opsForValue()
.setIfAbsent(lockKey, requestId, expireSeconds, TimeUnit.SECONDS);
return Boolean.TRUE.equals(flag);
}
/**
* 原子解锁
*/
public boolean unlock(String lockKey, String requestId) {
Long res = stringRedisTemplate.execute(
unlockScript,
Collections.singletonList(lockKey),
requestId
);
return res != null && res == 1;
}
}
使用方式:
String lockKey = "lock:order:create";
String requestId = UUID.randomUUID().toString();
try {
if (redisDistributedLock.tryLock(lockKey, requestId, 30)) {
// 执行业务逻辑
createOrder();
} else {
throw new BusinessException("系统繁忙,请稍后重试");
}
} finally {
// 必须在 finally 中释放锁,避免异常导致锁未释放
redisDistributedLock.unlock(lockKey, requestId);
}
四、经典业务场景完整实现
场景 1:固定窗口接口限流
/**
* 限流规则:单个用户1分钟内最多访问 limit 次
* @return true=允许访问,false=触发限流
*/
public boolean rateLimit(Long userId, int limit) {
String limitKey = "user:" + userId + ":api:limit";
Long count = stringRedisTemplate.opsForValue().increment(limitKey);
// 首次访问设置过期时间,避免重置有效期
if (count != null && count == 1) {
stringRedisTemplate.expire(limitKey, 60, TimeUnit.SECONDS);
}
return count != null && count <= limit;
}
场景 2:可靠任务队列(防消息丢失)
/**
* 消费者领取任务:原子从待处理队列移至处理中队列
* @return 任务内容,null=无待处理任务
*/
public String pickTask() {
return stringRedisTemplate.opsForList()
.rightPopAndLeftPush("task:pending", "task:processing");
}
/**
* 任务完成后,从处理中队列移除
*/
public void finishTask(String taskId) {
stringRedisTemplate.opsForList().remove("task:processing", 0, taskId);
}
关键注意事项
- 连接一致性:事务 + WATCH 必须绑定同一个连接,否则 WATCH 失效;单命令、Lua 脚本天然满足同连接要求。
- Lua 脚本规范:禁止在脚本中执行耗时操作(如全量遍历、大量循环),否则会阻塞 Redis 主线程,影响所有请求。
- 分布式锁规范:加锁必须同时设置过期时间;解锁必须校验持有者,禁止直接 DEL;过期时间需大于业务执行时长。
- 事务局限性:Redis 事务无回滚机制,仅语法错误会整体失败,运行时错误会继续执行后续命令,复杂逻辑优先用 Lua。
Redisson 分布式锁完整实现(Spring Boot 生产级)
Redisson 是 Redis 官方推荐的 Java 级分布式工具集,底层全部基于 Lua 脚本保证原子性,内置可重入、自动续期(看门狗)、故障容错等能力,是生产环境分布式锁的首选方案,远比手动实现 SETNX + Lua 更健壮。
以下示例基于 Spring Boot 2.x/3.x + Redisson 3.25.x,所有代码可直接复用。
一、Spring Boot 整合 Redisson
1. 引入 Maven 依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.25.0</version>
</dependency>
2. 基础配置(单机 Redis 为例)
在 application.yml 中配置,支持单机、哨兵、集群多种模式:
spring:
redis:
# 基础连接信息
host: 127.0.0.1
port: 6379
password: your-password
database: 0
# Redisson 专属配置(可选,也可通过配置类自定义)
redisson:
config: |
singleServerConfig:
connectTimeout: 3000
idleConnectionTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
配置类方式(更灵活,推荐生产使用):
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
// 单机模式
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
.setPassword("your-password")
.setDatabase(0)
.setConnectionMinimumIdleSize(5);
return Redisson.create(config);
}
}
使用时直接注入:
@Resource
private RedissonClient redissonClient;
二、7 种常用分布式锁实现与适用场景
1. 可重入锁 RLock(最核心、最常用)
完全替代手写 SETNX + Lua 锁,支持:
- 原子加锁解锁
- 可重入(同一线程可多次加锁)
- 看门狗自动续期(业务没执行完,锁不会过期)
- 阻塞等待 / 尝试加锁两种模式
代码示例
/**
* 方式1:阻塞式加锁(一直等到获取锁为止)
* 看门狗默认30秒过期,每10秒自动续期,业务执行完才会释放
*/
public void reentrantLockDemo() {
String lockKey = "lock:order:create:1001";
RLock lock = redissonClient.getLock(lockKey);
try {
// 加锁:默认看门狗机制生效,自动续期
lock.lock();
// 执行业务逻辑
doBusiness();
} finally {
// 必须在 finally 释放,防止异常导致死锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
/**
* 方式2:尝试加锁(带超时,最推荐生产使用)
* waitTime:最大等待时间,拿不到锁就放弃
* leaseTime:锁持有超时时间,超过自动释放(指定后看门狗不生效)
*/
public boolean tryLockDemo() {
String lockKey = "lock:stock:deduct:2001";
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试3秒内获取锁,拿到后持有10秒自动释放
boolean getLock = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (!getLock) {
// 加锁失败,返回业务提示
return false;
}
// 执行业务:扣减库存、生成订单
deductStock();
return true;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
看门狗机制说明: 不指定
leaseTime时,锁默认 30 秒过期,Redisson 会每 10 秒自动给锁续期到 30 秒,直到业务执行完成调用unlock。彻底避免「业务没执行完、锁提前过期」的问题。
2. 公平锁 RFairLock
- 特性:严格按照请求加锁的顺序分配锁,先到先得,避免线程饥饿
- 适用场景:对公平性要求高,不允许某些请求一直抢不到锁的场景
public void fairLockDemo() {
String lockKey = "lock:fair:task";
RFairLock fairLock = redissonClient.getFairLock(lockKey);
try {
fairLock.lock();
// 执行业务
} finally {
if (fairLock.isHeldByCurrentThread()) {
fairLock.unlock();
}
}
}
3. 读写锁 RReadWriteLock
- 特性:读锁共享、写锁互斥;读读不阻塞,读写、写写阻塞
- 适用场景:读多写少场景,大幅提升并发性能,比如商品详情、配置信息更新
public void readWriteLockDemo() {
String lockKey = "lock:goods:info:1001";
RReadWriteLock rwLock = redissonClient.getReadWriteLock(lockKey);
// 读锁:多个线程可同时持有,共享锁
RLock readLock = rwLock.readLock();
// 写锁:独占锁,写的时候所有读/写都阻塞
RLock writeLock = rwLock.writeLock();
// 读数据用读锁
try {
readLock.lock();
queryGoodsInfo();
} finally {
readLock.unlock();
}
// 写数据用写锁
try {
writeLock.lock();
updateGoodsInfo();
} finally {
writeLock.unlock();
}
}
4. 联锁 RMultiLock
- 特性:同时对多个锁进行加锁,全部加锁成功才算成功;任意一个失败则整体失败
- 适用场景:需要同时操作多个独立资源,必须全部锁定才能执行业务,比如同时扣减多个商品库存
public void multiLockDemo() {
RLock lock1 = redissonClient.getLock("lock:stock:1001");
RLock lock2 = redissonClient.getLock("lock:stock:1002");
RLock lock3 = redissonClient.getLock("lock:stock:1003");
// 组装联锁
RMultiLock multiLock = redissonClient.getMultiLock(lock1, lock2, lock3);
try {
// 三个锁全部加锁成功才继续
boolean getLock = multiLock.tryLock(5, 15, TimeUnit.SECONDS);
if (getLock) {
// 同时操作三个商品库存
batchDeductStock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
multiLock.unlock();
}
}
5. 红锁 RRedLock
- 特性:基于多个独立 Redis 节点,过半节点加锁成功才算成功,解决单节点宕机导致锁失效的问题
- 适用场景:对锁可靠性要求极高、Redis 多实例部署的核心业务
public void redLockDemo() {
// 需要3个独立Redis节点的RedissonClient实例
RLock lock1 = redissonClient1.getLock("lock:order:pay");
RLock lock2 = redissonClient2.getLock("lock:order:pay");
RLock lock3 = redissonClient3.getLock("lock:order:pay");
RRedLock redLock = redissonClient.getRedLock(lock1, lock2, lock3);
try {
// 过半节点加锁成功才算成功
boolean getLock = redLock.tryLock(3, 10, TimeUnit.SECONDS);
if (getLock) {
doPayBusiness();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
redLock.unlock();
}
}
6. 信号量 RSemaphore
- 特性:控制同时访问资源的并发线程数,本质是分布式计数器
- 适用场景:接口限流、资源池数量控制
/**
* 限流:同一时间最多允许10个线程访问
*/
public boolean semaphoreLimit() {
RSemaphore semaphore = redissonClient.getSemaphore("limit:api:query");
// 初始化总许可数(只初始化一次)
semaphore.trySetPermits(10);
try {
// 获取1个许可,获取不到则阻塞
semaphore.acquire();
// 执行业务
doQuery();
return true;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
} finally {
// 释放许可
semaphore.release();
}
}
三、完整业务实战:订单创建防重复提交
生产级示例,结合 tryLock 实现接口幂等,防止用户重复点击导致重复下单:
@Service
public class OrderService {
@Resource
private RedissonClient redissonClient;
/**
* 创建订单
* @param userId 用户ID
* @param goodsId 商品ID
* @param buyNum 购买数量
*/
public String createOrder(Long userId, Long goodsId, int buyNum) {
// 锁粒度:按用户+商品维度,避免锁太粗影响其他用户
String lockKey = "lock:order:create:" + userId + ":" + goodsId;
RLock lock = redissonClient.getLock(lockKey);
try {
// 等待2秒,锁持有20秒自动释放
boolean getLock = lock.tryLock(2, 20, TimeUnit.SECONDS);
if (!getLock) {
throw new BusinessException("订单处理中,请勿重复提交");
}
// 1. 校验库存
checkStock(goodsId, buyNum);
// 2. 扣减库存
deductStock(goodsId, buyNum);
// 3. 生成订单
String orderNo = generateOrderNo();
saveOrder(orderNo, userId, goodsId, buyNum);
return orderNo;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BusinessException("系统繁忙,请稍后重试");
} finally {
// 仅当前线程持有锁时才释放,避免误删他人锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
// 以下为业务方法省略
private void checkStock(Long goodsId, int buyNum) {}
private void deductStock(Long goodsId, int buyNum) {}
private String generateOrderNo() { return UUID.randomUUID().toString(); }
private void saveOrder(String orderNo, Long userId, Long goodsId, int buyNum) {}
}
四、核心原理与生产避坑
1. 原子性保障
Redisson 所有加锁、解锁、续期操作,底层全部通过 Lua 脚本 在 Redis 端原子执行,和手动写 Lua 原理一致,但边界处理更完善。
2. 可重入实现原理
使用 Redis Hash 结构存储锁信息:
- field:线程唯一标识
- value:重入次数 每次加锁重入次数 + 1,解锁重入次数 - 1,减到 0 时删除锁。
3. 生产避坑点
- 锁粒度要适中:不要全业务共用一把锁,也不要锁粒度过细导致管理混乱,按业务资源维度拆分。
- 必须在 finally 释放锁:防止业务异常导致锁无法释放。
- 优先使用 tryLock:避免无限阻塞,设置合理的等待时间和持有超时。
- 不要在异步线程释放锁:锁和线程绑定,跨线程解锁会抛出异常。
- 长业务建议指定 leaseTime:防止服务宕机后锁长期占用,设置大于业务预估时长的过期时间。
- 读写锁慎用写锁:写锁是独占锁,频繁写会导致读性能大幅下降。
更多推荐


所有评论(0)