千家信息网

怎么理解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

接口定义

接口定义,对于锁我们核心其实就连个方法 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 < 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软件开发工程师
0