web-dev-qa-db-ja.com

MYSQL:1億を超えるインデックス付きの行があるテーブルに100万を超える行を挿入するパフォーマンスを向上させる方法

私はこのmysqlテーブルを持っています:

CREATE TABLE `codes` (
  `code` bigint(11) unsigned NOT NULL,
  `allocation` int(11) NOT NULL DEFAULT '0',
  `used` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`code`),
  KEY `allocation` (`allocation`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

完全に稼働すると、1兆から10兆の数値からランダムに生成された1億から3億のコードが保持されます。

テーブルを埋めるために、私はこのストアドプロシージャを持っています。

DELIMITER ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `generate_codes_v4`(
    IN bf_codes_to_generate BIGINT,
    IN bf_lower_limit BIGINT,
    IN bf_upper_limit BIGINT,
    IN bf_allocation_num INT
)
BEGIN

    SET @Codes = bf_codes_to_generate;
    SET @Lower = bf_lower_limit;
    SET @Upper = bf_upper_limit;
    SET @Allocation = bf_allocation_num;

    SET @qry_Rand = 'SELECT ROUND(((@Upper - @Lower -1) * Rand() + @Lower), 0) INTO @Random';
    PREPARE qry_Rand_stmt FROM @qry_Rand;

    SET @qry_insert = 'INSERT IGNORE INTO `codes` (`code`,`allocation`) VALUES ( @Random, @Allocation )';
    PREPARE qry_insert_stmt FROM @qry_insert;

    START TRANSACTION;

    WHILE @Codes > 0 DO

        EXECUTE qry_Rand_stmt;
        EXECUTE qry_insert_stmt;

        SET @Codes = @Codes - ROW_COUNT();

    END WHILE;

    COMMIT;

    DEALLOCATE PREPARE qry_Rand_stmt;
    DEALLOCATE PREPARE qry_insert_stmt;

END;;
DELIMITER ;

これは、指定された境界の間の乱数を選び、それをテーブルに挿入することです。

現在、このストアドプロシージャを使用して、一度に500K〜5M行のどこかに挿入します。動作している間、テーブルに行が既に存在するため、非常に遅くなります。

テーブルに既に1000万行あると、生成プロセスは毎秒約1000行に遅くなります。最終的にこのテーブルに100Mから300Mのコードを格納することを計画しているため、その時点で挿入プロセスはさらに長くかかります。基本的に、このテーブルは適切にスケーリングされていません。

このプロセスのスケールを改善するためにできることはありますか?

あなたが尋ねるかもしれないと思う質問へのいくつかの答えはここにあります

Q:なぜ割り当て列にインデックスがあるのですか? A:行のバッチが挿入されるたびに、割り当て番号を割り当てます。特定の割り当て番号を持つ許可行をすばやく取得できる必要があります。

Q:なぜトランザクションを使用するのですか? A:どうやらこれにより、コードの挿入中にインデックスが常にディスクにフラッシュされることがなくなり、テストでは挿入が大幅に高速化されました。また、まだ実装されていませんが、いつでもバッチ挿入をキャンセルできるkillスイッチを配置できるようにしたいと考えています。

Q:テーブルを複数のテーブルに分割しないでください。たとえば、1-1Tはテーブル1に、1T-2Tはテーブル2にというようにしてください。 A:調査する必要があるかもしれません。これですが、今あるものが改善できるかどうかを確認したいと思います。

Q:他に知っておくべきことはありますか? A:このテーブルは、コードが存在するかどうか、およびコードが使用されていてSELECTが重いかどうかを確認するためのルックアップとして常に使用されます。どのようなソリューションでも、このテーブルの読み取りをブロックしてはならず、読み取りパフォーマンスをあまり低下させないでください。

2
Brady

サーバーファイルシステムにアクセスできる場合は、数値生成をフラットファイルにスクリプト(Perl、PHP、C++など)で記述し、LOAD DATA INFILE操作を実行することをお勧めします。

通常、LOAD DATA INFILEは、大きな行セットに対してINSERTステートメントを繰り返すよりも高速に実行され、IGNORE句も処理できます。 bulk_insert_buffer_size 変数について この答え を見てください。これは、LOAD DATA INFILEオプションを使用することを選択した場合に、一括挿入を行うときに重要です。

1
Francois

ランダムな順序でレコードを挿入しても、最適な書き込みパフォーマンスは得られません。

昇順で挿入または生成する前に、主キーでセットのソートをテストできます(たとえば、変数に最後の値を格納し、乱数でそれを増やします)。

1
Oleg Komarov