liqubase锁-原因?

当我在oracle服务器上运行大量的liquibase脚本时,我就遇到了这种情况。电脑就是我。

Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Liquibase Update Failed: Could not acquire change log lock.  Currently locked by SomeComputer (192.168.15.X) since 2013-03-20 13:39
SEVERE 2013-03-20 16:59:liquibase: Could not acquire change log lock.  Currently locked by SomeComputer (192.168.15.X) since 2013-03-20 13:39
liquibase.exception.LockException: Could not acquire change log lock.  Currently locked by SomeComputer (192.168.15.X) since 2013-03-20 13:39
at liquibase.lockservice.LockService.waitForLock(LockService.java:81)
at liquibase.Liquibase.tag(Liquibase.java:507)
at liquibase.integration.commandline.Main.doMigration(Main.java:643)
at liquibase.integration.commandline.Main.main(Main.java:116)

可能是达到了同时会话/事务的数量吗?有人有什么想法吗?

300773 次浏览

问题是在Liquibase中执行SequenceExists有bug。因为包含这些语句的变更集花费了很长时间,并且意外地中止了。然后,下一次尝试执行liquibase脚本时,锁被持有。

  <changeSet author="user" id="123">
<preConditions onFail="CONTINUE">
<not><sequenceExists sequenceName="SEQUENCE_NAME_SEQ" /></not>
</preConditions>
<createSequence sequenceName="SEQUENCE_NAME_SEQ"/>
</changeSet>

一个解决方法是使用纯SQL来检查这个:

  <changeSet author="user" id="123">
<preConditions onFail="CONTINUE">
<sqlCheck expectedResult="0">
select count(*) from user_sequences where sequence_name = 'SEQUENCE_NAME_SEQ';
</sqlCheck>
</preConditions>
<createSequence sequenceName="SEQUENCE_NAME_SEQ"/>
</changeSet>

Lockdata存储在DATABASECHANGELOCK表中。要摆脱锁,只需将1更改为0或删除该表并重新创建。

有时,如果更新应用程序突然停止,那么锁仍然卡住。

然后运行

UPDATE DATABASECHANGELOGLOCK SET LOCKED=0, LOCKGRANTED=null, LOCKEDBY=null where ID=1;

对数据库有帮助。

你可能还需要用LOCKED=FALSE替换LOCKED=0

或者你可以简单地删除DATABASECHANGELOGLOCK表,它将被重新创建。

我知道这不是OP的问题,但我最近遇到了这个问题,原因不同。作为参考,我在SQL Server上使用了Liquibase Maven插件(Liquibase - Maven -plugin:3.1.1)。

不管怎样,我错误地复制并粘贴了一个SQL Server“use”语句到我的一个切换数据库的脚本中,所以liquibase正在运行和更新DATABASECHANGELOGLOCK,在正确的数据库中获取锁,但随后切换数据库以应用更改。我不仅不能在正确的数据库中看到我的更改或liquibase审计,而且当然,当我再次运行liquibase时,它无法获得锁,因为锁已经在“错误的”数据库中释放,因此仍然锁定在“正确的”数据库中。我希望liquibase在释放锁之前检查锁是否仍然被应用,也许这是liquibase中的一个bug(我还没有检查过),但它很可能在以后的版本中得到解决!也就是说,我认为它可以被认为是一个功能!

我知道,这是一个学生的错误,但我在这里提出这个问题,以防有人遇到同样的问题!

2020年6月编辑

不要听从这个建议。多年来,它给许多人带来了麻烦。很久以前它就为我工作了,我真诚地发布了它,但显然不是这样做的方式。DATABASECHANGELOCK表中需要有内容,因此只删除其中的所有内容而不删除表是一个坏主意。

例如,狮子Literak遵循这些指示,服务器无法启动。

原来的答案

这可能是由于一个被杀死的liqubase进程没有释放它在DATABASECHANGELOGLOCK表上的锁。然后,

DELETE FROM DATABASECHANGELOGLOCK;

也许能帮到你。

