3

私は2つのテーブルを持っています:

CREATE TABLE `linf` (
  `ID` bigint(20) NOT NULL AUTO_INCREMENT,
  `glorious` bit(1) DEFAULT NULL,
  `limad` varchar(127) COLLATE utf8_bin DEFAULT NULL,
  `linfDetails_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  KEY `FK242415D3B0D13C` (`linfDetails_id`),
  CONSTRAINT `FK242415D3B0D13C` FOREIGN KEY (`linfDetails_id`) REFERENCES `linfdetails` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=135111 DEFAULT CHARSET=utf8 COLLATE=utf8_bin

(130K行)

CREATE TABLE `messageentry` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `mboxOffset` bigint(20) DEFAULT NULL,
  `mboxOffsetEnd` bigint(20) DEFAULT NULL,
  `from_id` bigint(20) DEFAULT NULL,
  `linf_ID` bigint(20) DEFAULT NULL,
  `mailSourceFile_id` bigint(20) DEFAULT NULL,
  `messageDetails_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FKBBB258CB60B94D38` (`mailSourceFile_id`),
  KEY `FKBBB258CB11F9E114` (`from_id`),
  KEY `FKBBB258CBF7C835B8` (`messageDetails_id`),
  KEY `FKBBB258CBB10E8518` (`linf_ID`),
  CONSTRAINT `FKBBB258CBB10E8518` FOREIGN KEY (`linf_ID`) REFERENCES `linf` (`ID`),
  CONSTRAINT `FKBBB258CB11F9E114` FOREIGN KEY (`from_id`) REFERENCES `emailandname` (`id`),
  CONSTRAINT `FKBBB258CB60B94D38` FOREIGN KEY (`mailSourceFile_id`) REFERENCES `mailsourcefile` (`id`),
  CONSTRAINT `FKBBB258CBF7C835B8` FOREIGN KEY (`messageDetails_id`) REFERENCES `messagedetails` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5888892 DEFAULT CHARSET=utf8 COLLATE=utf8_bin

(500万行)

linf.limadでlinfを検索してから、このlinfに対応するすべてのメッセージを検索する必要があります。

2つのクエリで選択した場合:

select sql_no_cache l.id from linf l where l.limad='test@';
select sql_no_cache me.* from messageentry me where me.linf_id = 118668;

その後、0.06秒かかります。

使用する場合

select sql_no_cache me.* from messageentry me where me.linf_id in(
select l.id from linf l where l.limad='test@') ;

実行には10秒かかります。そしてこれ:

select sql_no_cache me.* from messageentry me, linf l where me.linf_id=l.id
and l.limad='test@';

4秒かかります。(時間は安定しています)

このlinfのメッセージがないため、この要求は0の結果を返します。実際、私はこれを大きな要求から取り除きました

select messageent1_.*
from
    MailSourceFile mailsource0_,        
    MessageEntry messageent1_ ,
    MessageDetails messagedet2_,    
    Linf linf3_
where
    messageent1_.messageDetails_id = messagedet2_.id
        and messageent1_.linf_ID = linf3_.ID
        and linf3_.limad = 'test@'
and mailsource0_.id = messageent1_.mailSourceFile_id

これは約1分で動作します。そんなにありませんか?Explainは、messageEntriesインデックスは使用されていないと述べています。

mysql> explain select sql_no_cache me.* from messageentry me, linf l where me.linf_id=l.id and l.limad='test@';
+----+-------------+-------+--------+--------------------+---------+---------+------------------+---------+-------------+
| id | select_type | table | type   | possible_keys      | key     | key_len | ref              | rows    | Extra       |
+----+-------------+-------+--------+--------------------+---------+---------+------------------+---------+-------------+
|  1 | SIMPLE      | me    | ALL    | FKBBB258CBB10E8518 | NULL    | NULL    | NULL             | 5836332 |             |
|  1 | SIMPLE      | l     | eq_ref | PRIMARY            | PRIMARY | 8       | skryb.me.linf_ID |       1 | Using where |
+----+-------------+-------+--------+--------------------+---------+---------+------------------+---------+-------------+

なぜ何かアイデアはありますか?私はmysql〜1.6 Gのメモリを獲得しましたが、これはすべてのテーブルに適合するはずです。

ありがとう。

4

4 に答える 4

3

クエリを見てみましょう:

select sql_no_cache me.*
from messageentry me, linf l
where me.linf_id=l.id
and l.limad='test@';

それは何をするためのものか?EXPLAINテーブルの各行の実行プランに従ってme、に対応するレコードがあるかどうかを確認しますlinf。フィールドにインデックスがないためlimad、MySQL 5M回はlimadフィールドの値を(メモリからではなく)ディスクからフェッチして、「@test」と等しいかどうかを確認します。クエリが0行を返すと言いますが、より多くの行を提供する別の値の場合、すべてのフィールドlimadについてディスクに配置する必要があります。me.*

わかりました、limadフィールドはですvarchar(127) COLLATE utf8_bin、それはそれのインデックスが高価かもしれません(とにかくそれを追加します)。130k行は5M未満なので、から始めるとよいでしょう。最初linfから必要なmessageentryのは。だけですid, mailSourceFile_id, messageDetails_id。なぜそれらのフィールドだけですか?さらに2つの結合を行い、結合されたテーブルからデータを取得しないため、テーブルは最終的な結果セットを狭めるように見えます。つまり、クエリのスケルトンに必要です。それらだけから始めましょう:

SELECT me.id, me.mailSourceFile_id, me.messageDetails_id
FROM (
  SELECT ID as linf_ID
  FROM linf
  WHERE limad='test@'
) as linf
JOIN messageentry me USING (linf_ID);

クエリは必要なlinf_IDをとして選択し、見つかったIDごとに。の適切な行を検索しmessageentryます。linf_iDにインデックスがあるため、クエリの結果は4秒より速くなります。

ただしme.mailSourceFile_id, me.messageDetails_id、MySQLは複雑なインデックスマージを実行する必要があるため、これらをメモリから取得することはできません。したがって、MySQLは、linf_IDが一致する各行のディスクに移動します。これらの3つのフィールドすべてを一度に含むインデックスがある場合、後続の結合によってフィルタリングされる行が大量にある場合、クエリはさらに高速になります。

KEYFKBBB258CBB10E8518 (linf_ID)FKBBB258CBB10E8518 (linf_ID, mailSourceFile_id, messageDetails_id)に更新すると、そのようなインデックスが作成されます。

結果のクエリは次のようになります。

SELECT me.*
FROM (
  SELECT ID as linf_ID
  FROM linf
  WHERE limad='test@'
) as linf
JOIN messageentry me USING (linf_ID)
JOIN MailSourceFile ms ON ms.id = me.mailSourceFile_id
JOIN MessageDetails md ON md.id = me.messageDetails_id;

実際、上記のようにインデックスを更新するとすぐにFKBBB258CBB10E8518 (linf_ID)、元のクエリの実行プランとタイミングは最後のクエリと同じになる可能性があります。

于 2012-12-07T17:25:02.293 に答える
0

このように、結合基準を明示的に定義するとどうなりますか?

select sql_no_cache me.* 
from messageentry me JOIN linf l ON  me.linf_id=l.id
WHERE l.limad='test@';

オプティマイザーがクロスジョインまたはその他の奇妙なことを選択した場合、バージョンに問題が発生する可能性があります。

それを除けば、フォースインデックスを作成することを検討してください。

select sql_no_cache me.* 
from messageentry me FORCE INDEX (FKBBB258CBB10E8518)
JOIN linf l ON  me.linf_id=l.id         
WHERE l.limad='test@';

これにより、少なくともインデックスが実際に役立つかどうかがわかります。

于 2012-12-03T21:08:04.307 に答える
0

MySQLは、句内のサブクエリを使用して非常に貧弱な仕事をしておりin、そこで見られるパフォーマンスの低さを説明しています。結合のパフォーマンスは、結合の順序に関係していると思います。おそらくメッセージテーブル全体を読んでいます。

inバージョンをexists:に変更してみてください

select sql_no_cache me.*
from messageentry me
where exists (select 1 from linf l where l.limad='test@' and l.id = me.inf_id limit 1) ;

ちなみに、on句ではなく句で結合を行うことに慣れておく必要がありますwhere

于 2012-12-03T21:09:47.887 に答える
0
  • 可能であればBIGINTの代わりにINTを使用してみてください。また、可能であれば主キーにINTを選択してください。「linf_ID」のようなセカンダリインデックスは、関連するプライマリキーをディスクに格納します。BIGINTを使用すると、ページフォールトとディスク読み取りが増えることになります。 http://planet.mysql.com/entry/?id=13825

  • varcharのインデックスサイズを小さくするには、limadのインデックス部分を試してください。
    本「HighPerformanceMysql3Edition」では、varcharインデックスの長さを選択する方法を説明しています。次の2つのSQLの結果が類似する長さを選択してください

    SELECT COUNT(DISTINCT city)/ COUNT(*)FROM sakila.city_demo;

    SELECT COUNT(DISTINCT LEFT(city、3))/ COUNT()AS sel3、COUNT(DISTINCT LEFT(city、4))/ COUNT()AS sel4、COUNT(DISTINCT LEFT(city、5))/ COUNT()AS sel5、COUNT(DISTINCT LEFT(city、6))/ COUNT()AS sel6、COUNT(DISTINCT LEFT(city、7))/ COUNT(*)AS sel7 FROM sakila.city_demo;

  • MySQLにディスク内のデータを分析および最適化させ ますhttp://dev.mysql.com/doc/refman/5.1/en/optimize-table.htmlhttp://dev.mysql.com/doc/refman/5.0/en/分析-テーブル.html

  • 質問で1分間実行する「ビッグリクエスト」SQLの場合、このSQLを最適化するには、複数の列のインデックスを使用する必要があります。 http://dev.mysql.com/doc/refman/5.0/en/multiple-column-indexes.html

    CREATE UNIQUE INDEX idx_name ON MessageEntry(linf_ID、messageDetails_id、mailSourceFile_id)

于 2012-12-07T15:03:29.817 に答える