1. 为什么需要分布式锁??
我们都知道,Java提供的synchronized / lock锁只能作用在单个JVM中的单个应用中,Java中的锁只能锁定JVM级别的锁:
- synchronized就是利用JVM内部的锁监视器来控制线程的,在JVM的内部,因为只有一个锁监视器,所以只能有一个线程获取到锁,实现线程间的互斥;但是当有多个JVM的时候,就会有多个锁监视器,此时就会有多个线程获取到锁,这个时候就没有办法实现多JVM进程之间的互斥了。
而如果在以下场景下,比如:
- 单个应用进行集群部署,负载均衡可能把请求分配到不同的机器上
- 多个不同的分布式应用,多个应用需要同时锁定同一个资源
以上情况,单机锁就会失效,此时就需要一种全局应用锁代替单机锁,即:分布式锁。?
2. 单机锁 & 分布式锁的定义:?
- 单机锁:在单个JVM进程内起作用的同步机制,用于控制对共享资源的访问,这种锁,主要适用于单体应用,用于控制在同一JVM进程中多个线程对共享资源的互斥访问。
- 分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁,分布式锁用于协调分布式系统中的不同节点,以确保在全局范围内对共享资源的互斥访问。
锁的本质:让程序并行执行变成串行执行~!
3. 分布式锁应该具备哪些条件?
- 多线程可见
- 互斥
- 可重入:同一个线程可以反复获取锁,避免死锁
- 具备非阻塞锁特性:获取锁失败立即返回
- 获取锁和释放锁要具备高可用、高性能?
4. 分布式锁都有哪些主流的实现方案?
分布式锁的核心是实现多线程之间互斥,而满足这一点的方式有很多,常见的有三种:?
- Redis:简单,速度快,性能最好,现在企业级开发基本都使用Redis或者Zookeeper作为分布式锁,简单的SET NX EX这样的互斥命令就可以实现,如果插入key成功,则表示获取到了锁,如果插入失败则表示获取锁失败,而且还可以自动过期,利用锁超时时间自动释放,防止忘记释放锁
- MySQL:MySQL本身就带有锁机制(select...from...where..for update => 这是一种悲观锁,会锁住对应的索引行),利用MySQL本身的互斥锁机制来实现分布式锁,不用引入新的中间件,而是作为传统关系型数据库,存储的锁信息更详细,断开连接时自动释放锁,但是由于MySQL性能本身一般(锁的性能受限于MySQL的性能),所以使用MySQL作为分布式锁比较少见
- Zookeeper:Zookeeper也是企业级开发中较好的一个实现分布式锁的方案,利用临时顺序节点实现互斥锁,利用节点的唯一性和有序性实现互斥,断开连接时自动释放锁,更稳定更可靠,它不依靠超时时间释放锁,可靠性比Redis更高,同时也更复杂
5. 如何用 Redis 实现分布式锁的??
Redis本身可以被多个客户端访问,正好就是一个共享存储系统,可以用来保存分布式锁,而且Redis的读写性能高,可以应对高并发的锁操作场景。
Redis的SET命令有个NX参数可以实现「key不存在才插入」,所以可以用它来实现分布式锁:?
- 如果key不存在,则显示插入成功,可以用来表示加锁成功;
- 如果key存在,则会显示插入失败,可以用来表示加锁失败。?
del或UNLINK删除key代表释放锁?
另外,我们可以在SET命令执行时加上 EX / PX 选项,设置过期时间,以免客户端拿到锁后发生异常,导致锁一直无法释放。?
实现分布式锁时需要实现的两个基本方法:
-
获取锁:
-
释放锁:
-
手动释放:DEL手动删除
-
超时释放:获取锁时添加一个超时时间
?
?