2

问题

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


这个例子

这是假设一个 innoDB 数据库,设置为隔离级别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_timeout50 秒)

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

4

1 回答 1

2

在花了一些时间阅读MySQL 处理程序文档之后,我能够得到我想要的东西:

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;

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

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

于 2013-09-05T21:44:26.117 回答