2011-01-24 9 views
1

私は非常にシンプルなスキーマを持つMySQLデータベースを持っています。 parent,childおよびaccessのテーブルがあります。MySQLデータベースのパフォーマンスチューニング、スキーマの最適化

parent店舗4つのlongtextフィールドとbigint一次キーのを除いて(16から512までの長さの範囲の)全てvarcharある51のフィールド。主キーの他に3つのフィールドにインデックスがあります。 1つは、childテーブルがそれを外部キーとして参照できるようにすることです。

childは、ほとんどがvarcharで、フィールド数がtextの23のフィールドを格納します。 A varchar(256)フィールドは、それを親にリンクするための外部キーとして使用されます。ただし、外部キーフィールドの実際の内容はすべて60文字未満になると予想されます。

accesssは一緒に主キーを構成するbigintフィールドとvarcharフィールドを持ち、bigintフィールドがparentにリンクする外部キーです。

accessテーブルは、どのユーザーがparentからどのレコードにアクセスできるかを指定するために使用されます。任意のレコードにアクセスする必要がある複数のユーザーがいる可能性があります。

parent (したがって accessにし、childで2E7行の周り2E6行の周りにあります。編集:申し訳ありません、access 5329840行があります。つまり、parentの行ごとに1つ以上の行がaccessにあります。

上記のスキーマは、私たちがMySQLに移行しようとしている古いFileMakerデータベースに基づいています。私はそれが理想的ではないと確信していますが、私は正確になぜそれを知りません。

クエリは、パラメータによって高速またはかなり遅くなります。したがって、たとえば次のクエリは、bobがアクセスできるレコードが複数ある場合は、1〜2秒かかります。 (bobと呼ばれるユーザが存在しない場合、例えば)、ユーザーbobがアクセス権を持っていることをレコードがない場合、クエリは、しかし、(例えば12分)、数分かかります。

SELECT 
    p."RecordID", p."SerialNumber", p."Folder", p."NoteType", 
    p."FirstName", p."LastName", p."DOB", p."Body", p."From", 
    p."DateTxt", a."UserName" AS Access 
FROM parent AS p 
INNER JOIN access AS a ON a."RecordID" = p."RecordID" 
WHERE p."RecordID" > 123 
AND a."UserName" = 'bob' 
ORDER BY p."RecordID" 
LIMIT 500; 

ここEXPLAINは、クエリについて言っているのです:

+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref     | rows | Extra     | 
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+--------------------------+ 
| 1 | SIMPLE  | p  | range | PRIMARY  | PRIMARY | 8  | NULL    | 731752 | Using where    | 
| 1 | SIMPLE  | a  | eq_ref | PRIMARY  | PRIMARY | 74  | db.p.RecordID,const |  1 | Using where; Using index | 
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+--------------------------+ 
2 rows in set (0.01 sec) 

したがって、不一致のクエリを高速化する方法はありますか?これは、すべてにvarchar/textフィールドを使用することによって発生する可能性がありますか?外部キーとしてvarchar(256)フィールドを使用すると問題が発生します(ただし、上記のクエリでは使用されていません)。または、クエリに責任がありますか?

編集:私はちょうどSELECT ... FROM access WHERE UserName = 'blah'のテーブルのPRIMARY KEY ("RecordID", "UserName")が使用されていないことを認識しました。 UserName列にインデックスを作成しましたが、問題が修正されているようです。誰かアドバイスがあれば、私はまだそれを感謝します。 EXPLAIN

電流出力:

+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref     | rows | Extra     | 
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+--------------------------+ 
| 1 | SIMPLE  | p  | range | PRIMARY  | PRIMARY | 8  | NULL    | 605020 | Using where    | 
| 1 | SIMPLE  | a  | eq_ref | PRIMARY,UNidx | PRIMARY | 74  | db.p.RecordID,const |  1 | Using where; Using index | 
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+--------------------------+ 
2 rows in set (0.00 sec) 

EDIT:DRappの提案@大きな違いを生むん。クエリはaccess.UserNameのインデックスの有無にかかわらず高速です。私がインデックスを削除し、STRAIGHT_JOINなしでDRappのクエリを試してみると、それは再び遅くなります。

access.UserName上のインデックスのないDRappのクエリ:access.UserName上のインデックスを持つ

mysql> explain SELECT STRAIGHT_JOIN p."RecordID", p."SerialNumber", p."Folder", p."NoteType", p."FirstName", p."LastName", p."DOB", p."Body", p."From", p."DateTxt", a."UserName" AS Access  FROM access as a, parent AS p where a."UserName" = 'bob' and a."RecordID" > 123 and a."RecordID" = p."RecordID" order by a."RecordID" limit 500; 
+----+-------------+-------+--------+---------------+---------+---------+---------------+---------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra     | 
+----+-------------+-------+--------+---------------+---------+---------+---------------+---------+--------------------------+ 
| 1 | SIMPLE  | a  | range | PRIMARY  | PRIMARY | 8  | NULL   | 2382668 | Using where; Using index | 
| 1 | SIMPLE  | p  | eq_ref | PRIMARY  | PRIMARY | 8  | bb.a.RecordID |  1 |       | 
+----+-------------+-------+--------+---------------+---------+---------+---------------+---------+--------------------------+ 
2 rows in set (0.00 sec) 

同じクエリ:access.UserName上となしのインデックスなし

mysql> explain SELECT STRAIGHT_JOIN ...; 
+----+-------------+-------+--------+---------------+---------+---------+---------------+---------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra     | 
+----+-------------+-------+--------+---------------+---------+---------+---------------+---------+--------------------------+ 
| 1 | SIMPLE  | a  | ref | PRIMARY,UNidx | UNidx | 66  | const   | 1209780 | Using where; Using index | 
| 1 | SIMPLE  | p  | eq_ref | PRIMARY  | PRIMARY | 8  | db.a.RecordID |  1 |       | 
+----+-------------+-------+--------+---------------+---------+---------+---------------+---------+--------------------------+ 
2 rows in set (0.00 sec) 

同じクエリSTRAIGHT_JOIN

mysql> explain SELECT p."RecordID", p."SerialNumber", p."Folder", p."NoteType", p."FirstName", p."LastName", p."DOB", p."Body", p."From", p."DateTxt", a."UserName" AS Access FROM access as a, parent AS p where a."UserName" = 'zzz' and a."RecordID" > 123 and a."RecordID" = p."RecordID" order by a."RecordID" limit 500; 
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+----------------------------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref     | rows | Extra          | 
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+----------------------------------------------+ 
| 1 | SIMPLE  | p  | range | PRIMARY  | PRIMARY | 8  | NULL    | 484016 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | a  | eq_ref | PRIMARY  | PRIMARY | 74  | db.p.RecordID,const |  1 | Using where; Using index      | 
+----+-------------+-------+--------+---------------+---------+---------+---------------------+--------+----------------------------------------------+ 
2 rows in set (0.00 sec) 

答えて

1

私はいつも良いl MySQLで "STRAIGHT_JOIN"節を使用してuckを実行し、修飾子のリストの最初にプライマリテーブルの基礎を置きます。この場合、あなたの "アクセス"テーブルはボブを探していて、次にボブがアクセスできるレコードを見ています。 ACCESSクエリで失敗した場合は、詳細を確認する必要はありません。さらに、結合は同じ "RecordID"に基づいているので、 "a"への参照を変更しました。表。このクエリはユーザ名に基づいているので、私もそれにキーを持っています。私はそのExplainとパフォーマンスにも興味があります。

SELECT STRAIGHT_JOIN 
     p."RecordID", 
     p."SerialNumber", 
     p."Folder", 
     p."NoteType", 
     p."FirstName", 
     p."LastName", 
     p."DOB", 
     p."Body", 
     p."From", 
     p."DateTxt", 
     a."UserName" AS Access 
    FROM 
     access as a, 
     parent AS p 
    where 
      a."UserName" = 'bob' 
     and a."RecordID" > 123 
     and a."RecordID" = p."RecordID" 
    order by 
     a."RecordID" 
    limit 
     500 
+0

ありがとうございました。以前はSTRAIGHT_JOINのことは聞いたことがありません。それを見て。 – Wodin

+0

@Wodin、ええ、それはオプティマイザに伝えます、私のためには考えません、私はテーブルとジョイン条件を置く順序を知っています...そのシーケンスを使用してください。ルックアップテーブルへの15以上のジョインで、1400万行以上のデータを持つことができました。オプティマイザは、より小さいテーブルを使用して1400万以上のテーブルに戻ってきて、チョークしました。 – DRapp

+0

はい、ありがとう、私は知っている:)私はあなたの答えをupvoted。私が他の答えを得たかどうかを確認するのを待ってから答えてください。例えば私はまだvarchar/textのようにすべてのフィールドにかなりの影響があり、またvarchar(256)フィールドを外部キーとして使用するかどうかはひどいことです。私はこの1つ前にまだ答えられていない1つの質問を残しましたが、その回答はゼロであり、振り返ってみるとおそらくstackoverflowに属しません。だから私は4つの質問をしたので、私の受け入れは現時点で50%です。私はこの質問に回答した時点で75%まで上がります – Wodin