【Liquibase】关键组成分析

252 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

关键组成分析

书接上文,下面来分析下liquibase的组成部分。其中最重要的组成是changeLog文件,changeLog有四种形式,xml、json、yml、sql ,其中xml模式较为通用,下面以xml作讲解。

具体数据库的更改使用Changesets作为记录的基本单位,包含ChangeType来标识操作类型,像是新增一列或者设置主键等等。

ChangeSet

核心是基于changeLog概念来保证数据库中的元数据和xml中数据相同。每次都检查changeLog,再往深层看最基础的是ChangeSet,就是每个xml文件中对数据库的修改标签,例如:<changeSet id="202205131305-03" author="wqy">。liquibase会解析xml,获取对数据库的操作,逻辑在源码ChangeLogIterator.run()

具体配置示例信息如下:

<changeSet id="202110081227-01" author="wyr">
    <createTable tableName="remark"
        remarks="备注表">
        <column name="id" type="varchar(128)" autoIncrement="${autoIncrement}">
            <constraints primaryKey="true" nullable="false" />
        </column>
        <column name="contents" type="varchar(1024)" remarks="内容信息">
            <constraints nullable="true" />
        </column>
    </createTable>
</changeSet>

数据库配置

Liquibase默认使用Spring中spring.datasource配置的数据库地址,也可以配置自定义地址spring.liquibase详细实现可见LiquibaseAutoConfiguration

Liquibase会在数据库中创建两张表:DatabaseChangeLogDATABASECHANGELOGLOCK,分别用于保存修改记录和加锁。两张表的ddl如下。

  • DATABASECHANGELOG
CREATE TABLE `DATABASECHANGELOG` (
  `ID` varchar(255) NOT NULL,
  `AUTHOR` varchar(255) NOT NULL,
  `FILENAME` varchar(255) NOT NULL,
  `DATEEXECUTED` datetime NOT NULL,
  `ORDEREXECUTED` int(11) NOT NULL,
  `EXECTYPE` varchar(10) NOT NULL,
  `MD5SUM` varchar(35) DEFAULT NULL,
  `DESCRIPTION` varchar(255) DEFAULT NULL,
  `COMMENTS` varchar(255) DEFAULT NULL,
  `TAG` varchar(255) DEFAULT NULL,
  `LIQUIBASE` varchar(20) DEFAULT NULL,
  `CONTEXTS` varchar(255) DEFAULT NULL,
  `LABELS` varchar(255) DEFAULT NULL,
  `DEPLOYMENT_ID` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • DATABASECHANGELOGLOCK
CREATE TABLE `DATABASECHANGELOGLOCK` (
  `ID` int(11) NOT NULL,
  `LOCKED` bit(1) NOT NULL,
  `LOCKGRANTED` datetime DEFAULT NULL,
  `LOCKEDBY` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Liquibase在数据库中创建的表DATABASECHANGELOGLOCK用于加锁。

常常将Liquibase结合springboot使用,当springboot启动时,就会检查数据库中是否有这俩张表,没有会进行创建,检查内置表DatabaseChangeLog是否存在的方法为:checkLiquibaseTables(boolean updateExistingNullChecksums, DatabaseChangeLog databaseChangeLog,Contexts contexts, LabelExpression labelExpression)

检查内置表方法源码如下:

public void checkLiquibaseTables(boolean updateExistingNullChecksums, DatabaseChangeLog databaseChangeLog, Contexts contexts, LabelExpression labelExpression) throws LiquibaseException {
    ChangeLogHistoryService changeLogHistoryService = ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(getDatabase());
    changeLogHistoryService.init();
    if (updateExistingNullChecksums) {
        changeLogHistoryService.upgradeChecksums(databaseChangeLog, contexts, labelExpression);
    }
    LockServiceFactory.getInstance().getLockService(getDatabase()).init();
}

Liquibase脚本执行命令为 liquibase update,对应源码中的执行方法相应的是waitForLock,这个方法里面会先获取mysql的锁,就是日志中的Waiting for changelog lock。调用的方法是SelectFromDatabaseChangeLogLockStatement ,会去找DatabaseChangeLogLock表中的LOCKED字段值是否为1,值为1获取锁失败,继续执行一定时间的获取锁程序。

waitForLock对应源码如下:

public void waitForLock() throws LockException {

    boolean locked = false;
    long timeToGiveUp = new Date().getTime() + (getChangeLogLockWaitTime() * 1000 * 60);
    while (!locked && new Date().getTime() < timeToGiveUp) {
        locked = acquireLock();
        if (!locked) {
            LogFactory.getLogger().info("Waiting for changelog lock....");
            try {
                Thread.sleep(getChangeLogLockRecheckTime() * 1000);
            } catch (InterruptedException e) {
                ;
            }
        }
    }

    if (!locked) {
        DatabaseChangeLogLock[] locks = listLocks();
        String lockedBy;
        if (locks.length > 0) {
            DatabaseChangeLogLock lock = locks[0];
            lockedBy = lock.getLockedBy() + " since " + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(lock.getLockGranted());
        } else {
            lockedBy = "UNKNOWN";
        }
        throw new LockException("Could not acquire change log lock.  Currently locked by " + lockedBy);
    }
}

小结

以上,总结了liuqbase最关键的几个组成部分,且分析了一小段获取数据库表和锁的源码,后面会继续分析liquibase的源码。