zoukankan      html  css  js  c++  java
  • 分布式锁实现解决方案

    在多台服务器需要对某一个共享资源进行多线程同步访问的时候,就需要分布式锁

    分布式锁具备的条件:

    1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行; 
    2、高可用的获取锁与释放锁; 
    3、高性能的获取锁与释放锁; 
    4、具备可重入特性; 
    5、具备锁失效机制,防止死锁; 
    6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

    特点:

    • 排他性 :任意时刻,只能有一个client能获取到锁
    • 容错性 :分布式锁服务一般要满足AP,也就是说,只要分布式锁服务集群节点大部分存活,client就可以进行加锁解锁操作
    • 避免死锁 :分布式锁一定能得到释放,即使client在释放之前崩溃或者网络不可达

    主要有三种方式:

    基于数据库实现分布式锁; 
    基于缓存(Redis等)实现分布式锁; 
    基于Zookeeper实现分布式锁;

    基于数据库 :在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。

    存在要解决的:

    1、因为是基于数据库实现的,数据库的可用性和性能将直接影响分布式锁的可用性及性能,所以,数据库需要双机部署、数据同步、主备切换;

    2、不具备可重入的特性,因为同一个线程在释放锁之前,行数据一直存在,无法再次成功插入数据,所以,需要在表中新增一列,用于记录当前获取到锁的机器和线程信息,在再次获取锁的时候,先查询表中机器和线程信息是否和当前机器和线程相同,若相同则直接获取锁;

    3、没有锁失效机制,因为有可能出现成功插入数据后,服务器宕机了,对应的数据没有被删除,当服务恢复后一直获取不到锁,所以,需要在表中新增一列,用于记录失效时间,并且需要有定时任务清除这些失效的数据;

    4、不具备阻塞锁特性,获取不到锁直接返回失败,所以需要优化获取逻辑,循环多次去获取。

    5、在实施的过程中会遇到各种不同的问题,为了解决这些问题,实现方式将会越来越复杂;依赖数据库需要一定的资源开销,性能问题需要考虑。

    基于Redis :

    SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。

    expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。

    delete key:删除key

      获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断;获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁;释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

    如果一个获取到锁的client因为某种原因导致没能及时释放锁,并且redis因为超时释放了锁,另外一个client获取到了锁?

    此时就要引入锁续约机制,也就是获取锁之后,释放锁之前,会定时进行锁续约,比如以锁超时时间的1/3为间隔周期进行锁续约。出名的有 redisson 、百度的 dlock。edis锁优势是性能出色,劣势就是由于数据在内存中,一旦缓存服务宕机,锁数据就丢失了。

    基于zk :

       特点 :zab协议、node存储模型和watcher机制;通过zab协议保证数据一致性,zookeeper集群部署保证可用性,node存储在内存中,提高了数据操作性能,使用watcher机制,实现了通知机制(比如加锁成功的client释放锁时可以通知到其他client)

    (1)创建一个目录mylock;
    (2)线程A想获取锁就在mylock目录下创建临时顺序节点;
    (3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
    (4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;
    (5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。

    优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。缺点:因为需要频繁的创建和删除节点,性能上不如Redis方式。

    • 性能:redis > zookeeper > db。
    • 避免死锁:DB通过应用层设置定时任务来删除过期还未释放的锁,redis通过设置超时时间来解决,而zookeeper是通过临时节点来解决。
    • DB可通过数据库同步复制,vip切换master来解决,redis可通过集群或者master-slave方式来解决,zookeeper本身自己是通过zab协议集群部署来解决的。注意,DB和redis的复制一般都是异步的,也就是说某些时刻分布式锁发生故障可能存在数据不一致问题,而zookeeper本身通过zab协议保证集群内(至少n/2+1个)节点数据一致性。

    • 锁唤醒:DB和redis分布式锁一般不支持唤醒机制(也可以通过应用层自己做轮询检测锁是否空闲,空闲就唤醒内部加锁线程),zookeeper可通过本身的watcher/notify机制来做。


    原文:https://blog.csdn.net/xlgen157387/article/details/79036337 

  • 相关阅读:
    Django部分面试题目
    网编部分
    面试题
    mysql安装
    并发编程
    集合以及深浅拷贝和和小数据池--个人一些经验总结
    稍微比较全的那种字典
    个人声明
    python
    python-pdf文件(持续更新
  • 原文地址:https://www.cnblogs.com/facker1/p/10619362.html
Copyright © 2011-2022 走看看