Redis高并发分布式锁详解( 五 )

7)Redisson类#tryLock方法
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {long newLeaseTime = -1;if (leaseTime != -1) {newLeaseTime = unit.toMillis(waitTime)*2;}long time = System.currentTimeMillis();long remainTime = -1;if (waitTime != -1) {remainTime = unit.toMillis(waitTime);}long lockWaitTime = calcLockWaitTime(remainTime);int failedLocksLimit = failedLocksLimit();List<RLock> acquiredLocks = new ArrayList<RLock>(locks.size());for (ListIterator<RLock> iterator = locks.listIterator(); iterator.hasNext();) {RLock lock = iterator.next();boolean lockAcquired;try {if (waitTime == -1 && leaseTime == -1) {lockAcquired = lock.tryLock();} else {long awaitTime = Math.min(lockWaitTime, remainTime);lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS);}} catch (Exception e) {lockAcquired = false;}if (lockAcquired) {acquiredLocks.add(lock);} else {if (locks.size() - acquiredLocks.size() == failedLocksLimit()) {break;}if (failedLocksLimit == 0) {unlockInner(acquiredLocks);if (waitTime == -1 && leaseTime == -1) {return false;}failedLocksLimit = failedLocksLimit();acquiredLocks.clear();// reset iteratorwhile (iterator.hasPrevious()) {iterator.previous();}} else {failedLocksLimit--;}}if (remainTime != -1) {remainTime -= (System.currentTimeMillis() - time);time = System.currentTimeMillis();if (remainTime <= 0) {unlockInner(acquiredLocks);return false;}}}if (leaseTime != -1) {List<RFuture<Boolean>> futures = new ArrayList<RFuture<Boolean>>(acquiredLocks.size());for (RLock rLock : acquiredLocks) {RFuture<Boolean> future = rLock.expireAsync(unit.toMillis(leaseTime), TimeUnit.MILLISECONDS);futures.add(future);}for (RFuture<Boolean> rFuture : futures) {rFuture.syncUninterruptibly();}}return true;}Redis与Zookeeper分布式锁的区别1.从单机角度上来说,两者差别不大 , 都是项目引入的外部组件,redis相对于zookeeper来说,项目中使用的更多,常用性角度redis更加 。
2.但是一般我们都会做集群(容错率更高):
【1】从分布式的CAP角度分析:
redis满足AP , 在Master节点上写成功了会优先返回给客户端,之后在同步给从节点
zookeeper满足CP,在Master节点上写成功了会优先同步给从节点【ZAB协议(半数以上写成功)】,之后在返回给客户端
【2】主从架构锁失效问题:
redis会出现,因为从节点变成主节点时,会出现丢失数据的问题 。
zookeeper不会出现,因为从节点变成主节点时 , 不会会出现丢失数据的问题 。
【3】集群下性能角度:
redis性能会高于zookeeper , 同步是个耗时的操作(而且这个过程中还是相当于阻塞线程),并发越高的情况,我们想要的是耗时越少的越好 。
3.选redis还是zk实现分布式锁:
首先zk的性能肯定不如redis,但是从分布式锁的角度语义上来说,zk可能更适合一些,所以如果对性能要求比较高的话就选redis,对数据的强一致性有特别严格要求的话就选zk , 现在的主流的分布式锁方案还是redis,也有一些办法去减少redis主从架构锁失效问题 。
如何提升分布式锁性能问题分析1.分布式锁为我们解决了并发问题,但是其底层思路是将并行执行的请求给串行化了,因为redis是单线程执行任务的,肯定就不会有并发问题了 。
2.但是这种设计本身是与我们高并发的需求是冲突的 。但是某些场景下我们又不得不用,所以我们应该基于场景做一些优化 。
3.正如阿里巴巴Java开发手册里面写到:
6. 【强制】高并发时 , 同步调用应该去考量锁的性能损耗 。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁 , 就不要用类锁 。说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法 。7. 【强制】对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁 。说明:线程一需要对表 A、B、C 依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是 A、B、C,否则可能出现死锁 。8. 【强制】并发修改同一记录时,避免更新丢失,需要加锁 。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁 , 使用 version 作为更新依据 。说明:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁 。乐观锁的重试次数不得小于 3 次 。4.所以我们优先从锁的粒度开始,锁是否合适,加锁的范围是否够小 。锁的粒度范围越小越好 , 加锁的代码越少性能就越高,因为加锁的代码会串行执行,没有必要加锁的代码肯定是让他们并行执行这样效率更高 。

推荐阅读