自己用 redis setnx 实现的分布式锁有各种坑(锁续期、误删、非原子)。redisson 是官方推荐的分布式锁方案,开箱即用。
一、引入依赖
<dependency>
<groupid>org.redisson</groupid>
<artifactid>redisson-spring-boot-starter</artifactid>
<version>3.25.0</version>
</dependency>二、配置
@configuration
public class redissonconfig {
@bean
public redissonclient redissonclient() {
config config = new config();
config.usesingleserver()
.setaddress("redis://localhost:6379");
return redisson.create(config);
}
}
三、基础用法
@service
public class seckillservice {
@autowired
private redissonclient redissonclient;
@autowired
private stringredistemplate redistemplate;
public resultvo<?> seckill(long productid, long userid) {
string lockkey = "lock:seckill:" + productid;
rlock lock = redissonclient.getlock(lockkey);
// 尝试获取锁,最多等3秒,锁自动续期
boolean locked = false;
try {
locked = lock.trylock(3, 30, timeunit.seconds);
if (!locked) {
return resultvo.error(429, "操作太频繁,请稍后重试");
}
// 业务逻辑
return doseckill(productid, userid);
} catch (interruptedexception e) {
return resultvo.error(500, "系统异常");
} finally {
// 释放锁(只有持有锁的线程能释放)
if (locked && lock.isheldbycurrentthread()) {
lock.unlock();
}
}
}
}
四、注解式锁
@target(elementtype.method)
@retention(retentionpolicy.runtime)
public @interface redissonlock {
/** 锁的key(支持spel) */
string key();
/** 等待锁时间 */
long waittime() default 3;
/** 锁过期时间 */
long leasetime() default 30;
}
@aspect
@component
public class redissonlockaspect {
@autowired
private redissonclient redissonclient;
@around("@annotation(lock)")
public object around(proceedingjoinpoint pjp, redissonlock lock) throws throwable {
string key = parsekey(lock.key(), pjp);
rlock rlock = redissonclient.getlock(key);
boolean locked = rlock.trylock(lock.waittime(), lock.leasetime(), timeunit.seconds);
if (!locked) {
throw new businessexception(429, "操作太频繁");
}
try {
return pjp.proceed();
} finally {
if (rlock.isheldbycurrentthread()) {
rlock.unlock();
}
}
}
}
五、可重入锁
// redisson 的锁是可重入的
// 同一个线程可以多次获取同一个锁
public void methoda() {
rlock lock = redissonclient.getlock("lock:test");
lock.lock();
methodb(); // 内部再次获取同一个锁,不会死锁
lock.unlock();
}
public void methodb() {
rlock lock = redissonclient.getlock("lock:test");
lock.lock();
// 业务逻辑
lock.unlock();
}
六、公平锁
// 公平锁:按请求顺序获取锁
rlock fairlock = redissonclient.getfairlock("lock:fair");
七、读写锁
// 读锁:多个线程可同时读
// 写锁:独占
rreadwritelock rwlock = redissonclient.getreadwritelock("lock:rw");
rlock readlock = rwlock.readlock(); // 读锁
rlock writelock = rwlock.writelock(); // 写锁
八、redisson vs 手写 setnx
| 特性 | 手写 setnx | redisson |
|---|---|---|
| 锁续期 | ❌ 自己实现 | ✅ 自动续期 |
| 释放校验 | ❌ 可能误删 | ✅ 校验线程 |
| 可重入 | ❌ | ✅ |
| 公平锁 | ❌ | ✅ |
| 读写锁 | ❌ | ✅ |
| 看门狗 | ❌ | ✅ |
九、看门狗机制
// redisson 的锁默认使用看门狗(watchdog)
// 如果业务执行时间超过锁的过期时间(默认30秒),看门狗会自动续期
// 每 10 秒续期一次,直到业务完成释放锁
// 注意:只有不指定 leasetime 时看门狗才生效
rlock lock = redissonclient.getlock("lock:test");
lock.lock(); // 不指定leasetime → 看门狗生效
十、秒杀场景最佳实践
@service
public class seckillservice {
@redissonlock(key = "seckill:#productid", waittime = 3, leasetime = 30)
public resultvo<?> doseckill(@pathvariable long productid, long userid) {
// 前置校验
seckillproduct product = productservice.getbyid(productid);
if (product == null || product.getstatus() != 1) {
return resultvo.error(404, "商品不存在");
}
// 时间校验
localdatetime now = localdatetime.now();
if (now.isbefore(product.getseckillstart())) {
return resultvo.error(400, "秒杀未开始");
}
// 库存扣减
int rows = productmapper.decrementstock(productid);
if (rows == 0) {
return resultvo.error(400, "库存不足");
}
// 创建订单
return resultvo.success("秒杀成功");
}
}
到此这篇关于springboot整合redisson实现分布式锁的最佳实践的文章就介绍到这了,更多相关springboot redisson分布式锁内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论