深入解析Redis中的分布式锁

本篇文章给大家主要带大家了解一下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 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 实现了锁的重试操作。但是缺点还是有的,不支持锁的自动续期,锁的重入,以及公平性(目前通过自旋的方式实现,相当于是非公平的方式)。

更多编程相关知识,请访问:编程入门!!

以上就是深入解析Redis中的分布式锁的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2039975.html

(0)
上一篇 2025年2月24日 00:07:43
下一篇 2025年2月23日 19:23:34

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 一起聊聊Redis缓存的淘汰策略

    redis 缓存有哪些淘汰策略?本篇文章就来和大家一起聊聊redis缓存的淘汰策略,介绍缓存策略设置建议,希望对大家有所帮助! Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语…

    2025年2月24日
    200
  • 深入浅析Redis中的sentinel故障转移

    本篇文章带大家了解一下redis中的故障转移(sentinel),希望对大家有所帮助! 当两台以上的Redis实例形成了主备关系,它们组成的集群就具备了一定的高可用性:当master发生故障的时候,slave可以成为新的master对外提供…

    2025年2月24日 数据库
    200
  • 聊一聊分布式系统下基于Redis的分布式锁

    加锁了,还有并发问题?redis分布式锁你真的了解?下面本篇文章就来给大家聊一聊分布式系统下基于redis的分布式锁,希望对大家有所帮助! 新接手的项目,偶尔会出现账不平的问题。之前的技术老大临走时给的解释是:排查了,没找到原因,之后太忙就…

    2025年2月24日
    200
  • redis为什么用单线程?为什么那么快?

    什么是redis的单线程?redis为什么用单线程?为什么单线程redis能那么快?下面本篇文章给大家分析分析,希望对大家有所帮助! 1.基本概念 什么是redis的单线程(核心功能在单线程上,并不是所有功能) redis的网络IO和键值对…

    2025年2月24日 数据库
    200
  • 聊聊Redis中怎么实现支持几乎所有加锁场景的分布式锁

    本篇文章给大家介绍一下redis分布式锁的实现方法,希望对大家有所帮助! Hello 大家好,今天给大家分享redisson实现的多类型锁、支持几乎所有加锁场景的redis分布式锁的实现,还支持小型MQ和redis的各种数据操作。【相关推荐…

    2025年2月24日 数据库
    200
  • 一文聊聊Redis中的epoll和文件事件

    本篇文章给大家介绍一下redis中的文件事件,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 事件驱动 Redis 服务器是事件驱动程序,分为文件事件和时间事件 文件事件:socket 的可读可写事件定时任务 【相关推荐:R…

    2025年2月24日
    200
  • 总结分享几款实用Redis可视化工具

    本篇文章总结了几款实用redis可视化工具分享给大家,希望对大家有所帮助,快来收藏吧! 不啰嗦,我们直接开始! 1、命令行 1.1、iredis 利用iredis,用|将redis通过pipe用shell的其他工具,比如jq/fx/rg/s…

    2025年2月24日 数据库
    200
  • 浅谈Redis中的字典、哈希算法和ReHash原理

    本篇文章带大家了解一下redis中的字典、哈希算法和rehash原理,希望对大家有所帮助! Redis 中的字典被广泛用于实现Redis的各种功能,其中包括数据库和哈希键。 字典的底层实现为哈希表,每个字典带有两个哈希表,一个平时使用,另一…

    2025年2月24日
    200
  • 浅析Redis缓存中的8种淘汰策略

    本篇文章带大家来聊聊redis缓存中的8种淘汰策略,看看应该怎么使用它们,希望对大家有所帮助! 我们知道Redis缓存使用内存来保存数据,但内存大小毕竟有限,随着要缓存的数据量越来越大,有限的缓存空间不可避免地会被写满。这时候就需要缓存的淘…

    2025年2月24日
    200
  • Redis怎么进行去重?4种去重方法浅析

    redis怎么进行去重?下面本篇文章给大家介绍一下redis去重的4种方法,希望对大家有所帮助! 这篇文章主要介绍了Redis实现唯一计数的3种方法分享,本文讲解了基于SET、基于 bit、基于 HyperLogLog三种方法,需要的朋友可…

    2025年2月24日
    200

发表回复

登录后才能评论