You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Liquibase双重检查锁模式?无更新时不获取锁

Liquibase优化启动逻辑:无变更时跳过锁获取

完全可以实现你想要的逻辑——无新变更时跳过锁获取直接启动,既提升启动速度,又降低残留锁的概率。结合你用Spring集成Liquibase的场景,具体实现步骤如下:

1. 实现无锁预检查变更集的工具类

先写一个工具类,基于Liquibase API在不获取锁的前提下,检查是否有未执行的变更集。核心思路是直接对比本地变更文件和databasechangelog表的记录,避免触发默认的锁操作:

public class LiquibaseChangeChecker {
    public static boolean hasUnrunChanges(DataSource dataSource, String changeLogLocation) throws LiquibaseException {
        // 构建无锁的Liquibase实例
        Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(dataSource.getConnection()));
        Liquibase liquibase = new Liquibase(changeLogLocation, new ClassLoaderResourceAccessor(), database);
        
        // 跳过锁操作,直接查询未执行的变更集
        List<ChangeSet> unrunChanges = liquibase.listUnrunChangeSets(null, null);
        return !unrunChanges.isEmpty();
    }
}

2. 自定义条件注解判断是否需要迁移

创建一个@ConditionalOnLiquibaseChangesNeeded注解,结合Spring的Condition机制,在启动时自动判断是否需要执行迁移:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(LiquibaseChangesNeededCondition.class)
public @interface ConditionalOnLiquibaseChangesNeeded {
}

public class LiquibaseChangesNeededCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        try {
            DataSource dataSource = context.getBeanFactory().getBean(DataSource.class);
            return LiquibaseChangeChecker.hasUnrunChanges(dataSource, "classpath:/db/changelog");
        } catch (Exception e) {
            // 检查失败时默认执行迁移,避免漏更
            return true;
        }
    }
}

3. 优化SpringLiquibase Bean的创建逻辑

  • 用自定义条件注解控制SpringLiquibase Bean的初始化:只有当预检查发现有未执行变更时,才创建并初始化SpringLiquibase,无变更时直接跳过。
  • 扩展SpringLiquibase类,在执行迁移前做二次检查,防止预检查后其他节点已经执行完变更,避免不必要的锁获取:
public class SafeSpringLiquibase extends SpringLiquibase {
    @Override
    public void afterPropertiesSet() throws LiquibaseException {
        // 二次确认是否有未执行变更
        if (!LiquibaseChangeChecker.hasUnrunChanges(getDataSource(), getChangeLog())) {
            return;
        }
        super.afterPropertiesSet();
    }
}
  • 配置Bean:
@Bean
@ConditionalOnLiquibaseChangesNeeded
public SpringLiquibase springLiquibase(DataSource dataSource) {
    SafeSpringLiquibase liquibase = new SafeSpringLiquibase();
    liquibase.setDataSource(dataSource);
    liquibase.setChangeLog("classpath:/db/changelog/db.changelog-master.yaml");
    // 可选:配置锁重试参数,应对临时锁占用
    liquibase.setLockRetryCount(5);
    liquibase.setLockRetryWaitTime(1000);
    return liquibase;
}

4. 残留锁兜底处理

如果还是出现崩溃导致的残留锁,可以在工具类的预检查步骤中加入锁清理逻辑:检查databasechangeloglock表,如果锁记录存在且对应的节点标识(比如IP、进程ID)已失效,自动释放锁。

内容的提问来源于stack exchange,提问作者Andrey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:05:20