2016-12-29 9 views
1

2つのテーブルを含む左結合を最適化しようとしていますが、スピードアップのためにインデックスを丸めることができません。MySQLはインデックスとグループの最適化で結合しました

SELECT md.day as handle, count(db.text_id) as hits, 
    count(distinct db.text_id) as files FROM text_metadata_for_nzcorpus as md 
    LEFT JOIN db_dist_fb8ddyk760 as db on md.text_id = db.text_id 
    GROUP BY md.day; 

これは現在、より多くを取る:私は次のタイプのクエリを実行する必要が

db_dist_fb8ddyk760 | CREATE TABLE `db_dist_fb8ddyk760` (
    `text_id` varchar(255) COLLATE utf8_bin DEFAULT NULL, 
    `beginPosition` int(11) DEFAULT NULL, 
    `endPosition` int(11) DEFAULT NULL, 
    `refnumber` mediumint(9) NOT NULL AUTO_INCREMENT, 
    KEY `refnumber` (`refnumber`), 
    KEY `text_id` (`text_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=16384 DEFAULT CHARSET=utf8 COLLATE=utf8_bin | 

text_metadata_for_nzcorpus | CREATE TABLE `text_metadata_for_nzcorpus` (
    `text_id` varchar(255) NOT NULL, 
    `newspaper` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `year` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `month` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `day` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `section` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `subsection` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `topics` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `words` int(11) NOT NULL DEFAULT '0', 
    `cqp_begin` bigint(20) unsigned NOT NULL DEFAULT '0', 
    `cqp_end` bigint(20) unsigned NOT NULL DEFAULT '0', 
    PRIMARY KEY (`text_id`), 
    KEY `newspaper` (`newspaper`), 
    KEY `year` (`year`), 
    KEY `month` (`month`), 
    KEY `day` (`day`), 
    KEY `section` (`section`), 
    KEY `subsection` (`subsection`), 
    KEY `topics` (`topics`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

第二のテーブルのみ8584行が含まれています 表1は、2171289行が含まれています処理するのに5秒以上かかる。それはWebページ上に出力を表示する前に実行する必要がある非常に少数のクエリの1つなので、できる限り速くしたいと思います。 「説明」の出力は次のとおりです。

+----+-------------+-------+-------+---------------+---------+---------+----------------------+---------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref     | rows | Extra     | 
+----+-------------+-------+-------+---------------+---------+---------+----------------------+---------+--------------------------+ 
| 1 | SIMPLE  | md | index | day   | day  | 768  | NULL     | 2452080 | Using index    | 
| 1 | SIMPLE  | db | ref | text_id  | text_id | 768  | cqpweb_db.md.text_id |  1 | Using where; Using index | 
+----+-------------+-------+-------+---------------+---------+---------+----------------------+---------+--------------------------+ 

ご協力いただきありがとうございます。 (私はシステムの開発者ではなく、私はそのようなコードを担当していません - しかし、改善が可能な場合はプログラマに意見を提供したい...)

多くの感謝! Sebastian

答えて

1

EXPLAINレポートには、すでに両方のテーブルのインデックスが使用されており、GROUP BYにはテンポラリテーブルが使用されておらず、どちらのテーブルもカバリングインデックス( "インデックスの使用")を使用しています。

あなたは、インデックスを作成する以外に行うことができますいくつかの他の事:

  • NOT NULLとしてdb_dist_fb8ddyk760.text_id定義します。これは、 "Where where"ノートを取り除く可能性があります。つまり、検索の一部として式を評価する必要があります。それはやや効率的かもしれません。
  • db_dist_fb8ddyk760.text_idを、そのテーブルのPRIMARY KEYとして定義します(つまり、text_idがそのテーブル内で一意である場合)。こうすることで、 "type:ref"は "type:eq_ref"となり、より効率的な一意のキー検索を意味します。もちろん、このテーブルがtext_idごとに複数のヒットを記録する必要がある場合は、この提案を無視してください。
  • innodb_buffer_pool_sizeを十分に大きくして、インデックスをメモリにキャッシュできるようにします。問合せがバッファ・プールからのみ索引ページを読み取る場合は、パフォーマンスが向上し、ディスクI/Oが少なくなります。
  • MySQL Query Cacheを利用すると、同じクエリを再度実行すると、前のクエリの結果が再利用されます。しかし、これらのテーブルのデータがクエリを実行するよりも頻繁に変更される場合、クエリキャッシュはほとんど役に立たない可能性があります。
  • 結果をアプリケーションメモリまたはmemcachedなどにキャッシュすることを検討してください。あなたのコメントを再

はところで、テーブルdb_dist_fb8ddyk760は2回だけ使用し、その後廃棄される可能性が高いです。

なぜ耐久性のあるデータベースに保管しているのですか?

Redisのようなメモリ内キー/バリューストアの使用を検討してください。各キーを1日に対応させ、各値はヒット数と個別のtext_idのセットを含む構造体です。これは基本的にはサマリーテーブル(これもSQLでもできます)を作成していますが、Redisはメモリ内にあります。

+0

ありがとうございます。残念ながら、text_idを主キーにすることはできません。あなたが提案した他のものを試してみましょう。 –

+0

これはキャッシュされており、別のユーザーが同じクエリを実行した場合に再利用できるため、これらのデータベースの作成にかなりの時間を節約できます。特定のDBがどれくらいの頻度でどのくらいの期間、どのくらいのユーザーが使用されているかを事前に調べる方法はありません。時には30人が同じことをすることがあります(これがキャッシュが理にかなっています)。時には、巨大なテーブルをコンパイルすると出力を一度しか見ることができない場合があります。全体的に、これは最良の妥協策に見えました。 –

+0

また、「日」はあなたがそう思っているとは思っていません... ;-)「日」は、テキスト集合内の任意のレベルの注釈を含むことができるハンドルです(この場合、実際には日ですすなわち、1から31までの数字)。これはすべて、電子テキストコーパス(http://cwb.sourceforge.net/cqpweb.php)へのインターフェースに関連しています。 –

2

盲目的にVARCHAR(255)を使用しないでください。データに合ったデータ型を使用します。それらの列の多くは文字列ではなく数字のように聞こえる。

年+月+日が単にDATEの部分であると仮定すると、データ型がDATEの単一の列を使用します。次に、DAY(date_col)を使用して日付を抽出します。

すべてのInnoDBテーブルには、PRIMARY KEYが必要です。おそらく(text_id, beginPosition)のコンボはユニークで、PKである可能性がありますか?

すべての列がありますNULL ??疑わしい。理由がある場合を除き、NOT NULLにしてください。理由はNULLです。

refnumberは、AUTO_INCREMENTであるが、PRIMARY KEYではない。何がありますか?

上記の変更を行うと、が一部になります。しかし、2M行のテーブル全体をスキャンして、他のテーブルに多くアクセスすることを目的としています。物事を行うことができます。しかし、サマリーテーブルの作成と維持が必要になります。

+0

サマリーテーブルを持っていることに完全に同意します...それが与えられた日の終わりにあらかじめ集計されていても、それは一度実行され、最新の日。 – DRapp

+0

ありがとう - いくつかのコメント:私はVARCHARではなく数字について何を言っているのか理解していますが、テーブルは柔軟性が必要なシステムの一部です。最初から個々の列にどのような種類のデータがあるかは明らかではありません。はい、(text_id、beginPosition)のコンボは一意です。これを調べ、NULLについての問題についても調べます。ところで、テーブルdb_dist_fb8ddyk760は1回か2回しか使用されずに破棄される可能性があります。だから私は初めてのラウンドで動作する最適化を探しています... –

+0

別の質問... "日"は月の日ですか?または、他の何か? (私はグループ化の目的が何であるか疑問に思っています) –

関連する問題