MySQLインデックス作成VarChar


10

blogentriesデータベースのインデックスを作成してパフォーマンスを向上させようとしていますが、問題が見つかりました。

ここに構造があります:

CREATE TABLE IF NOT EXISTS `blogentries` (
  `id_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `title_id` varchar(100) COLLATE latin1_german2_ci NOT NULL,
  `entry_id` varchar(5000) COLLATE latin1_german2_ci NOT NULL,
  `date_id` int(11) NOT NULL,
  PRIMARY KEY (`id_id`)
)
ENGINE=MyISAM
DEFAULT CHARSET=latin1
COLLATE=latin1_german2_ci
AUTO_INCREMENT=271;

次のようなクエリは、インデックスを適切に使用します。

EXPLAIN SELECT id_id,title_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| id | select_type | テーブル| タイプ| 可能性のあるキー| キー| key_len | ref | 行| エクストラ|
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| 1 | シンプル| blogentries | インデックス| NULL | プライマリー 114 | NULL | 126 | インデックスの使用|
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +

ただし、クエリにを追加すると、filesortが使用entry_idされSELECTます

EXPLAIN SELECT id_id,title_id,entry_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| id | select_type | テーブル| タイプ| 可能性のあるキー| キー| key_len | ref | 行| エクストラ|
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| 1 | シンプル| blogentries | すべて| NULL | NULL | NULL | NULL | 126 | filesortの使用|
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +

なぜこれが起こっているのか、どうすれば回避できるのかと思っていました。それはによるVarCharものですか、それは別のものに変更する必要がありますか?

私は高いに実行しているように、すべての私のクエリがインデックスを使用していしようとしていますHandler_read_rndし、Handler_read_rnd_next値。

他の情報が必要な場合は、私もそれを投稿できます。


filesortは、ディスク上でソートを実行していることを意味します。
Kermit

WHERE 1=12番目のクエリに追加してみてください。
カーミット

これはMySQLのどのバージョンですか?ソートバッファーサイズ(SELECT @@sort_buffer_size)は?

@njk filesortは、クエリの「ORDER BY」部分の結果です

1
@TashPemhiwa必ずしもそうではありません。最初のステートメントを参照してください。
カーミット、2012年

回答:


6

WHEREどちらのクエリにも句がないため、両方のケースですべての行が返されるため、これらの例では、インデックスの使用または非使用によるパフォーマンスへの影響はほとんどないと思います。


確かにMySQLはORDER BY
eggyal

@eggyalメモリに対して大きすぎる場合はそうではありません。
カーミット

@njk:意味がありません...全体をメモリにロードする必要なく、インデックスを順番にトラバースできます。filesortを実行せずに結果がソートされます。
eggyal

@eggyalのサイズを質問しますvarchar(5000)
Kermit、

@njk:しかし、その列はインデックスにもソートにも使用されていません。
eggyal

2

ORDER BY最適化の下に文書化されているように

filesort使用されていない遅いクエリの場合は、max_length_for_sort_dataをトリガーするのに適切な値に下げてみてくださいfilesort

Peter Zaitsev 彼のブログ記事「正確にはread_rnd_buffer_sizeは何か」で次のように説明しています。

私にとってこれはMySQL 4.1以降、このオプションは狭い範囲で使用されます–取得するフィールドが少ない場合(max_length_for_sort_data未満)、データをソートバッファーとソートファイルに保存する必要があるため、選択した列の場合、read_rnd_bufferは不要です。長いため、max_length_for_sort_dataよりも長いため、それらの間にいくつかのTEXT / BLOB列があることを意味します。ただし、多数の列がある場合、または長いVARCHAR列が使用されている場合に使用されます。静的プレゼンテーションでmax_length_for_sort_dataよりも長い行を作成するには、UTF8 VARCHAR(255)のカップルしかかかりません。

これmax_length_for_sort_dataは、が選択している列の合計サイズの制限であり、それを超えるとfilesort、インデックスベースのソートの代わりにが使用されることを示唆しています。

あなたのケースでは、entry_id(5002バイト)を選択すると、この変数のデフォルト値である1KiBを超える合計サイズfilesortが使用されるため、このサイズが使用されます。制限を8KiBに上げるには、次のようにします。

SET SESSION max_length_for_sort_data = 8192;

私はこれと非常によく似た設定のテーブルを持っていますが、この設定はfilesortの使用の変更を引き起こさないようです。

@muffinista:面白いですね。@RolandoMySQLDBAの答えによれば、それは他のいくつかのバッファ設定に関連していると思いますか?
egyal 2012年

2

あなたはここで多くの興味深い回答を得ましたが、誰も質問に正確に答えていません-なぜこれが起こっているのですか?私が理解しているように、SELECTクエリにMySQLの可変長データが含まれ、リクエストされたすべての列に一致するインデックスがない場合、常にファイルソートが使用されます。ここでは、データのサイズはそれほど重要ではありません。MySQLのドキュメントでこの質問に対する直接的な回答を見つけるのは困難ですが、誰かがあなたと非常によく似た問題を経験している良いブログ投稿があります。

参照:MySQLクエリを最適化するための10のヒント(それは悪くない)

したがって、entry_idにインデックスを付けることができる場合は、それを追加してすべてを設定できます。しかし、私はそれがオプションであることを疑っています、それで何をすべきですか?

これについて何をすべきかは別の質問です。MySQLでは 'filesort'の名前が適切ではないことを知っておくことが重要です。これ、実際にはこの特定のクエリの並べ替えに使用されるアルゴリズムの名前にすぎません。多くの場合、並べ替えは実​​際にはメモリ内で行われます。このテーブルが大きくなると予想しない場合は、おそらく大したことではありません。

一方、このテーブルに100万行ある場合は、問題がある可能性があります。このテーブルでクエリのページ分割をサポートする必要がある場合は、ここで非常に深刻なパフォーマンスの問題が発生する可能性があります。その場合、可変長データを新しいテーブルに分割し、JOINを実行してそれを取得することは、検討すべき有効な最適化です。

この質問について話すSOに関する他のいくつかの回答を次に示します。


OPの最初のクエリには「MySQLの可変長データが含まれ、リクエストされたすべての列に一致するインデックスはありません」が、filesortその場合は明らかに使用されていません。また、メモリ内の小さなテーブルを並べ替えるだけでも、許容できないパフォーマンスヒットになる可能性があると思います。たとえば、クエリが頻繁に実行される場合(そしてキャッシュが使用できないようにテーブルが変更される場合)です。
eggyal

テストする時間はありませんが、dev.mysql.com / doc / refman / 5.1 / en / charで指定されている長さを格納するために2バイトを必要とするVARCHARがあることによってトリガーされるかどうか疑問に思っています。 html-最初のクエリはその制限内に収まりますが、2番目のクエリはそうではありません。

0

WHEREクエリに句を追加してみてください。

インデックスがあっても使用することができますORDER BYがある限り、インデックスの未使用部分のすべてとすべての余分なように、正確にインデックスが一致しないORDER BYの列が中に定数であるWHERE句。MySQLは、インデックスを使用してORDER BYを解決できない場合がありますが、インデックスを使用してWHERE句に一致する行を検索します。

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html


しかし、このケースではORDER BY ありません正確にインデックスを一致させるので、持っている必要はありませんWHERE句は。
eggyal

サイトの実際のクエリに "where"句があるので、それがファイルの並べ替えの原因ではないことがわかります。それがvarcharの使用かどうか疑問に思っていますか?

0

私の知る限り、varcharは最大で8000バイトしか保持できません。これは約4000文字です。したがって、5000はストレージの制限を超えているように見えます。この場合、おそらく並べ替えがめちゃくちゃになる理由です。

"varchar [(n | max)]可変長の非Unicode文字データ。nの値は1〜8,000です。maxは、最大ストレージサイズが2 ^ 31-1バイトであることを示します。ストレージサイズは実際のサイズです入力されたデータの長さ+ 2バイト。入力されたデータの長さは0文字にすることができます。varcharのSQL-2003シノニムは、char可変またはchar可変です。

これがあなたの質問に答えることを願っています


下の文書化したようタイプ:「VARCHAR列の値は可変長文字列です長さは5.0.3以降のバージョンで65,535のMySQL 5.0.3前に、0から255までの値として指定され、0することができます効果的。MySQL 5.0.3以降でのaの最大長は、最大行サイズ(すべての列で共有される65,535バイト)と使用される文字セットのCHARVARCHARVARCHAR
影響を受け

0

テーブルには126行しかありません。すべての行のサイズが最大約5KBであっても、ディスクから読み取る合計サイズは約600KBに過ぎないことを意味します-これは全体ではありません。正直なところ、それは非常に少量であり、おそらく最新のほとんどのディスクドライブのキャッシュサイズよりも小さいでしょう。

サーバーがクエリを実行するためにデータを取得する必要がある場合、最もコストのかかる操作はディスクからデータを読み取ることです。ただし、特にデータの量が非常に少ない場合は、インデックスの順序に従って読み取るのが常に最も速い方法であるとは限りません。

あなたの場合、ディスクからテーブルデータ全体を単一のブロックとしてメモリに(おそらく1回のディスク読み取り操作またはシークで)読み取り、それをRAMで並べ替えて、ディスクと比較して瞬時であるORDER BYを満たす方がはるかに効率的です。読み取り操作。サーバーがインデックスに従ってデータを読み取る場合、最大126(oops!)の読み取り操作を発行し、同じデータファイル内を何度も検索する必要があります。

言い換えると、逐次スキャンは必ずしも悪いことではなく、mysqlが必ずしも愚かであるとは限りません。mysqlに強制的にそのインデックスを使用させようとすると、現在のシーケンシャルスキャンよりも動作が遅くなる可能性があります。

5KBのフィールドが含まれていない場合にインデックスを使用したのは、取得したデータがテーブル内のデータの99%を構成しなかったためです。5KBのフィールドを含めた場合、クエリはデータの99%を読み取る必要があり、全体を読み取ってメモリ内で後でソートする方が安価です。


フルテーブルスキャンを回避する方法から多くのことを混乱させているように思われます。これはJOINWHERE句ではなく条件を満たす句や句でのインデックスの使用に関係していますORDER BY
eggyal

正反対です。この特定のケースでは、インデックス順で読み取るよりも高速であるため、全表スキャンは良いことです。

0

MySQLのどのバージョンを使用していますか?

5.1では、シナリオをセットアップして、いくつかのダミーデータを入力しました。指定したSQLを使用して、EXPLAINに従って毎回テーブルスキャンのみを取得します。MYSQLによる順序を使用する場合のデフォルトでは、プライマリインデックスが順序で使用されている場合でも、filesortを使用します。

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