2

質問

SELECT ... FOR UPDATE純粋に学問上の理由から、クエリの 1 つ (または など)がロックされた場合にロック待機タイムアウト エラーから回復できる mysql ストアド プロシージャにハンドラーを追加できるかどうか疑問に思っていUPDATEます。


Repeatable readこれは、空のusersテーブルが定義された、分離レベルに設定された innoDB データベースを想定しています。

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 に「タイムアウト」を保持させることはできますか?

4

1 に答える 1

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;

出力はstart-timeout-timeout-timeout-timeout-timeout-end、実行時間の約 10 秒後に表示されます (タイムアウトを 1 秒に設定せずに実行すると、さらに長くなります。

ほとんどのプロジェクトではおそらく実用的 (または推奨) ではありませんが、別のクエリ内からクエリを実行するときにタイムアウトの問題をデバッグするときに役立つ可能性があります。将来的に他の人に役立つことを願っています.

于 2013-09-05T21:44:26.117 に答える