您的位置:首页 > 数据库 > MySQL

【MySQL】索引分析与优化

2017-08-20 22:06 288 查看
     

        本篇博客主要从单表,两表,三表中分析索引的相关问题。

 

一、单表

建表如下:

CREATE TABLE IF NOT EXISTS `article` (
`id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`author_id` INT(10) UNSIGNED NOT NULL,
`category_id` INT(10) UNSIGNED NOT NULL,
`views` INT(10) UNSIGNED NOT NULL,
`comments` INT(10) UNSIGNED NOT NULL,
`title` VARBINARY(255) NOT NULL,
`content` TEXT NOT NULL
);
INSERT INTO `article`(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES
(1, 1, 1, 1, '1', '1'),
(2, 2, 2, 2, '2', '2'),
(1, 1, 3, 3, '3', '3');
执行查询:



分析:



查询索引:



除了主键,没有其他索引

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

【开始优化】

# 新建索引

#ALTER TABLE `article` ADD INDEX idx_article_ccv (`category_id` , `comments`, `views` );

create index idx_article_ccv onarticle(category_id,comments,views);



再次分析

mysql> EXPLAIN SELECT id,author_idFROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESCLIMIT 1;



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



#结论:

建立索引后,type 变成了 range,这是可以忍受的。但是 extra 里使用 Using filesort 仍是无法接受的。但是我们已经建立了索引,为啥没用呢?

根据 BTree 索引的工作原理, 先排序category_id, 如果遇到相同的 category_id 则再排序 comments,如果遇到相同的 comments 则再排序 views。

#当 comments 字段在联合索引里处于中间位置时,因comments > 1 条件是一个范围值(所谓 range),MySQL 无法利用索引再对后面的 views 部分进行检索,即 range 类型查询字段后面的索引无效。

【再次优化】

# 删除第一次建立的ccv索引

DROP INDEX idx_article_ccv ON article;

# 建立cv索引

#ALTER TABLE `article` ADD INDEX idx_article_cv ( `category_id` ,`views` ) ;

create index idx_article_cv on article(category_id,views);



分析



#结论:可以看到,type
变为了 ref,Extra
中的 Using filesort
也消失了,结果非常理想。

二、两表

# 下面开始explain分析

EXPLAIN SELECT * FROM class LEFT JOINbook ON class.card = book.card;



#结论:type 有All

 

# 添加索引优化

问题:在哪个表上哪个字段上添加好呢?下面分别添加一下,看看效果

 

#第1次,在book的card上建

ALTER TABLE `book` ADD INDEX Y (`card`);



执行分析:sql为左连接,在右表上加了索引



EXPLAIN SELECT * FROM class LEFT JOINbook ON class.card = book.card;

可以看到第二行的 type 变为了 ref,rows 也变成了优化比较明显。

这是由左连接特性决定的。

LEFT JOIN 条件用于确定如何从右表搜索行,左边一定都有,所以右边是我们的关键点,一定需要建立索引。

 

# 第2次,删除旧索引 +新建 + explain

DROP INDEX Y ON book;

ALTER TABLE class ADD INDEX X (card);

EXPLAIN SELECT * FROM class LEFT JOINbook ON class.card = book.card;



结果看到,和没有建立索引差不多。

 

# 然后来看一个右连接查询:

#优化较明显。这是因为 RIGHTJOIN 条件用于确定如何从左表搜索行,右边一定都有,所以左边是我们的关键点,一定需要建立索引。

EXPLAIN SELECT * FROM class RIGHT JOINbook ON class.card = book.card;

DROP INDEX X ON class;

ALTER TABLE book ADD INDEX Y (card);

# 右连接,基本无变化

EXPLAIN SELECT * FROM class RIGHT JOINbook ON class.card = book.card;

 

连接查询建索引结论:左连右表加,右连左表加。


三、三表

CREATE TABLE IF NOT EXISTS `phone` (
`phoneid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`card` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`phoneid`)
) ENGINE = INNODB;

INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));


# 接下来给表建立索引
ALTER TABLE `phone` ADD INDEX z ( `card`);

ALTER TABLE `book` ADD INDEX Y ( `card`);
EXPLAINSELECT * FROM class LEFT JOIN book ON class.card=book.card LEFT JOIN phone ONbook.card = phone.card;



# 后 2 行的 type 都是 ref 且总 rows 优化很好,效果不错。因此索引最好设置在需要经常查询的字段中。
【结论】
1.尽可能减少Join语句中的NestedLoop的循环总次数:“永远用小结果集驱动大的结果集”。Join语句的优化,优先优化NestedLoop的内层循环;

2.保证Join语句中被驱动表上Join条件字段已经被索引;

当无法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝惜JoinBuffer的设置;此属性在my.cnf中设置。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: