怎么理解Redis中的分布式锁
发表于:2025-01-19 作者:千家信息网编辑
千家信息网最后更新 2025年01月19日,本篇内容介绍了"怎么理解Redis中的分布式锁"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Redi
千家信息网最后更新 2025年01月19日怎么理解Redis中的分布式锁
本篇内容介绍了"怎么理解Redis中的分布式锁"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
Redis 分布式锁
大家项目中都会使用到分布式锁把,通常用来做数据的有序操作场景,比如一笔订单退款(如果可以退多次的情况)。或者用户多端下单。【相关推荐:Redis视频教程】
Maven 依赖
我主要是基于 Spring-Boot 2.1.2
+ Jedis
进行实现
4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.2.RELEASE cn.edu.cqvie redis-lock 1.0-SNAPSHOT UTF-8 1.8 2.9.0 5.0.7 org.springframework.boot spring-boot-autoconfigure org.springframework.data spring-data-redis redis.clients jedis ${redis.version} org.springframework.boot spring-boot-starter-logging org.slf4j log4j-over-slf4j org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin
配置文件
application.properties
配置文件内容如下:
spring.redis.host=127.0.0.1spring.redis.port=6379spring.redis.password=spring.redis.timeout=30000spring.redis.jedis.pool.max-active=8spring.redis.jedis.pool.min-idle=2spring.redis.jedis.pool.max-idle=4logging.level.root=INFO
接口定义
接口定义,对于锁我们核心其实就连个方法 lock
和 unlock
.
public interface RedisLock { long TIMEOUT_MILLIS = 30000; int RETRY_MILLIS = 30000; long SLEEP_MILLIS = 10; boolean tryLock(String key); boolean lock(String key); boolean lock(String key, long expire); boolean lock(String key, long expire, long retryTimes); boolean unlock(String key);}
分布式锁实现
我的实现方式是通过 setnx 方式实现了,如果存在 tryLock
逻辑的话,会通过 自旋
的方式重试
// AbstractRedisLock.java 抽象类public abstract class AbstractRedisLock implements RedisLock { @Override public boolean lock(String key) { return lock(key, TIMEOUT_MILLIS); } @Override public boolean lock(String key, long expire) { return lock(key, TIMEOUT_MILLIS, RETRY_MILLIS); }}// 具体实现@Componentpublic class RedisLockImpl extends AbstractRedisLock { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RedisTemplateredisTemplate; private ThreadLocal threadLocal = new ThreadLocal (); private static final String UNLOCK_LUA; static { StringBuilder sb = new StringBuilder(); sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] "); sb.append("then "); sb.append(" return redis.call(\"del\",KEYS[1]) "); sb.append("else "); sb.append(" return 0 "); sb.append("end "); UNLOCK_LUA = sb.toString(); } @Override public boolean tryLock(String key) { return tryLock(key, TIMEOUT_MILLIS); } public boolean tryLock(String key, long expire) { try { return !StringUtils.isEmpty(redisTemplate.execute((RedisCallback ) connection -> { JedisCommands commands = (JedisCommands) connection.getNativeConnection(); String uuid = UUID.randomUUID().toString(); threadLocal.set(uuid); return commands.set(key, uuid, "NX", "PX", expire); })); } catch (Throwable e) { logger.error("set redis occurred an exception", e); } return false; } @Override public boolean lock(String key, long expire, long retryTimes) { boolean result = tryLock(key, expire); while (!result && retryTimes-- > 0) { try { logger.debug("lock failed, retrying...{}", retryTimes); Thread.sleep(SLEEP_MILLIS); } catch (InterruptedException e) { return false; } result = tryLock(key, expire); } return result; } @Override public boolean unlock(String key) { try { List keys = Collections.singletonList(key); List args = Collections.singletonList(threadLocal.get()); Long result = redisTemplate.execute((RedisCallback ) connection -> { Object nativeConnection = connection.getNativeConnection(); if (nativeConnection instanceof JedisCluster) { return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args); } if (nativeConnection instanceof Jedis) { return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args); } return 0L; }); return result != null && result > 0; } catch (Throwable e) { logger.error("unlock occurred an exception", e); } return false; }}
测试代码
最后再来看看如何使用吧. (下面是一个模拟秒杀的场景)
@RunWith(SpringRunner.class)@SpringBootTestpublic class RedisLockImplTest { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RedisLock redisLock; @Autowired private StringRedisTemplate redisTemplate; private ExecutorService executors = Executors.newScheduledThreadPool(8); @Test public void lock() { // 初始化库存 redisTemplate.opsForValue().set("goods-seckill", "10"); ListfutureList = new ArrayList<>(); for (int i = 0; i < 100; i++) { futureList.add(executors.submit(this::seckill)); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } // 等待结果,防止主线程退出 futureList.forEach(action -> { try { action.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } }); } public int seckill() { String key = "goods"; try { redisLock.lock(key); int num = Integer.valueOf(Objects.requireNonNull(redisTemplate.opsForValue().get("goods-seckill"))); if (num > 0) { redisTemplate.opsForValue().set("goods-seckill", String.valueOf(--num)); logger.info("秒杀成功,剩余库存:{}", num); } else { logger.error("秒杀失败,剩余库存:{}", num); } return num; } catch (Throwable e) { logger.error("seckill exception", e); } finally { redisLock.unlock(key); } return 0; }}
总结
本文是 Redis 锁的一种简单的实现方式,基于 jedis
实现了锁的重试操作。 但是缺点还是有的,不支持锁的自动续期,锁的重入,以及公平性(目前通过自旋的方式实现,相当于是非公平的方式)。
"怎么理解Redis中的分布式锁"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!
方式
分布式
内容
库存
场景
情况
接口
文件
更多
知识
剩余
配置
实用
有序
成功
学有所成
接下来
代码
公平性
困境
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
吴中区品质网络技术诚信经营
网络数据库试卷及答案
荒野乱斗是什么服务器
喋血复仇什么服务器人多
中南民族大学管理学院数据库
gis软件开发实验报告代写
线程异步调用导致数据库崩溃
安装网络技术协议
游戏app软件开发商
与谷歌通讯服务器无法连接
下列语句中可以创建数据库的是
佛山市思赢互联网科技
软件开发学习什么好呢
服务器无法打开无线网
北京巨量引擎网络技术有限公司
服务器和光纤有什么区别
成翰科技 工业互联网
伟源网络技术工作室
小程序一般用什么软件开发
5g驱动网络安全需求
服务器电气设计安全指南
sql数据库表格无法删除列
服务器导轨安装 浪潮
查询数据库前两条
西安英慧达网络技术有限公司
河南第三方软件开发哪家正规
软件开发快照是什么意思
笔记本网络安全启动
江苏省社保服务器虚拟主机
榆林5g软件开发工程师