分布式锁是一种思想,它的实现方案有很多,许多的分布式软件例如redis,memcache以及数据库都会存在具体的实现,实现方案大同小异,分布式锁主要包含三个过程:加锁,解锁以及锁超时。
补充,redis的优点:
- 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
- 支持丰富数据类型,支持string,list,set,sorted set,hash
- 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
- 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
redis分布式锁实现
满足特性
一个redis分布式锁需要实现三个特性才能满足最低要求:
1.独享(排斥性),首先任何一个时刻只有一个客户端持有锁。
2,无死锁:当持有锁的客户端崩溃或者网络异常情况下,锁可以被释放,可被其他客户端获取
3,容错:当集群中的某些redis节点服务停止,只要大部分节点还活着,redis锁还可以被获取。
最简单的实现-基于故障转移的实现
redis分布式锁最简单的一个实现就是在redis中设置一个key,并设置key的过期失效时间,以保证锁会被自动释放,当客户端释放该锁的时候,删除该key即可。这是目前很多分布式锁的实现算法,简单明了,但是却存在一个问题:这个算法存在一个严重的单节点失败问题。或许进一步有人会说增加一个slave节点,但其实是实现不同的,因为它不能实现资源的独享,因为redis的主从同步通常是异步的。
例如,在这种场景(主从结构)中存在明显的竞态:
- 客户端A从master获取到锁
- 在master将锁同步到slave之前,master宕掉了。
- slave节点被晋级为master节点
- 客户端B取得了同一个资源被客户端A已经获取到的另外一个锁。安全失效!
即当某个节点停止服务的时候,多个客户端同时获取同一把锁,如果能够接受这种小概率的错误,那么这种实现方案是可以被接受的。
单节点redis实现分布式锁算法
set resource random_value NX PX 3000
这种算法的主要核心在于:在上锁过程中,A客户端给key设置某一个随机数,这个随机数在任意客户端任意时刻需要满足唯一性,满足唯一性是为了更安全的释放锁。当A在处理某个阻塞任务时,即便这个锁在中途由于时间失效而被释放,并且B客户端获取到该锁后,A客户端完成阻塞任务,需要释放该锁,也会由于key值不相同,而无法释放由B加上的锁。
key的失效时间被称为“锁定有效时”,它不仅仅是key的自动失效时间,也是锁在被一个客户端获取后还可被另外一个客户端获取的有效时间。单节点分布式锁算法也是实现分布式加锁算法的基础。
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
redlock算法
概念:
1.TTL:Time To Live;只 redis key 的过期时间或有效生存时间
2.clock drift:时钟漂移;指两个电脑间时间流速基本相同的情况下,两个电脑(或两个进程间)时间的差值;如果电脑距离过远会造成时钟漂移值 过大
3.脑裂://TODO
Redission源码原理
Java中最为流行的redis分布式库中最为流行的便是redission,redission的原理如下:
redission源码分析
redission会将一段lua脚本发送到redis节点中(因为redis中的操作都是原子性的),通过lua脚本可以将一堆业务复杂的执行逻辑l封装,并交由redis原子性执行,lua脚本如下
”
这段脚本的含义是:
如果key[1]不存在,则设置ke1[1]的值argv[2],并设置过期时间argv[1];【保证当A1客户端还在阻塞执行的时候,不为其他客户端解锁】
如果key[1]值存在并且存储的值为argv[2],则重入锁并加1【可以保证watch dog监督延长加锁时间】
其中argv[1]表示过期时间,argv[2]表示客户端编号,类似8743c9c0-0795-4907-87fd-6c719a6b4586:1
链接:
https://www.jianshu.com/p/47fd7f86c848
http://redis.cn/topics/distlock.html 这个链接里面有很多redis中可以学习的地方