编辑: @Adrian Ber的答案提供了一个更好的解决方案。只有当你在解他的解时有问题时才这么做。

有时截断或删除DATABASECHANGELOGLOCK表不起作用。我使用PostgreSQL数据库,遇到过这个问题很多次。我所做的解决方案是回滚在后台为该数据库运行的准备语句。尝试回滚所有准备好的语句,并再次尝试liqubase更改。

SQL:

SELECT gid FROM pg_prepared_xacts WHERE database='database_name';

如果上面的语句返回任何记录,那么用下面的SQL语句回滚准备好的语句。

ROLLBACK PREPARED 'gid_obtained_from_above_SQL';

这里没有提到使用哪个环境来执行Liquibase。如果是Spring Boot 2,可以扩展liquibase.lockservice.StandardLockService,而不需要直接运行SQL语句,这更干净。例如:

/**
* This class is enforcing to release the lock from the database.
*
*/
public class ForceReleaseLockService extends StandardLockService {


@Override
public int getPriority() {
return super.getPriority()+1;
}


@Override
public void waitForLock() throws LockException {
try {
super.forceReleaseLock();
} catch (DatabaseException e) {
throw new LockException("Could not enforce getting the lock.", e);
}
super.waitForLock();
}
}

代码正在强制释放锁。这在测试设置中很有用,在测试设置中,如果出现错误或调试中止,可能不会调用发布调用。

该类必须放置在liquibase.ext包中,并将由Spring Boot 2自动配置拾取。

Liquibase 4

注意扩展加载机制在Liquibase 4中已经改变。

现在必须在META-INF/services下创建一个具有实现的完整接口包名称的文件,并且在该文件中必须列出所有扩展名。

这可能意味着在META-INF/services/liquibase.lockservice.LockService

必须添加这一行:

com.company.liquibase.impl.ForceReleaseLockService

我还没有试过,还在用Liquibase 3,请修改。

您可以手动或使用query安全地删除表。它将被自动重新创建。

DROP TABLE DATABASECHANGELOGLOCK;

postgres 12中,我需要使用这个命令:

UPDATE DATABASECHANGELOGLOCK SET LOCKED=false, LOCKGRANTED=null, LOCKEDBY=null where ID=1;

请让Liquibase自己处理表格。正如user1434769提到的,正确的做法是使用Liquibase的releaseLocks命令。

当使用Gradle时,这将是:gradlew releaseLocks

如果@Adrian发布的解决方案不起作用,您可以尝试在INTERFACE_DATABASECHANGELOGLOCK表中执行相同的操作,该表包含有关锁的附加信息。

UPDATE INTERFACE_DATABASECHANGELOGLOCK SET LOCKED=0, LOCKGRANTED=null, LOCKEDBY=null where ID=1;

或者干脆把桌子清理干净

TRUNCATE TABLE INTERFACE_DATABASECHANGELOGLOCK

我在前面刚刚截断了DatabaseChangeLogLock表,当一个锁在失败后仍然存在时(这通常对我来说仍然有效)。

然而,这个项目(https://github.com/blagerweij/liquibase-sessionlock)现在存在,它在使用Oracle、PostgreSQL和MySQL时使用本地数据库会话锁来锁定Liquibase。

JAR文件可以放到现有的Liquibase 4中。x+应用程序,并将被Liquibase自动检测。这将使用会话锁进行锁定。如果应用程序崩溃或锁被持有时关闭,这些锁将自动释放。这可能会阻止锁定状态的发生(如果您正在使用受支持的数据库之一)

liquibase changelog的默认表是DATABASECHANGELOCK,但是可以通过设置下面的属性来覆盖或设置为自定义表。

在application.properties

database-change-log-lock-table=<SOME_CUSTOM_TABLE_NAME>

如果应用程序设置了此属性,则

UPDATE <SOME_CUSTOM_TABLE_NAME> SET LOCKED=0, LOCKGRANTED=null, LOCKEDBY=null where ID=1;

或删除表

drop table <SOME_CUSTOM_TABLE_NAME>