3

次のように要約できるテーブル構造があります。

pagegroup
* pagegroupid
* name

3600行あります

page
* pageid
* pagegroupid
* data

ページグループを参照します。10000行あります。ページグループごとに1〜700行の範囲で指定できます。データ列のタイプはmediumtextで、列には1行あたり100k〜200kバイトのデータが含まれます

userdata
* userdataid
* pageid
* column1
* column2
* column9

参照ページ; 約300,000行あります。1ページあたり約1〜50行にすることができます

上記の構造は非常に単純です。問題は、インデックスを作成する必要のあるすべての列にインデックスを付けたにもかかわらず、userdataからページグループへの結合が非常に遅くなることです。このような結合(userdatainner_joinページinner_joinページグループ)のクエリを実行するために必要な時間は3分を超えています。データ列をまったく選択していないことを考えると、これは非常に遅いです。時間がかかりすぎるクエリの例:

SELECT userdata.column1, pagegroup.name
FROM userdata
INNER JOIN page USING( pageid )
INNER JOIN pagegroup USING( pagegroupid )

なぜそんなに時間がかかるのか、そしてそれを速くするために何ができるのかを説明するのを手伝ってください。

編集#1

次のジブリッシュのリターンを説明します。

id  select_type  table      type    possible_keys        key      key_len  ref                         rows    Extra
1   SIMPLE       userdata   ALL     pageid                                                             372420
1   SIMPLE       page       eq_ref  PRIMARY,pagegroupid  PRIMARY  4        topsecret.userdata.pageid   1
1   SIMPLE       pagegroup  eq_ref  PRIMARY              PRIMARY  4        topsecret.page.pagegroupid  1

編集#2

SELECT
u.field2, p.pageid
FROM
userdata u
INNER JOIN page p ON u.pageid = p.pageid;
/*
0.07 sec execution, 6.05 sec fecth
*/

id  select_type  table  type    possible_keys  key      key_len  ref                rows     Extra
1   SIMPLE       u      ALL     pageid                                              372420
1   SIMPLE       p      eq_ref  PRIMARY        PRIMARY  4        topsecret.u.pageid 1        Using index

SELECT
p.pageid, g.pagegroupid
FROM
page p
INNER JOIN pagegroup g ON p.pagegroupid = g.pagegroupid;
/*
9.37 sec execution, 60.0 sec fetch
*/

id  select_type  table  type   possible_keys  key          key_len  ref                      rows  Extra
1   SIMPLE       g      index  PRIMARY        PRIMARY      4                                 3646  Using index
1   SIMPLE       p      ref    pagegroupid    pagegroupid  5        topsecret.g.pagegroupid  3     Using where

この話の教訓

このようなパフォーマンスの問題が発生した場合は、中/長のテキスト列を別のテーブルに保持してください。

4

6 に答える 6

4

userdata テーブルの columnX のデータ型と目的は何ですか? テキスト データ型 (つまり、char、varchar を除く) では、一時テーブルがディスク上に作成されることに注意してください。条件、グループ化、または順序付けなしで直接結合を行っているため、最終結果を集計することを除いて、おそらく一時テーブルは必要ありません。

インデックスがどのように作成されているかを示すことも非常に役立つと思います. InnoDB はテーブルの主キーを各インデックスに連結しますが、MyISAM は連結しません。これは、列を索引付けして LIKE で検索しても、ページ・グループのIDを取得したい場合を意味します。その後、クエリは、インデックスから ID を取得するのではなく、テーブルにアクセスしてIDを取得する必要があります。

これが意味することは、あなたの場合、apphackerへのコメントを正しく理解していれば、各ユーザーのページグループの名前を取得することです。クエリ オプティマイザは、結合にインデックスを使用する必要がありますが、結果ごとにテーブルにアクセスしてページ グループ名を取得する必要もあります。nameのデータ型が適度な varchar よりも大きくない場合、つまりテキストがない場合は、クエリがインデックスから直接名前を取得できるようにするインデックス (id、name) を作成することもできます。

