使用分布式锁时要考虑的事情
使用分布式锁时要考虑的事情
ivansli为什么需要锁
说起锁,你可能会想起,门锁、保险箱锁、汽车锁、手机锁。甚至,你内心上的那把心锁。
那么问题来了:为什么需要锁?
很多人会说,为了安全,防止丢失,防止被抢、被盗呀。因为,我有需要保护的金钱、资料、钻石、黄金等等这些珍贵的东西。
那,计算机中需要锁吗?
答案是,需要,非常需要。因为计算机也有需要保护起来、不想被篡改的——数据,这种珍贵的东西。
如今的计算机基本都是多核、多进程/线程、并发/并行、乱序执行指令的系统。对于某些共用的数据资源进行访问、修改,如果不进行保护,会引发意想不到的问题。
这些公用的数据资源被称为临界资源。
通常,在访问这些临界资源时,会对其进行加锁操作。把并行访问变为串行访问,以保证数据的一致性。
锁的分类
从硬件、软件系统维度,锁分为:
- 硬件支持的锁(CPU支持的总线锁)
- 操作系统/应用程序支持的应用锁
- 例如,程序的原子操作可以基于硬件支持的总线锁实现
从进程维度,锁分为:
- 进程内部的锁(通常称为mutex锁)
- 进程外部的锁(分布式锁)
- 进程内部锁锁定的是进程内部的临界资源(对应单机系统)
进程外部锁锁定的是进程外部的临界资源(对应分布式系统)
什么是分布式锁
要想知道什么是分布式锁,首先你要知道什么是分布式系统。
互联网是由很多计算机组成,每一台计算机被视为互联网的一个节点,这个单一的节点所组成的系统叫单机系统,而整个互联网组成的系统就叫做分布式系统。
分布式锁简而言之是指,在分布式系统中,当多个节点对共有的进程外部临界资源进行访问时,为了保证数据的一致性而使用的一种,把并行访问变成串行访问的一种策略。
分布式锁需要满足的条件
- 保证锁的互斥性:无论何时,只能一个客户端获取锁,不能同时有两个客户端获取到锁
- 保证锁的安全性:锁只能被该锁的持有者删除,不能由其它客户端删除
- 防止出现死锁:锁的持有者因某些原因(进程挂掉、服务器down机)未释放锁,其它客户端无法获取得锁
- 保证锁的容错:当提供锁服务的部分节点down掉时,锁持有者仍然能够获取锁和释放锁
只有满足了上面的四个条件,分布式锁在实际的使用中才能够安全可靠。
什么组件可以实现分布式锁
实现分布式锁的组件,通常有以下几种:
- 基于数据库的锁(不常用)
- 基于ZooKeeper的分布式锁(使用广泛)
- 基于Redis的分布式锁(使用广泛)
- 基于etcd的分布式锁(与ZooKeeper类似)
关于分布式锁需要知道的事情
- 分布式锁是否需要过期时间
- 分布式锁如何加锁、解锁
- 怎么保证锁的高可用性及容错性
- 锁提前过期了,如何处理
分布式锁是否需要过期时间
需要设置过期时间,是因为如果不设置过期时间会存在如下问题:
- 锁持有者长时间或者不释放,导致其他进程拿不到锁从而饿死
- 锁持有者意外挂掉,锁不能被解锁,从而导致锁永远不能释放
举个例子,大家去公园玩,要排队上厕所。如果有人把门从里面锁上之后,处于某些原因决定住在里面不出去,那岂不是导致后面的人无法使用。
如何加锁、解锁
Redis 加锁使用 setnx
1 | SET key value NX EX max-lock-time |
- 加锁时设置过期时间,时间以具体业务情况为主
- 加锁时设置唯一值保存在锁中,用于解锁
Redis 解锁使用 Redis命令执行lua脚本
1 | if redis.call('get', KEYS[1]) == ARGV[1] |
- 使用lua脚本执行是为了保证原子性
- 解锁包含两个操作动作:判断当前锁是否属于自己,是,则删除。不是,则不操作。以免误删。
怎么保证锁的高可用性及容错性
分布式锁一般是基于集群之上实现的,例如:zookeeper、Redis集群。
使用集群本身就是为了保证高可用性,但是在容错性方面却存在差异:
- zookeeper集群支持CP(发生分区时,保证数据一致性),能够保证数据的一致性及容错性。
- Redis集群则需要注意,Redis集群支持AP(发生分区时,只保证可用性不保证保证数据一致性),所以需要用到Red-Lock方案。
其原理殊途同路,都是需要集群节点保证半数以上成功,才会视为成功。
锁提前过期了,如何处理
关于这个问题,很多人在使用时可能没想过。
场景:设置分布式锁的过期时间是10秒,但是程序却需要执行15秒。那么,在10秒后锁就过期了。之后,其他进程抢到锁,也开始执行对临界资源的操作。此刻,多个进程在操作同一数据,会导致意想不到的问题。
解决方案可以参考redisson的操作:先上锁,默认过期时间30秒,如果处理完了,走正常逻辑。对一个值加锁之后,它会在自身维护一个加锁池的队列,每过10秒去重新设置一下过期时间。这样,即使一个锁对应的进程挂掉,也就维持30秒的时间。如果没有挂,并且30秒不够用了,他的内部队列会不断的更新这个过期时间为30秒,保证不会出现问题。
总结
分布式锁在分布式系统开发过程中,起着至关重要的作用,因为多个进程操作相同的临界资源,会导致数据的不一致性。稍一不慎,就会导致很严重的事故从而造成损失。为了解决这个问题,从而产生了各种分布式锁的实现方案以及组件。
在实际开发中,使用最多的当属于zookeeper与Redis,笔者觉得在使用之前要先熟悉原理,并问自己一些上面的问题是否知道以及怎么解决,从而才能能好的把控业务。
扩展阅读
- ZooKeeper和CAP理论及一致性原 https://blog.csdn.net/yanpenglei/article/details/80362561
- 分布式锁的几种实现方式 https://www.hollischuang.com/archives/1716