一、什么是分布式锁?
锁是实现多线程同时访问同一共享资源,保证同一时刻只有一个线程可访问共享资源所做的一种标记。分布式锁是在分布式环境下,系统部署在不同机器、集群,实现多进程争夺共享资源的标记。为了保证多个进程能看到锁,锁被存在公共存储(比如 Redis、Memcache、数据库等三方存储中),以实现多个进程并发访问同一个临界资源,同一时刻只有一个进程可访问共享资源,确保数据的一致性。
二、分布式锁的实现方式
1、基于数据库实现分布式锁
基于数据库实现分布式锁,主要是通过在数据库建立一张锁表,当我们要锁住某个资源时,就在该表中增加一条记录,想要释放锁的时候就删除这条记录。数据库对共享资源做了唯一性约束,如果有多个请求被同时提交到数据库的话,数据库会保证只有一个操作可以成功,操作成功的那个线程就获得了访问共享资源的锁,可以进行操作。
实现方式:每次请求可以在数据库表中插入一条同样的数据,请求处理完,删除这条记录。同时,在数据库表中添加唯一索引,保证这条记录唯一,如果多条数据同时请求,只有一条成功,其他的会因为唯一键冲突导致插入失败。
总结:
- 由于每次请求都需要落库,所以性能较差
- 如果数据库故障,会导致整个系统不可用
- 死锁问题:如果数据库锁一直未得到释放,未获得锁的进程只能一直等待已获得锁的进程主动释放锁。一旦已获得锁的进程挂掉或者解锁操作失败,会导致锁记录一直存在数据库中,其他进程无法获得锁。
2、基于缓存实现分布式锁
由于基于数据库实现分布式锁需要频繁的操作数据库,涉及读写磁盘,性能较低,于是就出现了基于缓存的分布式锁。所谓基于缓存,也就是说把数据存放在计算机内存中,不需要写入磁盘,减少了 IO 读写。
Redis 通常可以使用 setnx(key, value) 函数来实现分布式锁。key 和 value 就是基于缓存的分布式锁的两个属性,其中 key 表示锁 id,value = currentTime + timeOut,表示当前时间 + 超时时间。也就是说,某个进程获得 key 这把锁后,如果在 value 的时间内未释放锁,系统就会主动释放锁。
相对于基于数据库实现分布式锁的方案来说,基于缓存实现的分布式锁的优势表现在以下几个方面:
性能更好。数据被存放在内存,而不是磁盘,避免了频繁的 IO 操作。
避免单点故障。很多缓存可以跨集群部署,避免了单点故障问题。
可设置超时时间。很多缓存服务都提供了可以用来实现分布式锁的方法,比如 Redis 的 setnx 方法等。可以直接设置超时时间来控制锁的释放,因为这些缓存服务器一般支持自动删除过期数据。
这个方案的不足是,通过超时时间来控制锁的失效时间,并不是十分靠谱,因为一个进程执行时间可能比较长,或受系统进程做内存回收等影响,导致时间超时,从而不正确地释放了锁。
3、基于 ZooKeeper 实现分布式锁
参考引用中博客
引用:极客时间读书笔记
https://blog.csdn.net/crazymakercircle/article/details/85956246