22人参与 • 2026-03-02 • Redis
分布式锁是解决分布式系统中多节点并发访问共享资源的核心方案,redis凭借高性能、原子性操作等特性,成为实现分布式锁的主流选择。本文从原理层面拆解redis分布式锁的核心逻辑,并详细分析三种常见实现方式的代码逻辑、优缺点及生产环境注意事项。
一个可靠的分布式锁需满足以下特性:
redis通过以下核心命令支撑分布式锁实现:
| 命令/特性 | 作用 |
|---|---|
set key value nx ex t | 原子执行“不存在则设置(nx)+ 过期时间(ex)”,避免加锁与设超时的拆分操作 |
del key | 删除锁(释放锁),需配合校验锁归属,避免误删 |
| lua脚本 | 将“校验锁归属+释放锁”封装为原子操作,解决释放锁的并发安全问题 |
| redisson(客户端) | 基于redis封装了可重入、自动续期、公平锁等高级特性,简化锁的使用 |
@resource
private stringredistemplate stringredistemplate;
/**
* 示例:扣减库存(基础分布式锁实现)
*/
private void order(){
// 1. 生成唯一锁值(用于校验锁归属,避免误删)
string lockvalue = uuid.randomuuid().tostring();
// 2. 加锁:setnx + 过期时间(原子操作),30秒自动释放
boolean locked = stringredistemplate.opsforvalue()
.setifabsent("product:1001:lock", lockvalue, 30, timeunit.seconds);
try {
// 3. 加锁成功则执行业务逻辑(扣减库存)
if (locked) {
integer count = (integer) stringredistemplate.opsforhash().get("product:1001","number");
if (count > 0) {
stringredistemplate.opsforhash().put("product:1001", "number", count - 1);
}
}
} finally {
// 4. 释放锁:先校验锁归属,再删除(非原子操作)
if (lockvalue.equals(stringredistemplate.opsforvalue().get("product:1001:lock"))) {
stringredistemplate.delete("product:1001:lock");
}
}
}setifabsent(底层是set nx ex)实现原子加锁,同时设置30秒超时,避免死锁;lockvalue,释放锁前校验值是否匹配,防止误删其他客户端的锁;delete会误删新锁;stringredistemplate.opsforhash().get()返回object,强转integer可能出现类型异常(需先判空+类型校验)。@resource
private stringredistemplate stringredistemplate;
private static final string lock_key = "product:1001:lock";
private static final string stock_key = "product:1001:number";
private static final long lock_timeout = 30; // 锁超时时间(秒)
private static final long sleep_time = 100; // 重试间隔(毫秒)
private void order() {
string lockvalue = uuid.randomuuid().tostring();
try {
// 1. 尝试获取锁(原子加锁)
boolean locked = tryacquirelock(lockvalue);
if (!locked) {
// 加锁失败可重试/返回失败(示例直接返回,实际可加循环重试)
return;
}
// 2. 执行业务:获取并扣减库存(简化为string结构,避免hash类型转换问题)
string stockstr = stringredistemplate.opsforvalue().get(stock_key);
if (stockstr == null || integer.parseint(stockstr) <= 0) {
return;
}
stringredistemplate.opsforvalue().set(stock_key, string.valueof(integer.parseint(stockstr) - 1));
} finally {
// 3. 释放锁:lua脚本封装“校验+删除”,保证原子性
releaselock(lockvalue);
}
}
/**
* 原子加锁:set nx ex
*/
private boolean tryacquirelock(string lockvalue) {
return stringredistemplate.opsforvalue()
.setifabsent(lock_key, lockvalue, lock_timeout, timeunit.seconds);
}
/**
* 原子释放锁:lua脚本
*/
private void releaselock(string lockvalue) {
string script = "if redis.call('get', keys[1]) == argv[1] then " +
"return redis.call('del', keys[1]) " +
"else " +
"return 0 " +
"end";
stringredistemplate.execute(
new defaultredisscript<>(script, long.class),
arrays.aslist(lock_key),
lockvalue
);
}tryacquirelock和releaselock方法,提升代码复用性。lock_timeout(30秒),锁会提前过期,导致并发安全问题;integer.parseint(stockstr)未做异常捕获,若库存值非数字会抛出运行时异常;redisson是redis官方推荐的java客户端,内置了分布式锁的完整实现,解决了手动实现的诸多痛点。
@resource
private redissonclient redissonclient;
@resource
private stringredistemplate stringredistemplate;
private void order() {
// 1. 获取分布式锁对象(可重入锁)
rlock lock = redissonclient.getlock("product:1001:lock");
try {
// 2. 加锁:最多等待10秒,锁30秒后自动释放;获取锁成功则执行业务
if (lock.trylock(10, 30, timeunit.seconds)) {
try {
// 3. 扣减库存业务逻辑
integer count = (integer) stringredistemplate.opsforhash().get("product:1001","number");
if (count != null && count > 0) {
stringredistemplate.opsforhash().put("product:1001", "number", count - 1);
}
} finally {
// 4. 手动释放锁(若业务执行完未超时,主动释放)
lock.unlock();
}
}
} catch (interruptedexception e) {
// 5. 中断异常处理,恢复线程中断状态
thread.currentthread().interrupt();
}
}trylock不会导致死锁;trylock(waittime, leasetime, unit)支持“最大等待时间”,加锁失败时会阻塞等待,直到超时或获取到锁;finally块中执行unlock(),但需先判断lock.isheldbycurrentthread(),避免未持有锁时执行解锁抛出异常;trylock会抛出interruptedexception,需捕获并恢复线程中断状态,避免线程状态异常;| 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 方式1(基础版) | 代码简单、无额外依赖 | 释放锁非原子、无续期、易误删锁 | 测试环境、低并发非核心业务 |
| 方式2(lua版) | 释放锁原子化、代码结构清晰 | 无续期、重试逻辑需手动实现、单点风险 | 中小并发、核心逻辑简单场景 |
| 方式3(redisson) | 自动续期、可重入、集群适配 | 引入redisson依赖、配置稍复杂 | 生产环境、高并发核心业务 |
redissonnode或clusterserversconfig,避免主从切换导致锁丢失;redis分布式锁的核心是原子加锁+安全释放+超时兜底:
生产环境中,除非有特殊定制需求,否则优先基于redisson实现分布式锁,既保证可靠性,又降低开发和维护成本。
到此这篇关于redis分布式锁实现的三种方式-基于setnx,lua脚本和redisson的文章就介绍到这了,更多相关redis分布式锁内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论