INNODB削除パフォーマンスを改善する方法は?
-
16-10-2019 - |
質問
したがって、この監査テーブルがあります(データベースの任意のテーブルのアクションを追跡します):
CREATE TABLE `track_table` (
`id` int(16) unsigned NOT NULL,
`userID` smallint(16) unsigned NOT NULL,
`tableName` varchar(255) NOT NULL DEFAULT '',
`tupleID` int(16) unsigned NOT NULL,
`date_insert` datetime NOT NULL,
`action` char(12) NOT NULL DEFAULT '',
`className` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `userID` (`userID`),
KEY `tableID` (`tableName`,`tupleID`,`date_insert`),
KEY `actionDate` (`action`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
そして、私は時代遅れのアイテムのアーカイブを開始する必要があります。テーブルは約5,000万行まで成長しているため、行を削除できる最速の方法は、一度にテーブルを削除することでした(に基づいてテーブルを削除することでした tableName
).
これは非常にうまく機能しますが、書き込みが多いテーブルの一部では、完了しません。私のクエリは、関連するすべてのアイテムを削除します delete
Tupleid/TableNameの組み合わせに対するアクション:
DELETE FROM track_table WHERE tableName='someTable' AND tupleID IN (
SELECT DISTINCT tupleID FROM track_table
WHERE tableName='someTable' AND action='DELETE' AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
)
これをサーバーで3日間実行させましたが、最大のテーブルで完了することはありませんでした。説明出力(deleteを選択すると選択した場合:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
| 1 | PRIMARY | track_table | ref | tableID | tableID | 257 | const | 3941832 | Using where |
| 2 | DEPENDENT SUBQUERY | track_table | ref | tableID,actionDate | tableID | 261 | const,func | 1 | Using where; Using temporary |
したがって、削除するのに3日かかるべきではないと思います。 Innodb_buffer_pool_sizeが3GBに設定されていると、サーバーがOne_file_per_tableを使用するように設定されていません。 InnoDB削除パフォーマンスを改善する他のどのような方法はありますか? (Mac OSXでMySQL 5.1.43を実行しています)
解決
バッチ内のデータを削除できます。
SQL Serverでは、構文はです delete top X
テーブルからの行。次に、各バッチのトランザクション(もちろん複数のステートメントがある場合)でループでそれを行い、トランザクションを短くしてロックを短時間のみ維持します。
mysql構文: DELETE FROM userTable LIMIT 1000
それには制限があります(使用できません LIMIT
たとえば、結合付きの削除で)しかし、この場合、そのようにできるかもしれません。
使用には追加の危険があります LIMIT
と DELETE
複製に関しては;削除された行は、マスター上で削除されたため、スレーブで同じ順序で削除されない場合があります。
他のヒント
温度テーブルアプローチを使用してみてください。このようなことを試してみてください:
ステップ1) CREATE TABLE track_table_new LIKE track_table;
ステップ2) INSERT INTO track_table_new SELECT * FROM track_table WHERE action='DELETE' AND date_insert >= DATE_SUB(CURDATE(), INTERVAL 30 day);
ステップ3) ALTER TABLE track_table RENAME track_table_old;
ステップ4) ALTER TABLE track_table_new RENAME track_table;
ステップ5) DROP TABLE track_table_old;
ステップ2にタプルフィールドを含めませんでした。これが望ましい効果を生成するかどうかを確認してください。これがあなたが望むものである場合、他の理由でタプルフィールドを使用しない限り、タプルフィールドを完全に捨てることをお勧めします。
バッチ内の不要な行の削除は、他の操作を実行可能に保つ必要があります。ただし、操作の削除には条件があるため、条件にわたって列に適切なインデックスがあることを確認してください。
MySQLがルーズインデックススキャンの完全な機能をサポートしていないため、シーケンスを調整しようとすることができます KEY actionDate (action, date_insert)
に KEY actionDate (date_insert, action)
. 。 「date_insert」の接頭辞を使用すると、mysqlはこのインデックスを使用して、日時条件の前にある行をスキャンする必要があります。
このようなインデックスを使用すると、SQLを次のように記述できます。
DELETE
FROM track_table
WHERE tableName='someTable'
AND action='DELETE'
AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
LIMIT 1000 -- Your size of batch
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
| 1 | PRIMARY | track_table | ref | tableID | tableID | 257 | const | 3941832 | Using where |
| 2 | DEPENDENT SUBQUERY | track_table | ref | tableID,actionDate | tableID | 261 | const,func | 1 | Using where; Using temporary |
-fist、key_lenを説明することから、big =>できるだけ小さいサイズをダウングレードする必要があります。クエリの場合、最良の方法は、CHAR(12)からTinyIntへのアクションフィールドのデータ型を変更することだと思います。そのため、データマッピングは次のようになります。
1: -> DELETE
2: -> UPDATE
3: -> INSERT
...
代わりにTableNameを変更することもできます。最高のパフォーマンスのためのDDLは次のとおりです。
CREATE TABLE `track_table` (
`id` int(11) unsigned NOT NULL,
`userID` smallint(6) unsigned NOT NULL,
`tableid` smallint(6) UNSIGNED NOT NULL DEFAULT 0,
`tupleID` int(11) unsigned NOT NULL,
`date_insert` datetime NOT NULL,
`actionid` tinyin(4) UNSIGNED NOT NULL DEFAULT 0,
`className` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `userID` (`userID`),
KEY `tableID` (`tableid`,`tupleID`,`date_insert`),
KEY `actionDate` (`actionid`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `actions` (
`id` tinyint(4) unsigned NOT NULL
`actionname` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `table_name` (
`id` tinyint(4) unsigned NOT NULL
`tablename` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
したがって、クエリは次のように見えます。
DELETE FROM track_table WHERE tableid=@tblid AND tupleID IN (
SELECT DISTINCT tupleID FROM track_table
WHERE tableid=@tblid AND actionid=@actionid AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
).
しかし、最速の方法はパーティションを使用することでした。したがって、パーティションをドロップできます。現在、私のテーブルには約40マイル以上の列があります。 1時間ごと(毎回400k行の更新)を更新すると、Curr_Dateパーティションをドロップしてテーブルにデータをリロードできます。ドロップコマンドは非常に速く(<100ms)。この助けを願っています。