【Redis】分布式锁

经过上节课的演示,可以发现在集群模式下synchronized锁失效了,synchronized只能保证单个JVM内部的多个线程之间的互斥,而没有办法让我们集群下的多个JVM进程之间互斥,要想解决这个问题,我们必须使用分布式锁。

一、基本原理

分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。

分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路

1653374296906

那么分布式锁他应该满足一些什么样的条件呢?

可见性:多个线程都能看到相同的结果,注意:这个地方说的可见性并不是并发编程中指的内存可见性,只是说多个进程之间都能感知到变化的意思。例如redis。其实只要是JVM外部的基本上都可以做到多线程可见,用MySQL也可以,因此这个很容易做到。比较难的就是互斥。

互斥:即不管谁来访问你只有1个人能拿到,其他人都是失败。互斥是分布式锁的最基本的条件,使得程序串行执行

高可用:获取锁的时候都能是成功的,程序不易崩溃,时时刻刻都保证较高的可用性

高性能:由于加锁(串行执行)本身就让性能降低,所有对于分布式锁本身需要他就较高的加锁性能和释放锁性能

安全性:安全也是程序中必不可少的一环,即获取锁的时候需要考虑到一些异常的情况,例如获取锁的时候还没有释放,程序就挂了怎么办?会不会产生死锁?等问题

以上这五个是分布式锁要满足的基本的特性,除了这些以外,还有一些功能性的特征,你可以满足也可以不满足,例如是不是能满足可重入性,你获取锁的时候是阻塞的还是非阻塞的,你是公平锁还是非公平锁?等等,这些都是功能上的一些特性,这里我们不作为重点去讨论。

1653381992018

接下来我们讨论在不同的分布式锁实现方案中它是如何实现互斥的、有什么差异、它是如何保证高可用性的、高性能、以及它的安全性如何,这是我们重点要去对比的几个点。


二、实现方式对比

常见的分布式锁有三种

Mysql:MySQL数据库或者其他的数据库,都具备事务机制,那么当事务执行的时候,或者说我们在执行写操作的时候,MySQL就会自动给你分配一个互斥的锁,这样一来在多个事务之间就是互斥的,只有一个人能去执行,因此我们完全就能接于这个原理来实现一把锁。

我们可以在需要实现分布式锁的业务的地方,在业务执行前,先去MySQL中申请一个互斥锁,然后去执行我们的业务。当业务执行完后,我们去提交事务,这样一来锁就释放了。当我们的业务抛了异常的时候,它会自动触发回滚、锁也就释放了。

这样一来互斥效果、锁释放就都很容易实现了。

mysql实现的方式就是利用MySQL本身的互斥锁做。它的可用性依赖于MySQL本身的可用性,由于MySQL是支持主从模式的,因此可用性应该说还是不错的。但是由于mysql性能本身一般,所以采用分布式锁的情况下,其实使用mysql作为分布式锁比较少见。

安全性:一旦出现异常,这个锁能不能及时释放呢?其实是可以的,因为在MySQL中是利用事务机制获取的锁,但是一旦你系统崩溃了,其实连接断开后锁是会自动释放的,数据也会回滚


Redis:redis作为分布式锁是非常常见的一种使用方式,现在企业级开发中基本都使用redis或者zookeeper作为分布式锁,利用setnx这个命令,如果插入key成功,则表示获得到了锁,如果有人插入成功,其他人插入失败则表示无法获得到锁,利用这套逻辑来实现分布式锁。

可用性:redis不仅仅支持主从,它还支持集群模式,所以它的可用性是可以得到保障的。

高性能:性能更不用讲了,redis性能是远远好于MySQL的,因此它执行分布式锁的性能也是非常好的。

安全性:一旦出现异常,这个锁能不能及时释放呢?这个就必须了,因为我们利用setnx是设置了一个key到redis中,如果服务宕机了,将来没有人去执行删除的动作,key一直在那里,锁得不到释放,这样一来其他人也拿不到锁,就产生类似于死锁的效果的,就出现问题了。因此为了解决这个问题,我们再利用setnx获取锁的时候,必须想一个办法,将来一旦出现故障,锁也能释放。这个办法就是利用redis的key的过期机制。

不过这个锁的释放时间设置多长呢?,如果太长了,那这个锁无效等待时间就会比较多;但如果设置太短了,万一我的业务还没执行完呢?所以说这种方式做安全性的一个保证是可以做的,但是还需要去完善,具体怎么解决后面会讲。


Zookeeper:zookeeper也是企业级开发中较好的一个实现分布式锁的方案,它锁的原理其实是利用了内部的节点机制。

Zookeeper内部它可以去创建这种数据节点的,而节点具备这种唯一性和有序性,另外还可以创建这种临时节点。

所谓的唯一性就是:我们去创建节点的时候节点不能重复。

有序性:每创建一个节点ip就是递增的。

这里我们可以利用有序性来实现互斥,比如说我们现在有很多线程都从Zookeeper中创建节点,这样一来每一个节点它的id是不是就是单调递增的。如果我们约定id最小的那个它就算是获取成功,这样一来就实现互斥了,因为最小的只有一个。

当然你也可以利用唯一性,比如大家都去创建节点,并且节点名称大家都是一样的,这样一来是不是只有一个人成功,所以也可以。

但一般情况下我们都是利用可续行去实现这种互斥。

释放锁:你讲节点删除你就不是最小的了,此时另外一个人就变成最小的了。

可用性:Zookeeper本身也是支持集群的,因此它的可用性很好。

而性能上的话,Zookeeper集群强调的是强的一致性,而这种强的一致性就会导致它的主从之间做数据同步就会消耗一定的时间,所以它的性能相对来讲要比redis差一些。

而安全型它做的很好,因为它创建的节点往往是临时节点,这时候一旦出现故障,例如宕机了,断开后它的节点就会自动释放,所以锁也就释放了,所以这块其实也不用考虑。

因此从安全性考虑,Zookeeper和MySQL它们两个原理基本上类似,所以安全性是比较好的,比redis要好一些,redis只能利用超时机制来做。

但是从可用性和性能上来讲,redis是非常好的。因此接下来我们就会带着大家学一下,如何基于redis来去实现分布式锁。

1653382219377

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值