【问题标题】:Liquibase lock - reasons?Liquibase 锁 - 原因?
【发布时间】:2013-03-09 20:34:22
【问题描述】:

在针对 Oracle 服务器运行大量 liquibase 脚本时,我得到了这个。 SomeComputer 就是我。

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)

是否达到了同时会话/交易的数量?有人有什么想法吗?

【问题讨论】:

  • 您是否在 liquibase 持有锁时杀死了 JVM?这是我唯一发生这种情况的情况。
  • 似乎涉及另一台 PC:Konsultpc74。也许您同时将 liquibase 从不同的 PC 运行?如果没有,您对另一台 PC 有解释吗?
  • 我编辑了日志,但我不小心忘记将其更改为 SomeComputer
  • 您是否同时执行变更集?我认为每个文件和其中的每个变更集都是一个一个执行的。至少我是这样用的。我有一个主变更集文件,其中包含所有其他文件,并且一切都被一一运行。

标签: database oracle liquibase


【解决方案1】:

问题在于 Liquibase 中 SequenceExists 的错误实现。由于这些语句的变更集花费了很长时间并且被意外中止。然后下一次尝试执行持有锁的 liquibase-scripts。

  <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 或删除该表并重新创建。

【讨论】:

  • 在 liquibase 3.0.2(我正在使用的版本)中,不要从锁表中删除一行,否则下次运行 liquibase 时会出现不同的错误,因为 liquibase 期望有一行(或整张桌子不见了)。正如彼得所说,只是想添加该信息,因为在旧版本中,它似乎也可以删除该行。
【解决方案2】:

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

然后运行

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

对数据库有帮助。

您可能还需要将LOCKED=0 替换为LOCKED=FALSE

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

【讨论】:

  • 我需要将0 换成FALSE,但除此之外,它运行良好。谢谢
  • Liquibase 中有一个名为 releaseLocks 的内置命令可以执行@Adrian Ber 回答的内容,但我认为它与数据库无关。
  • 我刚刚在我的开发环境中遇到了这个错误。修复 DATABASECHANGELOGLOCK 表解决了它。
  • 我需要将FALSE 换成b'0'
  • 这是正确的解决方案,不要尝试清空表格,因为这无济于事。将其删除或将 LOCKED 标志更新为“FALSE”
【解决方案3】:

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

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

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

【讨论】:

    【解决方案4】:

    2020 年 6 月编辑

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

    Leos Literak,例如,按照这些说明操作,服务器无法启动。

    原答案

    这可能是由于已终止的 liquibase 进程未释放其对 DATABASECHANGELOGLOCK 表的锁定。那么,

    DELETE FROM DATABASECHANGELOGLOCK;
    

    可能会对你有所帮助。

    编辑: @Adrian Ber 的回答提供了比这更好的解决方案。仅当您在执行他的解决方案时遇到任何问题时才这样做。

    【讨论】:

    • 这并没有提供问题的答案。要批评或要求作者澄清,请在其帖子下方发表评论。
    • @Rachcha 我解释得更好。希望你更喜欢这样。
    • 不要遵循上面的建议。 DATABASECHANGELOGLOCK 必须包含没有任何行的行,否则会出现异常
    • 这没有帮助,我尝试了这个,而不是删除表或将锁定状态更新为“假”。它没有用。
    • 如果您遵循这个答案,那么未来的脚本很有可能不会运行,因为它们希望锁存在。如果你已经这样做了,你可以添加一个空锁来解决这个问题INSERT INTO yourdb.DATABASECHANGELOGLOCK VALUES (1, 0, null, null);
    【解决方案5】:

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

    SQL:

    SELECT gid FROM pg_prepared_xacts WHERE database='database_name';
    

    如果上述语句返回任何记录,则使用以下 SQL 语句回滚该准备好的语句。

    ROLLBACK PREPARED 'gid_obtained_from_above_SQL';
    

    【讨论】:

      【解决方案6】:

      没有提到使用哪个环境来执行 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,请编辑更正。

      【讨论】:

      • 能否提供更详细的解决方案描述?我们使用 Spring Boot 2 和 liquibase,不想每次手动删除数据库中的锁定状态。但我不明白你如何将 ForceReleaseLockService 注入到 liquibase。我不必在这个类上添加一个 Service/Component 注释,Spring 选择它作为主 bean 吗?
      • 最后一句中提到:“类必须放在liquibase.ext包中,会被Spring Boot 2自动配置拾取。”
      • 如何将类放在liquibase.ext 中,我需要在我的项目中定义那个包吗?
      • 我在我的项目中定义了那个包,它似乎可以工作,但我无法验证它。我用日志消息定义了一个 @PostConstruct 方法,但我没有看到它打印出来。
      • @akuma8:是的,只需在您的项目中使用该名称创建一个包。你在哪里定义了@PostConstruct 方法?在 ForceReleaseLockService 中?这不是 Spring 服务,因此不会被调用。
      【解决方案7】:

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

      DROP TABLE DATABASECHANGELOGLOCK;
      

      【讨论】:

        【解决方案8】:

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

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

        【讨论】:

          【解决方案9】:

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

          使用 Gradle 时,应为:gradlew releaseLocks

          【讨论】:

            【解决方案10】:

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

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

            或者干脆收拾桌子

            TRUNCATE TABLE INTERFACE_DATABASECHANGELOGLOCK
            

            【讨论】:

              【解决方案11】:

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

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

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2016-10-26
                • 1970-01-01
                • 2021-12-15
                • 1970-01-01
                • 2021-10-03
                • 2023-03-27
                • 1970-01-01
                相关资源
                最近更新 更多