问题
纯粹出于学术原因,我想知道您是否可以向 mysql 存储过程添加一个处理程序,如果其中一个查询锁定(例如 SELECT . .. FOR UPDATE
或 UPDATE
) 查询。
示例
假设有一个 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/