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
だから私の質問は
私が選択しようとしているアプローチは機能し、最適化された方法ですか?削除によるサーバーでの問題(スレーブラグのような)は必要ないためです。
問題やダウンタイムなしでそれを行うための最良のアプローチは何でしょうか。
これを行うために pt-archiver をどのように使用できますか?.
考慮すべき点は次のとおりです。
DELETE FROM tbl ... LIMIT 5000
の使用は、レプリケーションで安全ではありません。これは、削除される5000行が、スレーブ上の同じ順序または同じ5000行のセットではない可能性があるためです。 MySQLドキュメント(レプリケーションとLIMIT) を参照してください。マスターのDELETEが中断された場合は、ロールバックする必要があります。その場合、DELETE FROM tbl ... LIMIT 5000
が呼び出される回数が異なる可能性があります。スレーブでのDELETE
の追加呼び出しは重要ではありません。呼び出しが少なすぎると、問題が生じる可能性があります。これは起こらないはずです。
[〜#〜] suggestion [〜#〜]:RTesAll
の行数がマスターとすべてのスレーブで同一であることを確認してください。
マスターでストアドプロシージャを実行しているため、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:マスターとスレーブで同時にストアドプロシージャを実行します。
autocommit
がマスターとスレーブで有効になっていることを確認します。そうしないと、すべての削除が単一のトランザクションまたは巨大なロールバックとして一度にすべての削除を実行する前に、ibdata1内の取り消しログにすべての削除が蓄積されます。