企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 面试官:基于Redis怎么实现分布式锁? ## 一、错误方案:只使用setnx(key, value, expires) 1. 不同的服务器启动时间是不一致的; 2. key的过期时间设置太短,会有可能导致任务重复执行; 3. key的过期时间设置太长,则有可能错过下次任务执行周期; 因此,在这种场景下,保持服务器时间基本同步的情况下,设置小于周期,且足够大于服务器误差时间的周期,以保证该任务只能执行一次。 ![](https://img.kancloud.cn/cd/c1/cdc1e202b91c756cf5ddcf0176f84e28_970x248.png) 但是`缺点`也非常明显: 1. 服务器时间最早的机器会一直获得任务的执行权; 2. 基于服务器时间基本能够同步的条件; 3. key过期时长设置无法形成较好的标准。 > 无论是大周期任务,还是高并发场景下,都不应这样直接使用。 ## 二、基于客户端标识的本地时间差比较 Redis中存储的除了锁key,还有本地随机字符串+过期时间差。其源码如下: ``` //保存客户端标识 private static final ThreadLocal<String> LOCAL = new ThreadLocal<String>(); /** * * @param jedis * @param lockKey 锁key * @param expires 过期时间 一般为 System.currentTimeMillis()+ 过期时间 * @return */ public static boolean getDistributedLock(Jedis jedis, String lockKey, long expires) { //客户端标识 在释放锁时 确保由设置锁的客户端来释放自己的锁 String uuid = UUID.randomUUID().toString(); LOCAL.set(uuid); String expiresStr = uuid+"#"+expires; // 如果当前锁不存在,返回加锁成功 if (jedis.setnx(lockKey, expiresStr) == 1) { return true; } // 如果锁存在,获取锁的过期时间 String currentValue = jedis.get(lockKey); String currentValueStr = null==currentValue?null:currentValue.split("#")[1]; // 判断当前锁是否过期 if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) { // 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间 此处多个客户端会覆盖锁的过期时间 String oldValue = jedis.getSet(lockKey,expiresStr); String oldValueStr = null ==oldValue?null:oldValue.split("#")[1]; // 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才有权利加锁 if (oldValueStr != null && oldValueStr.equals(currentValueStr)) { //由于上面会覆盖锁的过期时间 此处让获取锁的客户端 重新设置为自己的过期时间 jedis.set(lockKey,expiresStr); return true; } } // 其他情况,一律返回加锁失败 return false; } /** * * @param jedis * @param lockKey 锁key * @param value 过期时间 一般为 System.currentTimeMillis()+ 过期时间 * @return */ public static boolean releaseDistributedLock(Jedis jedis, String lockKey, long value) { String uuid = LOCAL.get(); String valueStr = uuid+"#"+value; //根据uuid 这个标识 让客户端 去释放自己的锁 不能释放别人的锁 if(valueStr.equals(jedis.get(lockKey))){ jedis.del(lockKey); return true; } return false; } ``` ## 三、参考资料 1. [基于Redis的分布式锁 基于setnx的正确实现方式](https://blog.csdn.net/wudidewu/article/details/79817125)