Skip to content

Latest commit

 

History

History
executable file
·
132 lines (73 loc) · 4.95 KB

分布式锁.md

File metadata and controls

executable file
·
132 lines (73 loc) · 4.95 KB

分布式锁

目录


分布式锁是解决协调分布式系统之间,同步访问共享资源的一种方式。

举个例子,火车票保证不超买?

这类问题,有很多种解决的办法。大概的思路有三种。

  • 第一种是基于数据库实现

  • 第二种是基于缓存系统去实现 (典型代表是使用Redis实现的锁服务)

  • 第三种是基于分布式一致性算法实现 (典型代表为Zookeeper)

  • 提供个其他思路,解决需要加锁的问题,也许更好的方式是绕过加锁的环节。比如说两个请求要同时访问一个资源,如果可以把他们串行化(比如放到同一个队列来处理)。当然,只是提供一个思路,这个要看业务具体的特点。


1. 基于数据库实现

基于数据库的实现,大概也是两个思路。第一种是悲观锁(select xxx for update),第二种是乐观锁。

  1. 悲观锁

    通过数据库行锁 select for update 锁住这行数据,然后将查询和插入的 SQL 在同一个事务中提交。

    select id from order where order_id = xxx for update
    

    不过,这种方案的问题是:

    • 基于 MySQL 行锁的方式会出现交叉死锁,比如事务 1 和事务 2 分别取得了记录 1 和记录 2 的排它锁,然后事务 1 又要取得记录 2 的排它锁,事务 2 也要获取记录 1 的排它锁,那这两个事务就会因为相互锁等待,产生死锁。

    • 当然,你可以通过“超时控制”解决交叉死锁的问题,但在高并发情况下,出现的大部分请求都会排队等待,所以“基于关系型数据库实现分布式锁”的方式在性能上存在缺陷。

  2. 乐观锁

    带个版本号 (具体细节先不展开了)


2. 使用zk作为分布式锁

  1. 在锁的目录下面,创建临时有序节点

  2. 每个节点需要申请锁的时候,在这个目录下面创建临时节点

  3. 在锁的目录下面,锁的序号最小的一个节点认为拿到了锁

  4. 后面的节点 依次watch前面的节点,当前面的节点变更的时候,后面的节点被唤醒,进行step3的判断

举个例子:

  1. 比如某进程获得到的节点序号为 /zk_lock/003,然后所有的节点列表为[/zk_lock/001,/zk_lock/002,/zk_lock/003],则对 /zk_lock/002 这个节点添加一个事件监听器。

  2. 如果锁释放了,会唤醒下一个序号的节点,判断是否自己的节点序号是最小。

  3. 比如 /zk_lock/001 释放了,/lock/002 监听到时间,此时节点集合为[/zk_lock/002,/zk_lock/003],则 /lock/002 为最小序号节点,获取到锁。

优点:

  1. 临时有序的zk节点,可以保证当进程挂掉的时候,锁还是可以被释放的。

  2. 每个节点watch前面的节点,相当于一个订阅回调的机制,这样业务层使用起来更舒服。


3. 使用redis作为分布式锁

在redis上面设置一个key作为锁

  1. 使用set key value 作为锁的标记位,然后expire key 10s 设置key的有效期

    问题:这个方案的问题是,是通过两条语句执行的,不能保证原子性.

  2. SET key value NX PX milliseconds

    key: 资源的key,标识一种资源。
    
    value: 此值必须在所有进程和所有获取锁的请求中都是唯一的。
    
    NX: 该命令仅在key不存在时才设置。
    
    PX milliseconds: 过期时间,单位为毫秒。
    

    这个方案是通过一条命令设置key的value,以及key的有效期,这样可以保证是一个原子的操作。

    释放 :

    if redis.call("get",KEYS[1]) == ARGV[1] then
        return redis.call("del",KEYS[1])
    else
        return 0
    end
    

    这种方案解决不了的问题是:

    如果在一个redis的集群中,当在master的节点完成了这个操作,但是由于redis是异步复制的,slave的节点不指定会马上收到通知的数据。如果master立刻宕机,会导致上锁失败。

  3. redlock 方案

    对于在集群中的枷锁redis给的方案是redlock,它的基本思路就是为锁准备多个独立的节点,在锁过期时间内只要超过半数获取到锁,就算成功,避免redis主从切换的时候,数据丢失。


总结 :

总结一下,分布式锁是解决多个进程同时访问临界资源的常用方法,在分布式系统中非常普遍,常见的实现方式是基于数据库,基于 Redis。

  • redis方案注重性能,但是不能保证安全性。

  • redlock方案,资源成本太高,不能公用redis cluster集群,还不能绝对保证安全性,有点鸡肋。

  • zookeeper方案,比较简单,没有时钟跳跃的问题。可靠性要比redis的高,但是性能比redis的要差


参考:

https://www.cnblogs.com/yunlongn/p/14609443.html