分布式锁的几种实现 (4)

这里还可能存在另外一个问题,虽然我们对name 使用了唯一索引,并且显示使用for update来使用行级锁。但是,MySql会对查询进行优化,即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL 通过判断不同执行计划的代价来决定的,如果 MySQL 认为全表扫效率更高,比如对一些很小的表,它就不会使用索引,这种情况下 InnoDB 将使用表锁,而不是行锁。如果发生这种情况就悲剧了。。。

还有一个问题,就是我们要使用排他锁来进行分布式锁的lock,那么一个排他锁长时间不提交,就会占用数据库连接。一旦类似的连接变得多了,就可能把数据库连接池撑爆。

2.3.3 基于数据库的乐观锁

大多数是基于数据版本(version)的记录机制实现的。何谓数据版本号?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表添加一个“version”字段来实现读取出数据时,将此版本号一同读出,之后更新时,对此版本号加1。在更新过程中,会对版本号进行比较,如果是一致的,没有发生改变,则会成功执行本次操作;如果版本号不一致,则会更新失败。

注意ABA 问题等。

3. 比较总结

对于redis的分布式锁而言,它有以下缺点:

它获取锁的方式简单粗暴,获取不到锁直接不断尝试获取锁,比较消耗性能。另外来说的话,redis的设计定位决定了它的数据并不是强一致性的,在某些极端情况下,可能会出现问题。锁的模型不够健壮,即便使用redlock算法来实现,在某些复杂场景下,也无法保证其实现100%没有问题。但是另一方面使用redis实现分布式锁在很多企业中非常常见,而且大部分情况下都不会遇到所谓的“极端复杂场景”,所以使用redis作为分布式锁也不失为一种好的方案,最重要的一点是redis的性能很高,可以支撑高并发的获取、释放锁操作。

zookeeper天生设计定位就是分布式协调,强一致性。锁的模型健壮、简单易用、适合做分布式锁。如果获取不到锁,只需要添加一个监听器就可以了,不用一直轮询,性能消耗较小。

但是如果有较多的客户端频繁的申请加锁、释放锁,对于zk集群的压力会比较大。

我们介绍了几种分布式锁的实现方式,并进行了一些优缺点比较,哪种方式都无法做到完美。就像CAP一样,在复杂性、可靠性、性能等方面无法同时满足,所以需要根据不同的应用场景选择最适合的方式。

比较角度 结果
从理解的难易程度角度(从低到高)   数据库 > 缓存 > Zookeeper  
从实现的复杂性角度(从低到高)   Zookeeper >= 缓存 > 数据库  
从性能角度(从高到低)   缓存 > Zookeeper >= 数据库  
从可靠性角度(从高到低)   Zookeeper > 缓存 > 数据库  
References:

我们的系统需要什么样的分布式锁?

分布式锁的实现方式介绍

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgdxyg.html