web-dev-qa-db-ja.com

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

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

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に基づく)。

これはかなりうまく機能しますが、書き込みが多いテーブルの一部では完了しません。私のクエリは、tupleID/tableNameの組み合わせでdeleteアクションが関連付けられているすべてのアイテムを削除します。

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を実行)

9
Derek Downey

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

SQL Serverでは、構文はdelete top Xテーブルからの行。次に、それをループ内で実行します(もちろん、複数のステートメントがある場合)バッチごとにトランザクションを実行します。これにより、トランザクションを短く保ち、短期間のロックのみを維持します。

MySQL構文では:DELETE FROM userTable LIMIT 1000

これには制限があります(たとえば、ジョイン付きの削除でLIMITを使用することはできません)。この場合、その方法で実行できる可能性があります。

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

11
Marian

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

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

6
RolandoMySQLDBA

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

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
Mike Lue
| 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ミリ秒未満)。この助けを願っています。

1
Thanh Nguyen