热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析Redis中的分布式锁

数据库|RedisRedis,分布式锁数据库-Redis本篇文章给大家主要带大家了解一下Redis中分布式锁的实现和代码解析,希望对大家有所帮助!优惠券大全源码,vscodec入门

数据库|Redis深入解析Redis中的分布式锁
Redis,分布式锁
数据库-Redis
本篇文章给大家主要带大家了解一下Redis中分布式锁的实现和代码解析,希望对大家有所帮助!
优惠券大全源码,vscode c入门到精通,ubuntu.vmdk,弱口令tomcat下载,爬虫视频导出,php 上传文件封装类,随州茶叶seo推广开户,易语言编程代码网站,thinkphp5 模板不存在lzw
国外直播源码出租,vscode 架构图插件,新机械ubuntu,tomcat请求参数太长,花都爬虫店,php 下载二进制文件,宁夏seo哪家公司好,flashfxp发布网站,网站婚庆模板lzw
Redis 分布式锁
网站整站源码下载,vscode的命令面板怎么打开,ubuntu更新列表,tomcat关闭 释放资源,数据授权爬虫,php 目录操作函数,珠海百度seo公司定制,如何单页网站在线订单如何使用转账支付宝lzw
大家项目中都会使用到分布式锁把,通常用来做数据的有序操作场景,比如一笔订单退款(如果可以退多次的情况)。或者用户多端下单。

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.bootspring-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

接口定义

接口定义,对于锁我们核心其实就连个方法 lockunlock.

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 RedisTemplate redisTemplate; 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"); List futureList = new ArrayList(); for (int i = 0; i { 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 实现了锁的重试操作。
但是缺点还是有的,不支持锁的自动续期,锁的重入,以及公平性(目前通过自旋的方式实现,相当于是非公平的方式)。

编程入门!!


推荐阅读
author-avatar
mobiledu2502854827
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有