【问题标题】:How to ensure the query use the index?如何保证查询使用索引?
【发布时间】:2019-05-30 12:08:44
【问题描述】:

我的查询很慢。

SELECT
    posts.*
FROM
    posts
    INNER JOIN categories ON posts.category_id = categories.id
        AND categories.main = 1
        AND(categories.private_category = 0
            OR categories.private_category IS NULL)
WHERE
    posts.id NOT IN('')
    AND posts.deleted = 0
    AND posts.hidden = 0
    AND posts.total_points >= - 5
ORDER BY
    posts.id DESC
LIMIT 10;

所以在我解释之后:

| id | select_type | table       | partitions | type | possible_keys                                                                                                                                                    | key                         | key_len | ref                           | rows  | filtered | Extra                                        |
|----|-------------|-------------|------------|------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|---------|-------------------------------|-------|----------|----------------------------------------------|
|  1 | SIMPLE      | categories  |            | ALL  | PRIMARY,index_categories_on_private_category                                                                                                                     |                             |         |                               |    12 |    10.00 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | posts       |            | ref  | PRIMARY,index_posts_on_category_id,index_posts_on_deleted_and_hidden_and_user_id_and_created_at,index_posts_deleted,index_posts_hidden,index_posts_total_points  | index_posts_on_category_id  | 5       | mydb.categories.id            | 37516 |    12.50 | Using index condition; Using where           |

我在categories.main上添加了一个索引:

| id | select_type | table       | type   | possible_keys                                                                                                                                                    | key     | key_len | ref                               | rows  | Extra       |
|----|-------------|-------------|--------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|---------|-----------------------------------|-------|-------------|
|  1 | SIMPLE      | posts       | range  | PRIMARY,index_posts_on_category_id,index_posts_on_deleted_and_hidden_and_user_id_and_created_at,index_posts_deleted,index_posts_hidden,index_posts_total_points  | PRIMARY | 4       |                                   | 37516 | Using where |
|  1 | SIMPLE      | categories  | eq_ref | PRIMARY,index_categories_on_private_category,index_categories_on_main                                                                                            | PRIMARY | 4       | mydb.posts.category_id            |    12 | Using where |

它表明它不使用索引(?)。查询仍然很慢,我想优化它。查询有什么问题?

编辑

posts 表的创建方式如下:

CREATE TABLE `posts` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `category_id` int(11) DEFAULT NULL,
  `title` varchar(1000) NOT NULL,
  `content` text,
  `total_points` int(11) DEFAULT '0',
  `deleted` tinyint(1) DEFAULT '0',
  `hidden` tinyint(1) DEFAULT '0',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_posts_on_category_id` (`category_id`),
  KEY `index_posts_created_at` (`created_at`),
  KEY `index_posts_on_deleted_and_hidden_and_user_id_and_created_at` (`deleted`,`hidden`,`user_id`,`created_at`),
  KEY `index_posts_deleted` (`deleted`),
  KEY `index_posts_hidden` (`hidden`),
  KEY `index_posts_total_points` (`total_points`),
  KEY `index_posts_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=304063 DEFAULT CHARSET=utf8

类别表的创建方式如下:

CREATE TABLE `categories` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(100) NOT NULL,
  `main` tinyint(1) DEFAULT '0',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `hidden` tinyint(1) DEFAULT '0',
  `private_category` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `index_categories_on_private_category` (`private_category`),
  KEY `index_categories_on_main` (`main`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8

【问题讨论】:

  • 主要问题是查询规划器认为最好在posts 表之前访问categories 以减少记录,但它需要存储一个临时表Using temporary;,它可以是内存或基于磁盘...还需要`使用文件排序`文件排序是一个奇怪的名字,但这表明快速排序算法在数据上运行
  • 但是要求性能的答案应该包括SHOW CREATE TABLE table 输出以及问题中涉及的每个表。
  • 解释简单的顺序..如果您运行SELECT STRAIGHT_JOIN posts.*,您会注意到顺序会发生变化,并且很可能Using temporary; Using filesort 也会消失,因为索引正确.. 警告 STRAIGHT_JOIN 不是一个解决方案,因为有一天它可以正常工作,而另一天则不行/执行 annyway..
  • "显示它没有使用索引(?)。查询仍然很慢,我想优化它。查询有什么问题?"建立索引并不意味着 MySQL 必须使用它。优化器是基于成本的,第二个解释表明使用两个表中的 PRIMARY KEY 是获取数据的最便宜的方法。
  • 另外我相信升级到 MySQL 8.0 也会有所帮助,因为优化器倾向于尽可能避免Using temporary; Using filesort,但可能会扫描表/索引文件中的更多记录..@987654337当涉及大型表/结果集时,@组合可能是 MySQL 性能的真正杀手。

标签: mysql


【解决方案1】:

查询很慢,因为你使用了 OR 条件:

categories.private_category = 0 OR categories.private_category IS NULL

MySQL 必须扫描categories 表中的所有记录

【讨论】:

  • 谢谢!你能给出一个关于如何解决这个问题的提示吗?
  • 看看这个问题,它可能会有所帮助:stackoverflow.com/questions/2829544/…
  • @MIA "查询很慢,因为你使用了 OR 条件:" "MySQL 必须扫描类别表中的所有记录" MySQL 不能在一个列上优化 OR 就好了,请参阅 demo .. 多个(不同)列上的 OR 条件是像 column1 = 0 OR column2 iS NULL 这样的问题,您最好将其重写为 UNION 方法,某些优化器会像 Oracle 数据库一样自动执行该方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-19
  • 2014-02-13
  • 2018-11-01
  • 2013-03-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多