分布式锁之redis实现分布式锁 理论 - Go语言中文社区

分布式锁之redis实现分布式锁 理论


首先,分布式理论:任何一个分布式系统都无法同时满足Consistency、Availability、Partition tolerance(一致性、可用性、分区容错性)。最多只能同时满足两项。

在实际场景中,我们为了保证数据的最终一致性,需要很多技术方案来支持。例如分布式事务、分布式锁等。

分布式环境与单机环境的区别:

  1. 分布式与单机情况的最大的不同就是不是多线程而是多进程
  2. 多线程由于可以共享堆内存,因此可以简单的采取内存作为标记存储位置。而进程之间甚至可能都不在同一台物理机上,因此需要将标记存储在一个所有进程都能看到的地方。

分布式锁:

当在分布式模型下,数据只有一份(或有限制),此时需要利用锁的技术控制某一时刻修改数据的进程数。

与单机模式下的锁不仅需要保证进程可见,还需要考虑进程与锁之间的网络问题。(我觉得分布式情况下之所以问题变得复杂,主要就是需要考虑到网络的延时和不可靠。。。一个大坑)

分布式锁还是可以将标记存在内存,只是该内存不是某个进程分配的内存而是公共内存如 Redis、Memcache。
至于利用数据库、文件等做锁与单机的实现是一样的,只要保证标记能互斥就行。

分布式锁的实现

在我的另一篇文章中,我们知道分布式锁的实现需要满足四个必要条件(互斥性、容错性、不能死锁、解铃还须系铃人)。还有一些非必要条件:
最好锁是一个阻塞锁;最好是一个公平锁。
另外,非死锁的条件说明该锁也要是一个可重入锁。

几种实现方式

基于数据库的分布式锁:优点:操作简单、容易理解。缺点:存在单点问题、数据库性能够开销较大、不可重入;
基于缓存的分布式锁:优点:非阻塞、性能好。缺点:操作不好容易造成锁无法释放的情况。
Zookeeper 分布式锁:通过有序临时节点实现锁机制,自己对应的节点需要最小,则被认为是获得了锁。优点:集群可以透明解决单点问题,避免锁不被释放问题,同时锁可以重入。缺点:性能不如缓存方式,吞吐量会随着zk集群规模变大而下降。

数据库做分布式锁

A 基于乐观锁实现

在这里插入图片描述
在这里插入图片描述

MVCC实现

在单个数据库加锁有一定的限制,所以mysql引入了MVCC,这样的机制有一个问题,就是对数据表侵入较大,我们要为每个表设计一个版本号字段,然后写一条判断 sql 每次进行判断,增加了数据库操作的次数,在高并发的要求下,对数据库连接的开销也是无法忍受的。

B 基于悲观锁
基于数据库排它锁做分布式锁

在mysql中,只有通过索引进行检索的时候才会用行级锁,否则会使用表级锁。

在查询语句后面加上 for update,就是在查询过程中给数据库表增加排它锁,其他线程无法在该行记录上增加排它锁。
这里我们希望使用行级锁,就要给要执行的方法字段名添加索引,值得注意的是,这个索引一定要创建成唯一索引,否则会出现多个重载方法之间无法同时被访问的问题。

我们可以认为获得排他锁的线程即可获得分布式锁,当获取到锁之后,可以执行方法的业务逻辑,执行完方法之后,通过**connection.commit()**操作来释放锁。
在这里插入图片描述
但是无法解决数据库单点和可重入的问题。

总之,使用数据库的优点是简单,易于理解,但是会有各种各样的问题(单点问题、性能开销较大,不可重入等)。

基于Redis实现分布式锁

setnx() set if not eixt();是一个原子性的方法,key存在则当前key失败,返回为0;key不存在则设置当前key成功,返回1.
expire() 设置过期时间
在这里插入图片描述

第二种是通过REDIS 的 SETNX()、GET()、GETSET()方法做分布式锁,在上述用setnx和expire方法的方案上针对可能存在的死锁问题做了优化。
在这里插入图片描述
在这里插入图片描述

基于REDLOCK做分布式锁

RedLock是Redis的作者给出的集群模式的Redis的分布式锁,基于N个完全独立的Redis节点。(通常N可以设置为5)
在这里插入图片描述
优点:性能高;
缺点,失效时间的设置需要考虑好。若太短,方法还没执行完,锁就自动释放了,就会产生并发问题。设置的太长,其他等待的线程就会多等一段时间。

基于REDISSON做分布式锁

redisson是redis官方的分布式锁组件。

失效时间的设置在redisson中的做法:每获得一个锁,只设置一个很短的超时时间,同时起一个线程在每次快要到超时时间时去刷新锁的超时时间。在释放锁的同时结束这个线程。

0基于ZooKeeper做分布式锁

zk一般由多个节点组成,采用 zab 一致性协议。因此可以将 zk 看成一个单点结构,对其修改数据其内部自动将所有节点数据进行修改而后才提供查询服务。

zk 的数据以目录树的形式,每个目录称为 znode, znode 中可存储数据(一般不超过 1M),还可以在其中增加子节点。

子节点有三种类型。序列化节点,每在该节点下增加一个节点自动给该节点的名称上自增。临时节点,一旦创建这个 znode 的客户端与服务器失去联系,这个 znode 也将自动删除。最后就是普通节点

Watch 机制,client 可以监控每个节点的变化,当产生变化会给 client 产生一个事件。

ZK基本锁
上锁改为创建临时有序节点,每一个上锁的节点均能创建节点成功,只是其序列号不同,序列号最小的可以拥有锁。
如果这个节点序号不是最小的则 watch 序号比本身小的前一个节点 (公平锁)。

步骤:
1.在 /lock 节点下创建一个有序临时节点 (EPHEMERAL_SEQUENTIAL)。
2.判断创建的节点序号是否最小,如果是最小则获取锁成功。不是则取锁失败,然后 watch 序号比本身小的前一个节点。
3.当取锁失败,设置 watch 后则等待 watch 事件到来后,再次判断是否序号最小。
4.取锁成功则执行代码,最后释放锁(删除该节点)。

优点:
有效的解决单点问题,不可重入问题,非阻塞问题以及锁无法释放的问题。实现起来较为简单。
缺点:
性能上可能并没有缓存服务那么高,因为每次在创建锁和释放锁的过程中,都要动态创建、销毁临时节点来实现锁功能。ZK 中创建和删除节点只能通过 Leader 服务器来执行,然后将数据同步到所有的 Follower 机器上。还需要对 ZK的原理有所了解。
在这里插入图片描述

转自 https://www.cnblogs.com/seesun2012/p/9214653.html

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/mulinsen77/article/details/88963485
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-09-04 14:31:39
  • 阅读 ( 1343 )
  • 分类:Redis

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