3

本番サーバーで問題なく動作していたクエリが、非常に遅くなり始めました (数時間で)。

これです:

SELECT * FROM news_articles WHERE published = '1' AND news_category_id = '4' ORDER BY date_edited DESC LIMIT 1;

これには、実行に最大 20 ~ 30 秒かかります (テーブルには ~200.000 行あります)。

これは次の出力ですEXPLAIN

+----+-------------+---------------+-------------+----------------------------+----------------------------+---------+------+------+--------------------------------------------------------------------------+
| id | select_type | table         | type        | possible_keys              | key                        | key_len | ref  | rows | Extra                                                                    |
+----+-------------+---------------+-------------+----------------------------+----------------------------+---------+------+------+--------------------------------------------------------------------------+
|  1 | SIMPLE      | news_articles | index_merge | news_category_id,published | news_category_id,published | 5,5     | NULL | 8409 | Using intersect(news_category_id,published); Using where; Using filesort |
+----+-------------+---------------+-------------+----------------------------+----------------------------+---------+------+------+--------------------------------------------------------------------------+

date_editedそれをいじってみると、特定のインデックス ( ) をヒントすると、はるかに高速になることがわかりました。

SELECT * FROM news_articles USE INDEX (date_edited) WHERE published = '1' AND news_category_id = '4' ORDER BY date_edited DESC LIMIT 1;

これは実行に数ミリ秒かかります。

EXPLAINこの出力は次のとおりです。

+----+-------------+---------------+-------+---------------+-------------+---------+------+------+-------------+
| id | select_type | table         | type  | possible_keys | key         | key_len | ref  | rows | Extra       |
+----+-------------+---------------+-------+---------------+-------------+---------+------+------+-------------+
|  1 | SIMPLE      | news_articles | index | NULL          | date_edited | 8       | NULL |    1 | Using where |
+----+-------------+---------------+-------+---------------+-------------+---------+------+------+-------------+

news_category_idpublishedおよびdate_editedはすべて索引付けされています。

ストレージ エンジンは InnoDB です。

これはテーブル構造です:

CREATE TABLE `news_articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` text NOT NULL,
  `subtitle` text NOT NULL,
  `summary` text NOT NULL,
  `keywords` varchar(500) DEFAULT NULL,
  `body` mediumtext NOT NULL,
  `source` varchar(255) DEFAULT NULL,
  `source_visible` int(11) DEFAULT NULL,
  `author_information` enum('none','name','signature') NOT NULL     DEFAULT 'name',
  `date_added` datetime NOT NULL,
  `date_edited` datetime NOT NULL,
  `views` int(11) DEFAULT '0',
  `news_category_id` int(11) DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  `c_forwarded` int(11) DEFAULT '0',
  `published` int(11) DEFAULT '0',
  `deleted` int(11) DEFAULT '0',
  `permalink` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `news_category_id` (`news_category_id`),
  KEY `published` (`published`),
  KEY `deleted` (`deleted`),
  KEY `date_edited` (`date_edited`),
  CONSTRAINT `news_articles_ibfk_3` FOREIGN KEY (`news_category_id`) REFERENCES `news_categories` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
  CONSTRAINT `news_articles_ibfk_4` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=192588 DEFAULT CHARSET=utf8

Web アプリケーションが実行するすべてのクエリを変更して、そのインデックスを使用してヒントを与えることができます。しかし、これはかなりの作業です。

実際にすべてのクエリを書き換えることなく、最初のクエリがより効率的になるように MySQL を調整する方法はありますか?

4

3 に答える 3

3

ほんの少しのヒント..

1 - 公開されたフィールドと news_category_id は INTEGER のようです。その場合は、クエリから一重引用符を削除してください。パフォーマンスに関しては、大きな違いを生む可能性があります。

2 - また、公開されたフィールドには多くの異なる値がないと思います (おそらく 1 - はい、0 - いいえ、またはそのようなものです)。私が正しければ、これはインデックスを作成するのに適したフィールドではありません。この場合の解析では、探しているものを見つけるためにすべてのレコードを調べる必要があります。この場合、news_category_id を WHERE 句の最初のフィールドに移動します。

3 - 「一番左のインデックスを忘れないでください」。この肯定は、SELECT、JOINS、WHERE、ORDER BY に対して有効です。テーブル上の列の位置も重要です。インデックス付きの列を一番上にしてください。インデックスの使い方を知っている限り、インデックスは友達です。

それが何らかの形であなたを助けることを願っています..

SELECT * FROM news_articles WHERE published = '1' AND news_category_id = '4' ORDER BY date_edited DESC LIMIT 1;

于 2013-04-18T07:11:29.777 に答える
0

オリジナル: SELECT * FROM news_articles WHERE published = 1 AND news_category_id = 4 ORDER BY date_edited DESC LIMIT 1;

LIMIT 1があるため、最新の行のみを選択しています。ORDER BY date_editedは、MySQL にソートしてから 1 行を上から取り出すように指示します。これは本当に遅いので、USE INDEXが役立つ理由です。

代わりに、WHERE 句でMAX(date_edited)と一致するようにしてください。これにより、クエリ プランナーがそのインデックスを自動的に使用できるようになります。

MAX(date_entered) を選択: SELECT * FROM news_articles WHERE published = 1 AND news_category_id = 4 AND date_edited = (select max(date_edited) from news_articles);

于 2014-03-08T14:39:53.853 に答える
-1

クエリを次のように変更してください:

SELECT * FROM news_articles WHERE published = 1 AND news_category_id = 4 ORDER BY date_edited DESC LIMIT 1;

クエリで提供された「1」と「4」のデータから引用符を削除したことに注意してください

渡されたデータ型と列構造の違いにより、mysql はこれら 2 つの列でインデックスを使用できません。

于 2013-04-18T06:59:57.113 に答える