分布式锁详细简介说明
下文是笔者讲述分布式锁的相关简介说明,如下所示:
用于解决多线程(或多进程)运行时,访问共享资源 错误或数据不一致问题的工具
当A服务在获取锁之后,突然A服务关闭,此时B服务就无法获取锁
此时就需为锁设置一个合理的超时时间,保证服务的可用性
当我们采用主从模式部署,我们想象一个这样的场景:服务 A 申请到一把锁之后,此时作为主机的 Redis 宕机了
那么服务B在申请锁的时候就会从从机那里获取到这把锁,为了解决这个问题,Redis 作者提出了一种 RedLock 红锁的算法:
此命令只允许被一个客户端占有
分布式锁简介
锁的功能:用于解决多线程(或多进程)运行时,访问共享资源 错误或数据不一致问题的工具
为何需要分布式锁
分布式锁使用的两个场景:- 避免不同节点重复相同的工作:比如用户执行了某个操作有可能不同节点会发送多封邮件
- 避免破坏数据的正确性:如果两个节点在同一条数据上同时进行操作,可能会造成数据错误或不一致的情况出现
Java中分布式锁的常见实现方式
分布式锁的实现条件: 某个资源在同一时间只允许一个用户操作下文将列举java中分布式锁常见的实现方式,如下所示:
- 基于 MySQL 中的锁:MySQL 本身有自带的悲观锁 for update 关键字,也可以自己实现悲观/乐观锁来达到目的
- 基于 Zookeeper 有序节点:Zookeeper 允许临时创建有序的子节点,这样客户端获取节点列表时,就能够当前子节点列表中的序号判断是否能够获得锁
- 基于 Redis 的单线程:由于 Redis 是单线程,所以命令会以串行的方式执行,并且本身提供了像 SETNX(set if not exists) 这样的指令,本身具有互斥性
Redis分布式锁的问题
锁超时
当有两台平行的服务A,B当A服务在获取锁之后,突然A服务关闭,此时B服务就无法获取锁
此时就需为锁设置一个合理的超时时间,保证服务的可用性
单点/多点问题
当Redis 采用单机部署模式,则意味着当Redis出现故障,就会导致整个服务不可用当我们采用主从模式部署,我们想象一个这样的场景:服务 A 申请到一把锁之后,此时作为主机的 Redis 宕机了
那么服务B在申请锁的时候就会从从机那里获取到这把锁,为了解决这个问题,Redis 作者提出了一种 RedLock 红锁的算法:
Redis分布式锁的实现
Redis分布式锁使用SETNX(SET if Not eXists) 指令就是这样的一个操作此命令只允许被一个客户端占有
Java Redis分布式的代码实现
下文将使用Jedis来模拟实现,如下所示:private static final String LOCK_SUCCESS = "OK"; private static final Long RELEASE_SUCCESS = 1L; private static final String SET_IF_NOT_EXIST = "NX"; private static final String SET_WITH_EXPIRE_TIME = "PX"; @Override public String acquire() { try { // 获取锁的超时时间,超过这个时间则放弃获取锁 long end = System.currentTimeMillis() + acquireTimeout; // 随机生成一个 value String requireToken = UUID.randomUUID().toString(); while (System.currentTimeMillis() < end) { String result = jedis .set(lockKey, requireToken, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); if (LOCK_SUCCESS.equals(result)) { return requireToken; } try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } catch (Exception e) { log.error("acquire lock due to error", e); } return null; } @Override public boolean release(String identify) { if (identify == null) { return false; } String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; Object result = new Object(); try { result = jedis.eval(script, Collections.singletonlist(lockKey), Collections.singletonList(identify)); if (RELEASE_SUCCESS.equals(result)) { log.info("release lock success, requestToken:{}", identify); return true; } } catch (Exception e) { log.error("release lock due to error", e); } finally { if (jedis != null) { jedis.close(); } } log.info("release lock failed, requestToken:{}, result:{}", identify, result); return false; }
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。