web-dev-qa-db-ja.com

MySQL:トランザクションのあるデータをトランザクションのないデータよりも高速に挿入する理由

ストアドプロシージャを使用して、指定した範囲のプレフィックスと番号で構成されるシーケンスを生成しています(シーケンス "AB100"、 "AB101"、...、 "AB199"、 "AB200"など)

ストアドプロシージャにはREPEATループが含まれ、文字列が作成されてテーブルに挿入されます。

すべてのループがトランザクション内にある場合、プロシージャは非常に高速に実行されますが、トランザクションなしでデータを挿入すると、プロシージャの実行に400倍の時間がかかります。

誰かが説明できますか、なぜそうなのですか?

あるいは、このようなシーケンスを生成する方法について誰かより良いアイデアがあれば、私はそれらを聞いて幸せになります。

これがコードです:

-- create table
CREATE TABLE seq
(
    Code CHAR(12)
) ENGINE = INNODB;

-- fast procedure with transactions
DELIMITER $$
CREATE PROCEDURE sp_sequence(IN val_prefix CHAR(2), IN val_from INT, IN val_to INT )
BEGIN
    START TRANSACTION;
    SET @val = val_from;
    REPEAT
        INSERT INTO seq (Code) VALUES (CONCAT(val_prefix, @val));
        SET @val = @val + 1;
    UNTIL @val > val_to END REPEAT;
    COMMIT;
END$$
DELIMITER ;

-- slow procedure without transactions
DROP PROCEDURE IF EXISTS sp_sequence_slow;
DELIMITER $$
CREATE PROCEDURE sp_sequence_slow(IN val_prefix CHAR(2), IN val_from INT, IN val_to INT )
BEGIN
    SET @val = val_from;
    REPEAT
        INSERT INTO seq (Code) VALUES (CONCAT(val_prefix, @val));
        SET @val = @val + 1;
    UNTIL @val > val_to END REPEAT;
END$$
DELIMITER ;

そしてここに結果があります:

mysql> CALL sp_sequence('TX', 11000, 12000);
Query OK, 0 rows affected (0.09 sec)

mysql> CALL sp_sequence_slow('TX', 11000, 12000);
Query OK, 0 rows affected (40.07 sec)
4
Zbynek

明示的なトランザクションがない場合、すべてのステートメントは自動コミットモードになるため、各挿入は個別のトランザクションになります。これには同期などのオーバーヘッドが伴います。

間にコミットせずにすべての行を挿入すると、mysqlはその多くをメモリ/キャッシュで実行し、ディスクに1回だけ同期できます(単純化すると、キャッシュはファイルシステムとOSによって複数回フラッシュされる可能性があります)。

明示的なトランザクションがない場合、各挿入はディスクがすべての変更の書き込みを確認してからループに戻る前に待機する必要があります。これは、各トランザクションの後で定期的にフラッシュすることで少し緩和できる場合がありますが、IIRCの失敗時に一部のデータが失われる可能性があります。

反対に、1つのトランザクションで挿入または更新する行が多すぎる場合、ロールバックの可能性に関する情報を含むログが大幅に増大し、すべてが遅くなる可能性があります。したがって、トランザクションサイズは妥当なものでなければなりません(数百万行ではありません)。

いくつかのヒント: http://dev.mysql.com/doc/refman/5.5/en/optimizing-innodb-transaction-management.html

7
jkavalik