JAVA总结--分布式锁
1、概念
分布式锁出现的原因:单体应用单机部署环境下,为了解决多线程并发问题,我们会使用ReentrantLcok或synchronized来解决互斥问题;但业务的需求,单机部署演变成分布式系统后,在分布式部署环境下,原单机部署使用的并发控制锁策略失效,为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题。
分布式锁特征:一个方法在同一时间只能被一个机器的一个线程执行;阻塞锁(没有获取到锁,进行等待);非阻塞锁(没有获取到锁,返回失败);锁失效;可重入;高性能、高可靠获取与释放锁;
2、三种实现方式:基于数据库、基于Redis、基于Zookeeper
1)基于数据库
数据库中创建一张表,表中包含方法名在内的多个字段;方法名字段创建唯一索引;
获取与释放:想要执行某个方法,就将方法名插入到数据表中,成功插入则获取锁,执行完成后删除对应的行数据释放锁。
优点:
- 借助数据库,实现方式比较简单;
缺点:
- 基于数据库实现,数据库的可用性和性能将直接影响分布式锁的可用性及性能,数据库需要双机部署数据同步、主备切换;
不具备可重入的特性,因为同一个线程在释放锁之前,行数据一直存在,无法再次成功插入数据,所以,需要在表中新增一列,用于记录当前获取到锁的机器和线程信息,在再次获取锁的时候,先查询表中机器和线程信息是否和当前机器和线程相同,若相同则直接获取锁;
没有锁失效机制,因为有可能出现成功插入数据后,服务器宕机了,对应的数据没有被删除,当服务恢复后一直获取不到锁,所以,需要在表中新增一列,用于记录失效时间,并且需要有定时任务清除这些失效的数据;
不具备阻塞锁特性,获取不到锁直接返回失败,所以需要优化获取逻辑,循环多次去获取。
2)基于Redis
在Redis2.6.12版本之前,使用setnx命令设置key-value、使用expire命令设置key的过期时间获取分布式锁,使用del命令释放分布式锁;
如果setnx成功返回1,说明获得锁,当程序执行完成后删除键到达释放锁的目的,如果setnx失败返回0,说明未获得锁,可通过循环等待继续获取;如果程序获得锁后,断开了与Redis的连接,锁未进行释放,则程序发生死锁,但因有超时时间;
多种情形问题:
- setnx命令设置完key-value后,还没来得及使用expire命令设置过期时间,当前线程挂掉了,会导致当前线程设置的key一直有效,后续线程无法正常通过setnx获取锁,造成死锁;解决方法是因为两个命令是分开执行并且不具备原子特性,如果能将这两个命令合二为一就可以解决问题了,在Redis2.6.12版本中实现了这个功能,Redis为set命令增加了一系列选项,可以通过SET resource_name my_random_value NX PX max-lock-time来获取分布式锁,这个命令仅在不存在key(resource_name)的时候才能被执行成功(NX选项),并且这个key有一个max-lock-time秒的自动失效时间(PX属性)。这个key的值是“my_random_value”,它是一个随机值,这个值在所有的机器中必须是唯一的,用于安全释放锁。
- 在分布式环境下,线程A通过这种实现方式获取到了锁,但是在获取到锁之后,执行被阻塞了,导致该锁失效,此时线程B获取到该锁,之后线程A恢复执行,执行完成后释放该锁,直接使用del命令,将会把线程B的锁也释放掉,而此时线程B还没执行完,将会导致不可预知的问题;解决方法是释放锁的时候,只有key存在并且存储的“my_random_value”值和指定的值一样才执行del命令;
- 为了实现高可用,将会选择主从复制机制,但是主从复制机制是异步的,会出现数据不同步的问题,可能导致多个机器的多个线程获取到同一个锁;解决方法是因为采用了主从复制导致的问题,解决方案是不采用主从复制,使用RedLock算法;
RedLock描述如下:在Redis的分布式环境中,假设有5个Redis master,这些节点完全互相独立,不存在主从复制或者其他集群协调机制。为了取到锁,客户端应该执行以下操作:
获取当前Unix时间,以毫秒为单位;
依次尝试从N个实例,使用相同的key和随机值获取锁。在步骤2,当向Redis设置锁时,客户端应该设置一个网络连接和响应超时时间,这个超时时间应该小于锁的失效时间。例如你的锁自动失效时间为10秒,则超时时间应该在5-50毫秒之间。这样可以避免服务器端Redis已经挂掉的情况下,客户端还在死死地等待响应结果。如果服务器端没有在规定时间内响应,客户端应该尽快尝试另外一个Redis实例;
客户端使用当前时间减去开始获取锁时间(步骤1记录的时间)就得到获取锁使用的时间。当且仅当从大多数(这里是3个节点)的Redis节点都取到锁,并且使用的时间小于锁失效时间时,锁才算获取成功。
如果取到了锁,key的真正有效时间等于有效时间减去获取锁所使用的时间(步骤3计算的结果);
如果因为某些原因,获取锁失败(没有在至少N/2+1个Redis实例取到锁或者取锁时间已经超过了有效时间),客户端应该在所有的Redis实例上进行解锁(即便某些Redis实例根本就没有加锁成功)。
优点:
- 高性能,借助Redis实现比较方便;
缺点:
- 线程获取锁后,如果处理时间过长会导致锁超时失效,所以,通过锁超时机制不是十分可靠;
3)基于Zookeeper
ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:
创建一个目录mylock;
线程A想获取锁就在mylock目录下创建临时顺序节点;
获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;
线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。
apache的开源库Curator,它是一个ZooKeeper客户端,Curator提供的InterProcessMutex是分布式锁的实现,acquire方法用于获取锁,release方法用于释放锁。
优点:
- 具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。
缺点:
- 因为需要频繁的创建和删除节点,性能上不如Redis方式。
JAVA总结--分布式锁的更多相关文章
- [Java复习] 分布式锁 Zookeeper Redis
一般实现分布式锁都有哪些方式? 使用 Redis 如何设计分布式锁?使用 Zookeeper 来设计分布式锁可以吗? 这两种分布式锁的实现方式哪种效率比较高? 1. Zookeeper 都有哪些使用场 ...
- Java实现分布式锁方式
1.数据库乐观锁 2.redis 3.zookeeper
- 分布式锁2 Java非常用技术方案探讨之ZooKeeper
前言: 由于在平时的工作中,线上服务器是分布式多台部署的,经常会面临解决分布式场景下数据一致性的问题,那么就要利用分布式锁来解决这些问题.以自己结合实际工作中的一些经验和网上看到的一些资料 ...
- Java使用Redis实现分布式锁来防止重复提交问题
如何用消息系统避免分布式事务? - 少年阿宾 - BlogJavahttp://www.blogjava.net/stevenjohn/archive/2018/01/04/433004.html [ ...
- 分布式锁2 Java非常用技术方案探讨之ZooKeeper 【转载】
前言: 由于在平时的工作中,线上服务器是分布式多台部署的,经常会面临解决分布式场景下数据一致性的问题,那么就要利用分布式锁来解决这些问题.以自己结合实际工作中的一些经验和网上看到的一些资料 ...
- spring boot redis分布式锁
随着现在分布式架构越来越盛行,在很多场景下需要使用到分布式锁.分布式锁的实现有很多种,比如基于数据库. zookeeper 等,本文主要介绍使用 Redis 做分布式锁的方式,并封装成spring b ...
- 分布式锁与实现(一)——基于Redis实现 【比较靠谱】
转: 分布式锁与实现(一)——基于Redis实现 概述 目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们“任何一个分布式系统 ...
- 利用ZooKeeper简单实现分布式锁
1.分布式锁的由来: 在程序开发过程中不得不考虑的就是并发问题.在java中对于同一个jvm而言,jdk已经提供了lock和同步等.但是在分布式情况下,往往存在多个进程对一些资源产生竞争关系,而这些进 ...
- spring boot redis分布式锁 (转)
一. Redis 分布式锁的实现以及存在的问题 锁是针对某个资源,保证其访问的互斥性,在实际使用当中,这个资源一般是一个字符串.使用 Redis 实现锁,主要是将资源放到 Redis 当中,利用其原子 ...
随机推荐
- ubuntu不能登陆
开机按shift,找到之前的内核版本或者recovery 安装vmtools 报错Not enough free space to extract VMwareTools 解决办法:将此文件夹复制到另 ...
- SpringBoot之初体验
一.SpringBoot 介绍 1.1 SpringBoot 使命 在之前我们学习 Spring 时,我们了解到 Spring 最根本的使命就是 简化Java开发.而 SpringBoot 的出现也有 ...
- Django【第19篇】:Django之extra
extra过滤 extra extra(select=None, where=None, params=None, tables=None, order_by=None, select_params= ...
- js去重的es6做法和es5做法
1.es5做法var array=[1,3,4,5,2,3,4,5,5,5];var ob={};var result=[];array.forEach(function (a) { var key= ...
- Eclipse搭建Maven项目并上传SVN备份
本文出自:http://www.cnblogs.com/2186009311CFF/p/7226127.html 背景:近段时间在学着Java,想着用Java做BS的项目.但是项目一遇到问题又要重做, ...
- react native 之 AsyncStorage
新版本中不时从react-native导入了,而是 react-native-async-storage 使用static setItem(key: string, value: string, [c ...
- 【CF1237D】Balanced Playlist(set,二分,线段树)
题意:给定一个n首歌的播放列表,第i首的值为a[i],听完第i首会回到第1首 现在从每首开始往下,记录听过的最大值,如果当前听的值严格小于听过最大值的一半则停止 问从每首歌开始往下听能听几首,不会停止 ...
- C#判断网络链接状态
using System.Net.NetworkInformation; bool isLocalAreaConnected = NetworkInterface.GetIsNetworkAvaila ...
- qcom Android Camera【转】
本文转载自:http://blog.csdn.net/Wilsonboliu/article/details/54949196 1.总体架构 Android Camera 框架从整体上看是一个 cli ...
- mysql_存储引擎层-innodb buffer pool
buffer pool 是innodb存储引擎带的一个缓存池,查询数据时,首先从内存中查询 数据如果内存中存在的话直接返回. innodb buffer pool 和 qcache 的区别:Qcach ...