大きな2億2000万行のテーブル(9ギガデータ)でクエリを高速化する方法は?


31

問題:

互換性またはマッチングについて、メンバーがお互いに評価できるソーシャルサイトがあります。このuser_match_ratingsテーブルには、2億2000万を超える行(9ギガのデータまたはほぼ20ギガのインデックス)が含まれています。このテーブルに対するクエリは、slow.log(しきい値> 2秒)に定期的に表示され、システムで最も頻繁に記録される低速クエリです。

Query_time: 3  Lock_time: 0  Rows_sent: 3  Rows_examined: 1051
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 395357 group by rating;"

Query_time: 4  Lock_time: 0  Rows_sent: 3  Rows_examined: 1294
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 4182969 group by rating;"

Query_time: 3  Lock_time: 0  Rows_sent: 3  Rows_examined: 446
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 630148 group by rating;"

Query_time: 5  Lock_time: 0  Rows_sent: 3  Rows_examined: 3788
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1835698 group by rating;"

Query_time: 17  Lock_time: 0  Rows_sent: 3  Rows_examined: 4311
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1269322 group by rating;"

MySQLバージョン:

  • プロトコルバージョン:10
  • バージョン:5.0.77-log
  • バージョンbdb:Sleepycatソフトウェア:Berkeley DB 4.1.24:(2009年1月29日)
  • バージョンコンパイルマシン:x86_64 version_compile_os:redhat-linux-gnu

テーブル情報:

SHOW COLUMNS FROM user_match_ratings;

与える:

╔═══════════════╦════════════╦════╦═════╦════════╦════════════════╗
║ id            ║ int(11)    ║ NO ║ PRI ║ NULL   ║ auto_increment ║
║ rater_user_id ║ int(11)    ║ NO ║ MUL ║ NULL   ║                ║
║ rated_user_id ║ int(11)    ║ NO ║ MUL ║ NULL   ║                ║
║ rating        ║ varchar(1) ║ NO ║     ║ NULL   ║                ║
║ created_at    ║ datetime   ║ NO ║     ║ NULL   ║                ║
╚═══════════════╩════════════╩════╩═════╩════════╩════════════════╝

サンプルクエリ:

select * from mutual_match_ratings where id=221673540;

与える:

╔═══════════╦═══════════════╦═══════════════╦════════╦══════════════════════╗
║ id        ║ rater_user_id ║ rated_user_id ║ rating ║ created_at           ║
╠═══════════╬═══════════════╬═══════════════╬════════╬══════════════════════╣
║ 221673540 ║ 5699713       ║ 3890950       ║ N      ║ 2013-04-09 13:00:38  ║
╚═══════════╩═══════════════╩═══════════════╩════════╩══════════════════════╝

インデックス

テーブルには3つのインデックスが設定されています:

  1. 単一のインデックス rated_user_id
  2. 上の複合インデックスrater_user_idとcreated_at
  3. 上の複合インデックスrated_user_idとrater_user_id
user_match_ratingsのインデックスを表示。

与える:

╔════════════════════╦════════════╦═══════════════════════════╦══════════════╦═══════════════╦═══════════╦═════════════╦══════════╦════════╦═════════════════════════╦════════════╦══════════════════╗
║ Table              ║ Non_unique ║ Key_name                  ║ Seq_in_index ║ Column_name   ║ Collation ║ Cardinality ║ Sub_part ║ Packed ║ Null                    ║ Index_type ║ Comment          ║
╠════════════════════╬════════════╬═══════════════════════════╬══════════════╬═══════════════╬═══════════╬═════════════╬══════════╬════════╬═════════════════════════╬════════════╬══════════════════╣
║ user_match_ratings ║ 0          ║ PRIMARY                   ║ 1            ║ id            ║ A         ║ 220781193   ║ NULL     ║ NULL   ║ BTREE                   ║            ║                  ║
║ user_match_ratings ║ 1          ║ user_match_ratings_index1 ║ 1            ║ rater_user_id ║ A         ║ 11039059    ║ NULL     ║ NULL   ║ BTREE                   ║            ║                  ║
║ user_match_ratings ║ 1          ║ user_match_ratings_index1 ║ 2            ║ created_at    ║ A         ║ 220781193   ║ NULL     ║ NULL   ║ BTREE                   ║            ║                  ║
║ user_match_ratings ║ 1          ║ user_match_ratings_index2 ║ 1            ║ rated_user_id ║ A         ║ 4014203     ║ NULL     ║ NULL   ║ BTREE                   ║            ║                  ║
║ user_match_ratings ║ 1          ║ user_match_ratings_index2 ║ 2            ║ rater_user_id ║ A         ║ 220781193   ║ NULL     ║ NULL   ║ BTREE                   ║            ║                  ║
║ user_match_ratings ║ 1          ║ user_match_ratings_index3 ║ 1            ║ rated_user_id ║ A         ║ 2480687     ║ NULL     ║ NULL   ║ BTREE                   ║            ║                  ║
╚════════════════════╩════════════╩═══════════════════════════╩══════════════╩═══════════════╩═══════════╩═════════════╩══════════╩════════╩═════════════════════════╩════════════╩══════════════════╝

