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

标签 mysql stored-procedures error-handling locking

问题

纯粹出于学术原因,我想知道您是否可以向 mysql 存储过程添加一个处理程序,如果其中一个查询锁定(例如 SELECT . .. FOR UPDATEUPDATE) 查询。


示例

假设有一个 innoDB 数据库,设置为隔离级别可重复读取,并定义了一个空的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;
  • 将显示用户的内容,但交易将保持开放状态。

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

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

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

最佳答案

花了一些时间阅读 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 秒,输出会更长。

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

关于MySQL - 检测和处理存储过程中的锁等待超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18646429/

相关文章:

mysql - 从按字段分组的表中选择并使用 Django ORM 选择最大日期时间

php - 如何从 MySQL 中的文本区域插入多行

mysql - attr_encrypted 和加密由 date_select 表单助手生成的日期

php - mysql(i)_real_escape_string,安全可靠吗?

mysql - 更新表中的 FromDate 和 ToDate 列

coldfusion - 将 CFTRY 标签包裹在所有内容周围

sql - MySQL foreach 替代过程

Azure SQL 数据库 : "No columns were selected" when calling a stored procedure using Dapper

error-handling - 设置方法无效,而获取方法正常

bash - 如果用户错误地调用脚本,使 Bash 脚本退出并打印错误消息