web-dev-qa-db-ja.com

MySQLマスターとスレーブの巨大なInnoDBテーブルからデータを削除する

MySQLデータベースにはいくつかの巨大なテーブルがあります。2013年に2012年より古いデータをすでにアーカイブ/削除しています。2013年より古いデータをアーカイブ/削除する必要があるということは、アプリケーションのダウンタイムなしで2012年のデータをアーカイブする必要があるということです。

Tables  Size in GB      "TABLE_ROWS"    "TABLE_ROWS BEFORE 2012"
RTesAll 923.65          1982098430       611992998
RTest   32.1            205527090   
RAdT    6.97            25324446    
RAdv    4.37            28260973    

そのため、biggetsテーブルから611992998レコードを削除する必要があります。

1つのMySQLマスターと4つのMySQLスレーブがあります。すべてのサーバーからデータを削除する必要があります。マスターもスレーブもあまり遅れないようにしました。そのため、ここでプロシージャを作成しました。このプロシージャは、まだテストしていません。

DROP PROCEDURE IF EXISTS PurgeOlderData;
DELIMITER $$
CREATE PROCEDURE `PurgeOlderData`(In StartDate DATETIME ,In EndDate DATETIME,In NoOfRecordsToDelete BIGINT,In TableName CHAR(50))
BEGIN
    SET @delete_counter             = 0;
    SET @table_name             = TableName;
    SET @number_of_records_to_delete    = NoOfRecordsToDelete;
    SET @start_date             = StartDate;
    SET @end_date               = EndDate;

    WHILE @delete_counter < @number_of_records_to_delete DO
        SET @varSQL = CONCAT('DELETE FROM ', @table_name,' WHERE recordDate BETWEEN \'',@start_date ,'\' AND \'', @end_date ,'\' LIMIT 5000;');
        PREPARE stmt FROM @varSQL;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        /*SELECT @varSQL;*/
        SET @delete_counter = @delete_counter + 5000;
    END WHILE;          
END $$
DELIMITER ;

プロシージャで変数@number_of_records_to_deleteを使用しました。削除する対象のレコードを渡すことができないためです。

呼び出しステートメントの例

CALL PurgeOlderData('2012-01-01 00:00:00','2012-01-05 00:00:00',100000,'RTestAll');

それに応じて@number_of_records_to_deleteの値を増やすことができます。

Why DELETE with LIMIT 5000 :Ok(平均1-3秒で実行され、スレーブもOK)、DELETE LIMITを10000に増やすことができます。これもOkの場合、さらに増やすことができます。

巨大なテーブルのテーブル構造

CREATE TABLE `RTesAll` (
  `recordDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `networkId` bigint(11) NOT NULL DEFAULT '0',
  `publisherId` bigint(11) NOT NULL DEFAULT '0',
  `feedId` bigint(11) NOT NULL DEFAULT '0',
  `subPublisherId` bigint(11) NOT NULL DEFAULT '0',
  `subId` varchar(100) NOT NULL DEFAULT '',
  `searches` bigint(20) DEFAULT NULL,
  `matches` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`recordDate`,`networkId`,`publisherId`,`feedId`,`subPublisherId`,`subId`),
  KEY `K_networkId` (`networkId`),
  KEY `K_publisherId` (`publisherId`),
  KEY `K_feedId` (`feedId`),
  KEY `K_subPublisherId` (`subPublisherId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

だから私の質問は

  1. 私が選択しようとしているアプローチは機能し、最適化された方法ですか?削除によるサーバーでの問題(スレーブラグのような)は必要ないためです。

  2. 問題やダウンタイムなしでそれを行うための最良のアプローチは何でしょうか。

  3. これを行うために pt-archiver をどのように使用できますか?.

3
Abdul Manaf

考慮すべき点は次のとおりです。

考慮事項#1

DELETE FROM tbl ... LIMIT 5000の使用は、レプリケーションで安全ではありません。これは、削除される5000行が、スレーブ上の同じ順序または同じ5000行のセットではない可能性があるためです。 MySQLドキュメント(レプリケーションとLIMIT) を参照してください。マスターのDELETEが中断された場合は、ロールバックする必要があります。その場合、DELETE FROM tbl ... LIMIT 5000が呼び出される回数が異なる可能性があります。スレーブでのDELETEの追加呼び出しは重要ではありません。呼び出しが少なすぎると、問題が生じる可能性があります。これは起こらないはずです。

[〜#〜] suggestion [〜#〜]RTesAllの行数がマスターとすべてのスレーブで同一であることを確認してください。

考慮事項#2

マスターでストアドプロシージャを実行しているため、DELETEコマンドは、マスターでDELETEが完了し、DELETEコマンドが完了するまで、スレーブで実行を開始しません。マスターのバイナリログに投稿されます。 1つのマスターと4つのスレーブ(5つのDBサーバー)では、DELETEへの特定の呼び出しに5を掛けたものがあります。

提案#1:ストアドプロシージャにset sql_log_bin = 0;を追加する必要があります

DROP PROCEDURE IF EXISTS PurgeOlderData;
DELIMITER $$
CREATE PROCEDURE `PurgeOlderData`(In StartDate DATETIME ,In EndDate DATETIME,In NoOfRecordsToDelete BIGINT,In TableName CHAR(50))
BEGIN
    SET sql_log_bin             = 0;
    SET @delete_counter             = 0;
    SET @table_name             = TableName;
    SET @number_of_records_to_delete    = NoOfRecordsToDelete;
    SET @start_date             = StartDate;
    SET @end_date               = EndDate;

    WHILE @delete_counter < @number_of_records_to_delete DO
        SET @varSQL = CONCAT('DELETE FROM ', @table_name,' WHERE recordDate BETWEEN \'',@start_date ,'\' AND \'', @end_date ,'\' LIMIT 5000;');
        PREPARE stmt FROM @varSQL;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
        /*SELECT @varSQL;*/
        SET @delete_counter = @delete_counter + 5000;
    END WHILE;          
    SET sql_log_bin             = 1;
END $$
DELIMITER ;

すべてのDELETEが複製されないようにします。 611992998行を削除する場合、5000行ごとに削除するにはDELETE ... LIMIT 5000 122399回実行する必要があります(つまり、611992998/5000は次の整数に切り上げられます)。

これにより、binlogに122399回書き込む必要がなくなります。

提案#2:マスターとスレーブで同時にストアドプロシージャを実行します。

  • これは複製の遅れを防ぎます
  • これにより、すべての行が同時に削除されます
  • これは、メンテナンスサイクルまたは読み取り時間が遅いときに行うのが最適です。

考慮事項#3

autocommitがマスターとスレーブで有効になっていることを確認します。そうしないと、すべての削除が単一のトランザクションまたは巨大なロールバックとして一度にすべての削除を実行する前に、ibdata1内の取り消しログにすべての削除が蓄積されます。

試してみる !!!

4
RolandoMySQLDBA