InnoDB DELETEのパフォーマンスを向上させる方法は?


9

だから私はこの監査テーブルを持っています(私のデータベース内の任意のテーブルでのアクションを追跡します):

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万行に増えたので、行を削除する最も速い方法は、一度に1つのテーブルを削除することでした(に基づく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日間実行しましたが、最大のテーブルでは完了しませんでした。説明出力(削除を選択に切り替えた場合:

| 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 |

したがって、400万行を削除するのに3日はかかりません。私のinnodb_buffer_pool_sizeを3GBに設定していますが、サーバーがone_file_per_tableを使用するように設定されていません。InnoDBの削除パフォーマンスを向上させるには、他にどのような方法がありますか?(Mac OSXでMySQL 5.1.43を実行)

回答:


11

データはバッチで削除できます。

SQL Serverでは、構文はdelete top Xテーブルの行です。次に、ループ内で、バッチごとにトランザクションを使用して(もちろん、複数のステートメントがある場合)、トランザクションを短く維持し、ロックを短時間だけ維持するようにします。

MySQL構文では: DELETE FROM userTable LIMIT 1000

これには制限があります(LIMITたとえば、結合を使用した削除では使用できません)。この場合、その方法で実行できる場合があります。

レプリケーションに関しては、LIMITと一緒DELETEに使用するとさらに危険があります。削除された行は、マスターで削除されたのと同じ順序でスレーブで削除されない場合があります。


6

一時テーブルを使用してみてください。このようなものを試してください:

ステップ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ではタプルフィールドを含めませんでした。これが望ましい効果をもたらすかどうかを確認してください。これが必要な場合は、他の理由でタプルフィールドを使用しない限り、タプルフィールドをすべて破棄することができます。


それは興味深い解決策です。テーブルにタプルフィールドが必要です。tableName / tupleIDは、ログに記録されているテーブルの未定義の外部キーです。最近まで、このテーブルはMyISAMであり、外部キーをサポートしていないため、未定義です。
Derek Downey

1

不要な行をバッチで削除すると、他の操作が実行可能になります。ただし、操作の削除には条件があります。そのため、条件に対して列に適切なインデックスがあることを確認してください。

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

1
| 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 |

-最初に、key_len so big =>の説明から、サイズを可能な限り小さくする必要があります。あなたのクエリでは、アクションフィールドのデータ型をchar(12)からtinyintに変更するのが最善の方法だと思うので、データマッピングは次のようになります。

1: -> DELETE
2: -> UPDATE
3: -> INSERT
...

また、tablenameの代わりにtable_idを変更することもできます。最高のパフォーマンスを得るための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)
).

しかし、最も速い方法はパーティションを使用することでした。パーティションを削除できます。現在、私のテーブルには約40mil以上の行があります。1時間ごとに更新(毎回40万行が更新されます)。curr_dateパーティションを削除して、データをテーブルに再読み込みできます。ドロップコマンドは非常に高速(100ミリ秒未満)。この助けを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.