インデックスを使用しても、これらのクエリは遅くなります。

私の質問:

このデータをメモリに格納するのに十分なRAMを備えたサーバー上の別のデータベースにこのテーブル/データを分離すると、これらのクエリが高速化されますか?とにかく、これらのクエリを高速化するために改善できるテーブル/インデックスが設定されているものはありますか?

現在、16GBのメモリがあります。ただし、既存のマシンを32GBにアップグレードするか、少なくとも同じくらいの、おそらくはソリッドステートドライブを備えた新しいマシンを追加することを検討しています。


1
あなたの質問は信じられないほどです。現在のソリューションで、2秒以内に結果を得る方法に非常に興味がありますか?なぜなら、1つのテーブルには2000万件のレコードしかなく、まだ30秒かかりますSELECT QUERY。提案していただけますか?PSあなたの質問が私をこのコミュニティに参加させました(y);)
— NullPointer

2
クエリを実行しているテーブルのインデックスを見てください。多くの場合、適切なインデックスを作成することでクエリを大幅に改善できます。常にではありませんが、クエリのwhere句の列に対してインデックスを提供することでクエリが高速化される多くのインスタンスが見られます。特に、テーブルがますます大きくなる場合。
— -Ranknoodle

確かに@Ranknoodle。ありがとうございました。それぞれ確認します。
— にNullPointer

回答:


28

ランダムな順序でスローされた問題に関する考え:

  • このクエリの明白なインデックスは次のとおり(rated_user_id, rating)です。100万人のユーザーのうち1人だけのデータを取得し、17秒を必要とするクエリは、何か問題があります:(rated_user_id, rater_user_id)インデックスから読み取った後、テーブルから(数百から数千)rating列の値を読み取ります(ratingインデックスにはない)。そのため、クエリは多くの異なるディスクの場所にあるテーブルの多くの行を読み取る必要があります。

  • テーブルに多数のインデックスを追加する前に、データベース全体、低速クエリのセット全体のパフォーマンスを分析し、データ型の選択、使用するエンジン、構成設定を再度調べてください。

  • MySQLの新しいバージョン5.1、5.5、さらには5.6(PerconaおよびMariaDBバージョン)への移行を検討してください。バグとしてのいくつかの利点が修正され、オプティマイザーが改善され、低速クエリの低しきい値を1秒未満に設定できます(10ミリ秒など)。これにより、遅いクエリに関するより良い情報が得られます。

  • のデータ型の選択ratingは奇妙です。VARCHAR(1)?どうしてCHAR(1)?どうしてTINYINT?これにより、テーブルと、その列が含まれる(含まれる)インデックスの両方のスペースを節約できます。varchar(1)列にはchar(1)より1バイト多く必要であり、utf8の場合、(var)char列には1(tinyint)ではなく3(または4)バイトが必要です。


2
u間違ったデータ型を使用した場合、パフォーマンスインパクトまたはストレージの無駄の割合(%)
— FlyingAtom

1
@FlyingAtom場合によって異なりますが、スキャンが必要な一部のインデックス付き列(たとえば、where句がなく、その列を取得するだけの場合)では、エンジンは代わりにインデックスをスキャンすることを決定する場合がありますデータ型を半分のサイズに最適化すると、スキャンは2倍の速度になり、応答は半分のサイズになります。インデックスの代わりにテーブルをスキャンしている場合(たとえば、インデックス内の列だけでなく、より多くの列を取得する場合)、その利点はそれほど重要ではありません。
— セバスチャン・グリニョーリ

-1

私はドイツ政府のテーブルを時々6000万件のレコードで処理しました。

このテーブルはたくさんありました。

また、テーブルの合計行数を何度も知る必要がありました。

オラクルとマイクロソフトのプログラマーと話した後、私たちはそれほど幸せではありませんでした...

そこで、データベースプログラマのグループである私たちは、すべてのテーブルのレコードが常に1つであり、合計レコード番号が格納されているレコードであると判断しました。INSERT行またはDELETE行に応じて、この数値を更新しました。

他のすべての方法を試しました。これは断然最速の方法です。

1998年以降、この方法を使用しており、数百万件のレコードテーブルすべてにおいて、間違った行数はありませんでした。


7
過去18年間に導入された機能のいくつかを検討することをお勧めします。とりわけ、count(*)いくつかの改善があります。
— -dezso

あなたがそれらを数えることができなかった場合、あなたは間違った番号を持っていなかったことをどのように知っていますか?uhmmmm ...
— Tonca

-3

次のような評価タイプでパーティション分割を試みます。

mutual_match_ratings_N、mutual_match_ratings_Sなど

各タイプに対してクエリを実行する必要がありますが、おそらく他の方法よりも高速です。試してみる。

これは、評価タイプの数が固定されており、この新しい構造では最悪になる他のクエリにはこのテーブルが必要ないことを前提としています。

その場合は、他のアプローチを探すか、スペースと保守性(またはアプリケーションロジック)の観点から手頃な価格であれば、テーブルの2つのコピー(初期テーブルとパーティションコピー)を維持する必要があります。

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