MySQL GET_LOCK()在应该的时候没有失败

MySQL GET_LOCK()在应该的时候没有失败,mysql,stored-procedures,Mysql,Stored Procedures,MySQL版本=5.7.16 我需要自动处理数据库中的一些数据行。我有一个表的主键队列,以及三段应该一起工作的代码: 每10秒触发一次的事件,它调用: 一种控制过程,从队列表中选择单个行,并将其传递给: 行级过程,它对数据的各个行执行业务逻辑 通常,要完成的工作量要比10秒长得多,因此事件将在其调用的过程完成之前再次触发。这使得进程争用相同的行,所以我不希望发生这种情况 我已经用if(get_lock())语句将控制过程中的所有内容包装起来: drop procedure if exists s

MySQL版本=5.7.16

我需要自动处理数据库中的一些数据行。我有一个表的主键队列,以及三段应该一起工作的代码:

  • 每10秒触发一次的事件,它调用:
  • 一种控制过程,从队列表中选择单个行,并将其传递给:
  • 行级过程,它对数据的各个行执行业务逻辑
  • 通常,要完成的工作量要比10秒长得多,因此事件将在其调用的过程完成之前再次触发。这使得进程争用相同的行,所以我不希望发生这种情况

    我已经用if(get_lock())语句将控制过程中的所有内容包装起来:

    drop procedure if exists schema.controlling_procedure;
    
    delimiter $$
    create procedure schema.controlling_procedure()
    begin
      declare lnRowsToProcess int default 0;
    
      declare continue handler for sqlexception
      begin
        do release_lock('controlling_procedure');
      end;
    
      if (get_lock('controlling_procedure',1)) then
    
        select count(*)
        into   lnRowsToProcess
        from   vcs_raw.sys_pfq_1;
    
        if (lnRowsToProcess > 0) then
          begin
            ...
            declare zzzzzz
            ...    
    
            read_loop: loop
              select min(primary_key)
              into   thePrimaryKey
              from   vcs_raw.sys_pfq_1;
    
              if (thePrimaryKey is null)then
                leave read_loop;
              end if;
    
              call schema.row_level_procedure(thePrimaryKey);
    
              delete
              from  vcs_raw.sys_pfq_1
              where job_id = thePrimaryKey;
    
              set thePrimaryKey = null;
            end loop;
          end;
        end if;
      end if;
    
      do release_lock('controlling_procedure');
    end$$
    DELIMITER ;
    
    我希望发生的是,如果controlling_过程的实例已经在运行,那么同一过程的任何新实例都将无法获得锁,并且在不读取队列表或调用row_level_过程的情况下退出

    但当我在Workbench的Client Connections屏幕中查看时,我可以看到越来越多的连接,所有连接的信息值都设置为:

    call schema.row_level_procedure(thePrimaryKey);
    
    新的连接出现在表中,频率由事件决定(我已经尝试过事件时间表)

    看起来if(get_lock))测试总是通过,即使同一控制过程的其他实例已经在运行


    我误解了什么或做错了什么?

    我不能用一个简单的例子来重现这个问题:

    mysql>选择版本();
    +-----------+
    |版本(|
    +-----------+
    | 5.7.17    |
    +-----------+
    一行一组(0.00秒)
    mysql>设置全局事件调度程序=ON;
    查询正常,0行受影响(0.00秒)
    mysql>如果存在“evt_测试”,则删除事件;
    查询正常,0行受影响(0.00秒)
    mysql>删除过程(如果存在)`sp_test`;
    查询正常,0行受影响(0.00秒)
    mysql>如果存在“tbl\U测试”,则删除表,tbl\U尝试;
    查询正常,0行受影响(0.00秒)
    mysql>创建不存在的表`tbl\U尝试`(
    ->`id`BIGINT无符号自动\u递增主键,
    ->`connection_id`BIGINT UNSIGNED,
    ->`created_at`TIMESTAMP DEFAULT CURRENT_TIMESTAMP()
    -> );
    查询正常,0行受影响(0.00秒)
    mysql>创建表(如果不存在)`tbl\U测试`(
    ->`id`BIGINT无符号自动递增主键,
    ->`created_at`TIMESTAMP DEFAULT CURRENT_TIMESTAMP()
    -> );
    查询正常,0行受影响(0.01秒)
    mysql>分隔符//
    mysql>创建过程`sp_test`()
    ->开始
    ->在“tbl\U尝试次数”(“连接id”)值(连接id())中插入;
    ->如果(GET_LOCK('controlling_procedure',0)),则
    ->做基准测试(35000000,AES_加密(“你好”,“再见”);
    ->释放锁定(“控制程序”);
    ->在“tbl\U测试”(`id`)值中插入空值;
    ->如果结束;
    ->结束//
    查询正常,0行受影响(0.00秒)
    mysql>分隔符;
    mysql>按计划每1秒创建事件'evt_test'
    ->启动当前时间戳
    ->结束当前时间戳+间隔10秒
    ->竣工保护区
    ->请务必调用'sp_test';
    查询正常,0行受影响(0.00秒)
    mysql>选择'id','connection\u id','created\u at`
    ->来自“tbl_尝试”;
    +----+---------------+---------------------+
    |id |连接| id |已创建||
    +----+---------------+---------------------+
    |  1 |            62 | 2010-01-01 00:00:17 |
    |  2 |            63 | 2010-01-01 00:00:18 |
    |  3 |            64 | 2010-01-01 00:00:19 |
    |  4 |            65 | 2010-01-01 00:00:20 |
    |  5 |            66 | 2010-01-01 00:00:21 |
    |  6 |            67 | 2010-01-01 00:00:22 |
    |  7 |            68 | 2010-01-01 00:00:23 |
    |  8 |            69 | 2010-01-01 00:00:24 |
    |  9 |            70 | 2010-01-01 00:00:25 |
    | 10 |            71 | 2010-01-01 00:00:26 |
    | 11 |            72 | 2010-01-01 00:00:27 |
    +----+---------------+---------------------+
    一组11行(0.00秒)
    mysql>选择'id','created\u at`
    ->来自“tbl_测试”;
    +----+---------------------+
    |id |创建于|
    +----+---------------------+
    |  1 | 2010-01-01 00:00:26 |
    |  2 | 2010-01-01 00:00:35 |
    +----+---------------------+
    一组2行(0.00秒)
    
    问题在于此代码:

    declare continue handler for sqlexception
      begin
        do release_lock('controlling_procedure');
      end;
    
    行级过程的单独调用正常工作(超过95%的时间),但有些调用抛出异常。上面的处理程序捕获了异常,释放了锁,然后继续。因为锁现在可用,所以下次触发事件时,它调用的控制过程可以获得锁,现在有另一个控制过程正在运行。最终它也可能放弃锁,允许另一个过程运行


    相反,应该让处理程序捕获异常,释放锁并终止,而不是继续,或者干脆在运行时不释放锁。

    GET\u lock
    如果可以在给定的超时内获得锁,则成功。这意味着您的
    获取锁定
    会等待一秒钟。它不会失败,它会等待。我应该澄清一下,我最初将超时设置为零,而不是一。在这种情况下,我应该期望GET_LOCK在这种情况下失败,对吗?好吧,它确实应该失败。因此,绝对明确地说,问题似乎是GET_LOCK调用没有等待,也没有失败。它正在过去。