web-dev-qa-db-ja.com

MySQL MEMORYテーブルでのDELETEクエリの最適化

私は、バックエンドデータストレージ用のMySQLデータベースを管理する大きなフォーラムを運営しています。 「セッション」テーブルは、ログインしているユーザーとゲストを追跡します。現在は約10万件のレコードなので、それほど大きくはありません。ただし、古いレコードをトリミングすると、このセッションテーブルがスロークエリログに表示されます。

# Time: 120719 10:05:11
# User@Host: xxx[xxx] @  [10.x.x.x]
# Thread_id: 369051896  Schema: forumdb  Last_errno: 0  Killed: 0
# Query_time: 8.352811  Lock_time: 0.000028  Rows_sent: 0  Rows_examined: 19635  Rows_affected: 19635  Rows_read: 0
# Bytes_sent: 13  Tmp_tables: 0  Tmp_disk_tables: 0  Tmp_table_sizes: 0
SET timestamp=1342710311;
DELETE FROM session
    WHERE lastactivity < 1342709401;

最後のアクティビティテーブルにBTREEインデックスがあることを確認しました。

mysql> SHOW INDEX FROM session FROM forumdb;
+---------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name     | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| session |          0 | PRIMARY      |            1 | sessionhash  | NULL      |       78941 |     NULL | NULL   |      | HASH       |         |               |
| session |          1 | userid       |            1 | userid       | NULL      |       26313 |     NULL | NULL   |      | HASH       |         |               |
| session |          1 | idhash       |            1 | idhash       | NULL      |        8771 |     NULL | NULL   |      | HASH       |         |               |
| session |          1 | userid_2     |            1 | userid       | NULL      |        NULL |     NULL | NULL   |      | HASH       |         |               |
| session |          1 | userid_2     |            2 | lastactivity | NULL      |       39470 |     NULL | NULL   |      | HASH       |         |               |
| session |          1 | userid_3     |            1 | userid       | NULL      |        NULL |     NULL | NULL   |      | HASH       |         |               |
| session |          1 | userid_3     |            2 | Host         | NULL      |       39470 |     NULL | NULL   |      | HASH       |         |               |
| session |          1 | lastactivity |            1 | lastactivity | A         |        NULL |     NULL | NULL   |      | BTREE      |         |               |
+---------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
8 rows in set (0.00 sec)

レコードが少ないため、削除クエリに時間がかかるのはなぜですか。

このテーブルは、同時に多くのユーザーが同時にオンラインで非常に頻繁に使用されていることに注意してください。その後ろに重いハードウェアがいくつかあります。

このプロセスをスピードアップするために何ができるかについてのアイデアはありますか?この削除機能が実行されているときに、クラスター全体に高い負荷がかかるようにテーブルをロックしているようです。

クエリキャッシュがオフになっています(memcacheを使用しています)。 my.cnfの関連部分は次のとおりです。

table_open_cache=8242
table_definition_cache=600
open_files_limit=65535
binlog_cache_size=6M
sort_buffer_size=8M
key_buffer_size=5G
myisam_sort_buffer_size=256M
join_buffer_size=3M
thread_cache_size=1000
thread_concurrency=16
ft_min_Word_len=3
tmp_table_size=512M
max_allowed_packet=128M
max_heap_table_size=512M
read_rnd_buffer_size=1M
skip-external-locking

query_cache_limit=1M
query_cache_size= 32M
query_cache_type = 0

最後に、テーブルの詳細を次に示します。

Table info

修繕:

テーブルをInnoDBに変更した後(下のチャートの17:15)、このテーブルのスローログでパフォーマンスが大幅に向上し、DELETEがなくなったことがわかります。この変更により、クラスター全体のパフォーマンスが向上しています。ありがとうございました。

CPU Results

4
Dave Drager

おそらくメモリテーブルをInnoDBに変換します

ALTER TABLE session ENGINE=InnoDB

あなたの状況のた​​めに。メモリテーブルは、書き込みが少ない場合や読み取りが多い場合に有効です。データを失っても気にしない(たとえば、構成値の読み込み)。しかし、多くの書き込みを開始すると、それにアクセスする多くの接続がある場合、テーブルロックによって強制終了されます。

MEMORYテーブルのインメモリ処理にもかかわらず、ビジーサーバー、汎用クエリ、または読み取り/書き込みワークロードでのInnoDBテーブルよりも高速であるとは限りません。特に、更新の実行に伴うテーブルのロックにより、複数のセッションからのMEMORYテーブルの同時使用が遅くなる可能性があります。 [src]

データ損失を気にしないため、セッションデータの追跡は良い候補のように思えるかもしれませんが、この場合はInnoDBに切り替えることでテーブルロックを削除することで大きなメリットが得られると思います。

4
Derek Downey

メモリテーブルはヒープメモリの割り当てを必要とし、削除された行にそのメモリを再利用します。

"削除された行はリンクリストに入れられ、テーブルに新しいデータを挿入すると再利用されます。"- http:// dev .mysql.com/doc/refman/5.5/en/memory-storage-engine.html

私の推測では、1万9,000行の場合、削除された行が渡される裏で、リンクされたリストのデータ構造の管理に8秒かかります。

Freshman comp sciを覚えている場合、リストを挿入するには、最後の要素の次のポインタを更新して新しい要素を指すようにし、リストの末尾のポインタを新しい要素に更新する必要があります。また、リストの数も更新する必要があります。 19k xそれらにはいくつかのサイクルがかかり、さらにデータを削除してメモリを交換することを想像できます。

19行を削除すると、1000倍高速になりますか?これは、(O)n線形コストとして説明できます。

3
randomx

多分あなたは一時テーブルのベイトアンドスイッチを行うことができます

ALTER TABLE session RENAME sessionold;
CREATE TABLE sessionnew LIKE sessionold;
INSERT INTO sessionnew SELECT * FROM sessionold WHERE lastactivity >= 1342709401;
ALTER TABLE sessionnew RENAME session;
DROP TABLE sessionold;

これはより高速なDELETEです。

  • これにより、sessionテーブルが全員から削除され、このDELETEが実行されます。 MEMORYテーブルはMyISAMのようなフルテーブルロックを使用するため、頻繁に使用されるMEMORYテーブルは、ディスクI/Oが一定であるため、DMLを実行するのが面倒です(MEMORYテーブルの各アクセスは常に.frm)。
  • ユーザーから分離されたsessionテーブルを使用して、lastactivity> = 1342709401のすべてをコピーし、それに対する任意のI/Oを実行できます。
  • 保持するレコードがある場合のみ、テーブルが再び使用可能になります

試してみる !!!

PDATE 2012-07-19 12:38 EDT

これはより良い代替案です:

CREATE TABLE sessionnew LIKE session;
ALTER TABLE session RENAME sessionold;
ALTER TABLE sessionnew RENAME session;
INSERT INTO session SELECT * FROM sessionold WHERE lastactivity >= 1342709401;
DROP TABLE sessionold;

最初の3行がsessionテーブルをすぐに空にするため、これはさらに高速に動作します。次に、古いsessionテーブルのlastactivity> = 1342709401の行はすべて持ち込まれます。次に、古いsessionテーブルが削除されます。

2
RolandoMySQLDBA