纯粹出于学术原因,我想知道你是否可以为mysql存储过程添加一个处理程序,如果其中一个查询被锁定(例如SELECT ... FOR UPDATE
,它就能从锁等待超时错误中恢复或UPDATE
)查询。
这是假设一个innoDB数据库,设置为issolation level Repeatable read
,并定义了一个空的users
表。
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 ;
START TRANSACTION;
SELECT * FROM `users` FOR UPDATE;
users
的内容,但交易将保持打开状态。CALL `lock_test`(@out);
SELECT @out;
innodb_lock_wait_timeout
为50秒)是否可以在lock_test()
程序中添加处理程序,以便我们可以让@out保持'超时'?
答案 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秒的情况下运行则会更长。
虽然在大多数项目中可能不太实际(或可取),但在从另一个查询中运行查询时调试超时问题时可能会有用 - 我希望它可能在将来帮助其他人。