你们可能都知道,锁的存在本质上是为了解决共享资源互斥访问的问题,为了解决这个问题,在单机系统中(一个进程),不少开发语言都提供了锁的特性,好比说java的synchoronized、lock等;在分布式系统中(多个进程),则须要实现分布式锁,由于语言层面的锁特性不足以解决问题。php
关于分布式锁的概念、特性以及实现方式,网上有不少相关的文章,感兴趣的童鞋能够自行搜索。html
简单讲,分布式锁也须要知足通常开发语言提供的锁的一些基本特性:java
* 互斥性:多个线程(可能位于不一样的进程上)访问共享资源时,同时只能有一个线程访问。redis
阻塞性:一个线程访问共享资源时,其余线程应该被阻塞执行。数据库
如今常见的分布式锁的实现方案有:缓存
基于数据库实现分布式锁架构
基于缓存(redis,memcached etc.)实现分布式锁分布式
...ide
上面分享了一些关于分布式锁的理论知识,接下来从liquibase和flyway两个library来解析它们实现分布式锁的区别。memcached
有同窗可能知道,liquibase和flyway是数据库表结构改变的管理工具,这类工具的目的是使对数据库表结构的改变作到自动化,以防止人工对数据库表结构的改动带来的风险。两个工具的基本原理都相似,便是对数据库表结构的每一次改动维护成一条changeset(changeset能够是建立一个表,也能够是增长一个字段等),当应用程序启动时,会依次执行维护的changeset,一旦changeset被执行过,就不会被再执行,具体如何使用能够查看:
liquibase:https://www.liquibase.org/index.html
目前,这两个工具在不少项目中都有应用。
以前在项目(微服务架构)中,遇到过一个liquibase的问题:一个service用liquibase管理数据库change,有时候service在启动阶段忽然crash,再次启动,一直启动不起来,控制台一直看到以下日志:
INFO … Liquibase: Waiting for changelog lock.... INFO … Liquibase: Waiting for changelog lock.... INFO … Liquibase: Waiting for changelog lock....
在另一个场景,有时候也发现过相似的问题,一个service有两个instance,在第一个instance启动阶段,因为未知缘由忽然crash,这时候第二个instance再也启动不起来,控制台一样看到和上面同样的日志:
INFO … Liquibase: Waiting for changelog lock.... INFO … Liquibase: Waiting for changelog lock.... INFO … Liquibase: Waiting for changelog lock....
而一样的,有的service使用的flyway,却没有遇到过这样的问题。这是为何呢?
固然,在正常状况下,第一个service启动没问题,另一个service就会成功启动起来。
其实,上面这个场景是典型的一个分布式锁应用的场景:service的两个instance须要互斥访问数据库以执行changeset,第一个instance执行过程当中,第二个instance须要阻塞等待;第一个instance执行完了,会自动释放锁,接着第二个instance继续执行。
因为这两个library自己就是数据库相关的工具,自然就要依赖数据库,因此采用的分布式锁的实现方案就是基于数据库实现的方案。一般,基于数据库实现分布式锁有两种方式(参考https://blog.csdn.net/dingjianmin/article/details/82763871):
1.基于数据库表
2.基于数据库排他锁
两个library分别采用了这两种方式,Liquibase采用的是第一种-基于数据库表,Flyway采用的是第二种-基于数据库排他锁。
Liquibase维护了一张databasechangeloglock表来实现分布式锁。
Flyway则利用的是数据库的排他锁,以下图源码所示。(参考http://dbabullet.com/index.php/2018/03/29/best-practices-using-flyway-for-database-migrations/)
采用第一种基于数据库表的实现方式,一个关键的问题就是,如何防止一个线程解锁失败,致使锁记录一直在数据库中,其余线程没法再得到到锁?而这个问题也就是上面项目中遇到的liquibase的问题,一个service instance忽然crash致使解锁失败,其余线程没法再得到到锁。
对于这个问题,liquibase官网只给出了一个workaround去清理脏锁,没有具体的计划fix这个问题。
而因为flyway采起的是第二种基于数据库排他锁的方式,则不会有这个问题。由于基于数据库的排他锁,若是service忽然crash,service跟数据库的链接也就会断掉,加在表上的排他锁就会自动释放,进而接下来其余线程能够继续得到锁。
最后,针对分布式锁各类方案的解释,网上有不少写得挺好的文章,下面列出一些仅供参考:
漫画:什么是分布式锁?
漫画:如何用Zookeeper实现分布式锁?