【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中设置。
相关文章推荐
- mysql性能优化-慢查询分析、优化索引和配置
- 索引的分析和优化(基于MySQL)
- mysql性能优化-慢查询分析、优化索引和配置
- 由浅入深探究mysql索引结构原理、性能分析与优化
- 由浅入深探究mysql索引结构原理、性能分析与优化
- 由浅入深探究mysql索引结构原理、性能分析与优化
- MySQL索引分析和优化(转)
- Mysql 索引与优化 加锁处理分析
- [置顶] MySQL 索引分析和优化
- mysql性能优化-慢查询分析、优化索引和配置
- MySQL索引优化分析
- MySQL索引分析和优化
- MYSQL索引结构原理、性能分析与优化
- mysql性能优化-慢查询分析、优化索引和配置
- 由浅入深探究mysql索引结构原理、性能分析与优化
- MySQL索引分析和优化
- mysql性能优化-慢查询分析、优化索引和配置
- mysql性能优化-慢查询分析、优化索引和配置
- 由浅入深探究mysql索引结构原理、性能分析与优化
- MySQL 索引分析和优化