MySQL - 检测和处理存储过程中的锁定等待超时

时间:2013-09-05 21:41:22

标签: mysql stored-procedures error-handling locking

问题

纯粹出于学术原因,我想知道你是否可以为mysql存储过程添加一个处理程序,如果其中一个查询被锁定(例如SELECT ... FOR UPDATE,它就能从锁等待超时错误中恢复或UPDATE)查询。


示例

这是假设一个innoDB数据库,设置为issolation level Repeatable read,并定义了一个空的users表。

1。示例过程:

DROP PROCEDURE IF EXISTS `lock_test`;
DELIMITER ;;
CREATE PROCEDURE `lock_test`(OUT status ENUM('success','timeout'))
    MODIFIES SQL DATA
BEGIN
    START TRANSACTION;

        SELECT * FROM `users` FOR UPDATE;
        SET status := 'success';

    COMMIT;
END;;
DELIMITER ;

2。在mysql终端1中运行代码:

START TRANSACTION;
SELECT * FROM `users` FOR UPDATE;
  • 将显示users的内容,但交易将保持打开状态。

3。在mysql终端2中运行代码:

CALL `lock_test`(@out);
SELECT @out;
  • 交易将一直运行,直到超时(默认值innodb_lock_wait_timeout为50秒)

是否可以在lock_test()程序中添加处理程序,以便我们可以让@out保持'超时'?

1 个答案:

答案 0 :(得分:2)

花了一些时间阅读MySQL Handler Documentation后,我得到了我想要的东西:

DROP PROCEDURE IF EXISTS `lock_test`;
DELIMITER ;;
CREATE PROCEDURE `lock_test`(OUT status_out VARCHAR(255))
    MODIFIES SQL DATA
BEGIN
    DECLARE procedure_attempts INT DEFAULT 5;
    DECLARE query_timeout INT DEFAULT FALSE;
    SET status_out := 'start';

    procedure_loop:
        REPEAT
            BEGIN
                DECLARE CONTINUE HANDLER FOR 1205
                -- Error: 1205 SQLSTATE: HY000 (ER_LOCK_WAIT_TIMEOUT)
                    BEGIN
                        SET query_timeout := TRUE;
                        SET status_out := CONCAT(status_out,'-timeout');
                    END;

                IF ( procedure_attempts < 1) THEN
                    LEAVE procedure_loop;
                END IF;

                START TRANSACTION;

                    SELECT * FROM `users` FOR UPDATE;

                    IF (query_timeout) THEN
                        SET query_timeout := FALSE;
                    ELSE
                        SET status_out := CONCAT(status_out,'-success');
                        SET procedure_attempts := 0;
                    END IF;

                COMMIT;

                SET procedure_attempts := procedure_attempts - 1;
            END;
        UNTIL FALSE END REPEAT;
    -- loop
    SET status_out := CONCAT(status_out,'-end');
END;;
DELIMITER ;

运行时如下:

SET @@innodb_lock_wait_timeout:=1;
CALL `lock_test`(@out);
SELECT @out;

在大约10秒的运行时间后输出将是start-timeout-timeout-timeout-timeout-timeout-end(如果在没有将超时设置为1秒的情况下运行则会更长。

虽然在大多数项目中可能不太实际(或可取),但在从另一个查询中运行查询时调试超时问题时可能会有用 - 我希望它可能在将来帮助其他人。