[MySQL高级](三) 索引分析详解数据库

1. 单表

1.1 建表SQL

CREATE TABLE `article` ( 
  `id` int(10) NOT NULL, 
  `author_id` int(10) DEFAULT NULL, 
  `category_id` int(10) DEFAULT NULL, 
  `views` int(10) DEFAULT NULL, 
  `comments` int(10) DEFAULT NULL, 
  `title` varchar(255) DEFAULT NULL, 
  `content` text, 
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
 
INSERT INTO `test`.`article` (`id`, `author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES ('1', '1', '1', '1', '1', '1', '1'); 
INSERT INTO `test`.`article` (`id`, `author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES ('2', '2', '2', '2', '2', '2', '2'); 
INSERT INTO `test`.`article` (`id`, `author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES ('3', '3', '3', '3', '3', '3', '3'); 
INSERT INTO `test`.`article` (`id`, `author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES ('4', '4', '4', '4', '4', '4', '4');

1.2 案例分析

查询category_id为1且comments大于1的情况下,views最多的article_id

EXPLAIN SELECT 
    id, 
    author_id 
FROM 
    article 
WHERE 
    category_id = 1 
AND comments > 1 
ORDER BY 
    views DESC 
LIMIT 1;

这里写图片描述
结论:很显然,type是ALL,即最坏的情况。Extra里还出现了Using filesort,也是最坏的情况。优化是必须的。

1.3 开始优化

1.3.1 创建索引 idx_article_ccv

首先,我们根据where后面要查询的字段建立复合索引,即category_id、comments、views。

CREATE INDEX idx_article_ccv ON article (category_id, comments, views);

那么我们再来执行一下,看看测试结果:
这里写图片描述
✈ 分析:根据测试结果,key的值为index_article_ccv,说明该索引已经被使用,而且type变成了range,这是可以忍受的,但是Extra中的Using filesort仍然在,这是无法接受的。但是,我们已经建立了索引,为啥没用呢?这是因为按照BTree索引的工作原理,先排序category_id,如果遇到相同的category_id则再排序comments,如果遇到相同的comments则再排序views。当comments字段在联合索引里处于中间位置时,因comments>1条件是一个范围值(所谓range),MySQL无法利用索引再对后面的views部分进行检索,即range类型查询字段后面的索引无效。

那么我们看下面这条SQL的执行情况。

EXPLAIN SELECT 
    id, 
    author_id 
FROM 
    article 
WHERE 
    category_id = 1 
AND comments = 1   -- 注意这里改为了常量 
ORDER BY 
    views DESC 
LIMIT 1;

这里写图片描述
我们发现,当comments字段的查询条件改为常量1的时候,没有了Using filesort。这说明使用 > 这种范围查询会使后面的索引失效。

1.3.2 创建索引 idx_article_cv

删除刚刚建立的索引

DROP INDEX idx_article_ccv ON article 

建立category_id、views索引 idx_article_cv

CREATE INDEX idx_article_cv ON article (category_id, views);

我们再次执行查看执行结果:
这里写图片描述
✈ 分析:可以看到,type变为了ref,Extra中的Using filesort也消失了,结果非常理想。

2. 单表

2.1 建表SQL

CREATE TABLE `book` ( 
  `id` int(11) NOT NULL, 
  `card` varchar(255) DEFAULT NULL, 
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
 
CREATE TABLE `class` ( 
  `id` int(11) NOT NULL, 
  `card` varchar(255) DEFAULT NULL, 
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.2 案例分析

查询两表中card相等的数据

EXPLAIN SELECT 
    * 
FROM 
    class 
LEFT JOIN book ON class.card = book.card

这里写图片描述
结论:type为ALL

2.3 开始优化

2.3.1 创建索引 idx_book_card

这里创建索引有两种方式,我们来看哪种方式更加合适,那么我们先在book表中创建索引idx_book_card

ALTER TABLE book ADD INDEX idx_book_card(card) 

那么我们再来执行一下,看看测试结果:
这里写图片描述

2.3.2 创建索引 idx_class_card

删除book表的索引,然后创建class表的索引idx_class_card

DROP INDEX idx_book_card ON book;  
ALTER TABLE class ADD INDEX idx_class_card(card)

那么我们再来执行一下,看看测试结果:

这里写图片描述
✈ 分析:我们之前的博客说过,ref 要好于 index,所以经过实践证明,使用book表的idx_book_card索引更加高效。这是由左连接特性决定的,left join条件用于确定如何从右表搜索行,左边一定都有,所以右表是我们的关键点,一定需要建立索引。需要注意的是,如果条件不允许,只能在左表存在索引,那么我们可以使用右连接,交换两表位置,达到相同的效果。

3. 三表

3.1 建表SQL

在上面的基础上,再创建一个表,SQL语句如下:

CREATE TABLE `phone` ( 
  `id` int(11) NOT NULL, 
  `card` varchar(255) DEFAULT NULL, 
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3.2 案例分析

查询三表中card相等的数据

EXPLAIN SELECT 
    * 
FROM 
    class 
LEFT JOIN book ON class.card = book.card 
LEFT JOIN phone ON book.card = phone.card

这里写图片描述
结论:type为ALL

2.3 开始优化

2.3.1 创建索引 idx_book_card 和 idx_phone_card

ALTER TABLE phone ADD INDEX idx_phone_card(card) 
ALTER TABLE book ADD INDEX idx_book_card(card)

优化后的执行结果为:
这里写图片描述
✈ 分析:type都是ref且总rows优化很好,效果不错。因此索引最好设置在需要经常查询的字段中。

总结: JOIN语句的优化

  • 尽可能减少join语句中的NestedLoop的循环总次数:“永远用小结果集驱动大的结果集”。
  • 优先优化NestedLoop的内层循环
  • 保证join语句中被驱动表上Join条件字段已经被索引
  • 当无法保证被驱动表的JOIN条件字段被索引且内存资源充足的前提下,不要太吝啬JoinBuffer的设置。

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/3921.html

(0)
上一篇 2021年7月16日
下一篇 2021年7月16日

相关推荐

发表回复

登录后才能评论