2つのテーブルがあります。最初のテーブルには、CMS内のすべての記事/ブログ投稿が含まれています。これらの記事の一部は雑誌にも掲載される場合があります。その場合、それらは雑誌固有の情報を含む別のテーブルと外部キーの関係を持っています。
以下は、これらの2つのテーブルの作成テーブル構文の簡略化されたバージョンで、いくつかの重要でない行が削除されています。
CREATE TABLE `base_article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date_published` datetime DEFAULT NULL,
`title` varchar(255) NOT NULL,
`description` text,
`content` longtext,
`is_published` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `base_article_date_published` (`date_published`),
KEY `base_article_is_published` (`is_published`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `mag_article` (
`basearticle_ptr_id` int(11) NOT NULL,
`issue_slug` varchar(8) DEFAULT NULL,
`rubric` varchar(75) DEFAULT NULL,
PRIMARY KEY (`basearticle_ptr_id`),
KEY `mag_article_issue_slug` (`issue_slug`),
CONSTRAINT `basearticle_ptr_id_refs_id` FOREIGN KEY (`basearticle_ptr_id`) REFERENCES `base_article` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CMSには合計約250,000の記事が含まれており、この問題をローカルで再現したい場合に、テストデータベースにサンプルデータを入力するために使用できる簡単なPythonスクリプトを作成しました。
これらのテーブルの1つから選択した場合、MySQLは適切なインデックスを選択したり、記事をすばやく取得したりしても問題ありません。ただし、2つのテーブルが次のような単純なクエリで結合されている場合:
SELECT * FROM `base_article`
INNER JOIN `mag_article` ON (`mag_article`.`basearticle_ptr_id` = `base_article`.`id`)
WHERE is_published = 1
ORDER BY `base_article`.`date_published` DESC
LIMIT 30
MySQLは適切なクエリの選択に失敗し、パフォーマンスは急落します。拡張された関連する説明は次のとおりです(実行時間が1秒を超えています):
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| 1 | SIMPLE | mag_article | ALL | PRIMARY | NULL | NULL | NULL | 23830 | 100.00 | Using temporary; Using filesort |
| 1 | SIMPLE | base_article | eq_ref | PRIMARY,base_article_is_published | PRIMARY | 4 | my_test.mag_article.basearticle_ptr_id | 1 | 100.00 | Using where |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
- 9月30日の編集:
WHERE
このクエリから句を削除できますが、EXPLAIN
それでも外観は同じで、クエリはまだ低速です。
解決策の1つは、インデックスを強制することです。同じクエリをFORCE INDEX (base_articel_date_published)
実行すると、約1.6ミリ秒で実行されるクエリが生成されます。
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| 1 | SIMPLE | base_article | index | NULL | base_article_date_published | 9 | NULL | 30 | 833396.69 | Using where |
| 1 | SIMPLE | mag_article | eq_ref | PRIMARY | PRIMARY | 4 | my_test.base_article.id | 1 | 100.00 | |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
いくつかの理由で、回避できる場合は、このクエリにインデックスを強制する必要がないようにしたいと思います。特に、この基本的なクエリは、さまざまな方法(によるフィルタリングなどissue_slug
)でフィルタリング/変更できます。その後base_article_date_published
、使用するのに最適なインデックスではなくなります。
誰かがこのクエリのパフォーマンスを改善するための戦略を提案できますか?
base_article_is_published
(is_published
)を本当にドロップできます。私に見えるのはブール型です