最後の試みとして、mediumtext がページ テーブルにない場合、クエリ全体がおそらく高速になることを指摘します。

  1. この列は、実行中のクエリから除外されていると思いますか?
  2. また、ページ データをページの「構成」、つまりどのグループに属するかから分離することもできます。あなたはおそらく次のようなものを持っているでしょう:
    • ページ
      • ページ ID
      • pageGroupId
    • ページデータ
      • ページ ID
      • データ

ページ内の列が多くのスペースを占有しないため、これにより、より迅速に参加できるようになることが期待されます. 次に、特定のページを表示する必要がある場合は、pageId-column で PageData テーブルと結合して、特定のページを表示するために必要なデータを取得します。

于 2009-05-09T08:53:36.667 に答える
2

MySQLがクエリで何をしているのかを理解する簡単な方法は、クエリを説明してもらうことです。これを実行して、出力を確認してください。

EXPLAIN SELECT userdata.column1, pagegroup.name
FROM userdata
INNER JOIN page USING( pageid )
INNER JOIN pagegroup USING( pagegroupid )

MySQLは、クエリを処理する順序と使用するインデックスを通知します。インデックスを作成したからといって、MySQLが実際にインデックスを使用しているわけではありません。

EXPLAINを使用したクエリの最適化も参照してください。

編集

EXPLAINの出力は正常に見えます。userdataテーブルに対して全表スキャンを実行しますが、その中のすべての行を返したいので、これは正常です。これを最適化する最良の方法は、アプリケーションを再考することです。本当にすべての372K行を返す必要がありますか?

于 2009-05-09T07:11:56.207 に答える
2

userdata テーブルが非常に大きく、メモリに収まらないと想定しています。MySQL は、2 つの小さな列しか必要としない場合でも、ハードディスクからテーブル全体を読み取る必要があります。

クエリに必要なものすべてを含むインデックスを定義することで、テーブル全体をスキャンする必要をなくすことができます。このように、インデックスはメイン テーブルへの検索を容易にする方法ではありませんが、テーブル自体の簡略版です。MySQL は、ディスクから短縮テーブルを読み取るだけで済みます。

インデックスは次のようになります。

column1, pageid

これはクラスター化されていない必要があります。そうしないと、大きなテーブルの一部になり、その目的が果たせなくなります。MySQL がクラスター化するインデックスを決定する方法については、このページを参照してください。最も簡単な方法は、クラスター化される pageid に主キーがあることを確認することです。そのため、セカンダリ column1 + pageid インデックスはクラスター化されません。

于 2009-05-09T09:08:22.137 に答える
1

考えられる問題の1つは、MySQLがクエリごとに1つのインデックスのみを使用し、それらの列に単一のインデックスがないか、MySQLのクエリオプティマイザがそれを選択していないことです。EXPLAIN SELECT&cはここで何を教えてくれますか?

于 2009-05-09T07:10:16.570 に答える
1

まず、クエリを分割して、遅い部分と速い部分があるかどうか、または両方が遅いかどうかを判断します (申し訳ありませんが、私は USING 構文が好きではないので、ON を使用します)。

SELECT 
  u.userdata, p.pageid
FROM
  userdata u
  INNER JOIN page p ON u.pageid = p.pageid

SELECT 
  p.pageid, g.pagegroupid
FROM
  page 
  INNER JOIN pagegroup g ON p.pagegroupid = g.pagegroupid

それはあなたに何を与えますか?これらを実行するEXPLAIN EXTENDEDと、追加のヒントが提供されます。

于 2009-05-09T07:45:55.720 に答える
1

すべての行を結合してuserdataから、すべてを選択しようとしているようです。それはwithのすべてpageです。条文はどこ?はありません。いくつの結果が必要でしたか? 結果の行で行カウントを下げてみませんか。これにより、クエリが高速化されます。へー。pagegroupuserdataWHERELIMITuserdataexplain

于 2009-05-09T07:53:40.633 に答える