2.5 优化分析
2.5.1 索引单表优化
数据准备
DROP TABLE IF EXISTS `article`; CREATE TABLE IF NOT EXISTS `article`( `id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '主键', `author_id` INT(10) UNSIGNED NOT NULL COMMENT '作者id', `category_id` INT(10) UNSIGNED NOT NULL COMMENT '分类id', `views` INT(10) UNSIGNED NOT NULL COMMENT '被查看的次数', `comments` INT(10) UNSIGNED NOT NULL COMMENT '回帖的备注', `title` VARCHAR(255) NOT NULL COMMENT '标题', `content` VARCHAR(255) NOT NULL COMMENT '正文内容' ) COMMENT '文章'; INSERT INTO `article`(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES(1,1,1,1,'1','1'); INSERT INTO `article`(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES(2,2,2,2,'2','2'); INSERT INTO `article`(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES(3,3,3,3,'3','3'); INSERT INTO `article`(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES(1,1,3,3,'3','3'); INSERT INTO `article`(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES(1,1,4,4,'4','4');
案例:查询category_id为1且comments大于1的情况下,views最多的article_id。
1、编写SQL语句并查看SQL执行计划。
# 1、sql语句 SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1; # 2、sql执行计划 mysql> EXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: article partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 5 filtered: 20.00 Extra: Using where; Using filesort # 产生了文件内排序,需要优化SQL 1 row in set, 1 warning (0.00 sec)
2、创建索引idx_article_ccv。
CREATE INDEX idx_article_ccv ON article(category_id,comments,views);
3、查看当前索引。
show index from 表名;
4、查看现在SQL语句的执行计划。
我们发现,创建符合索引idx_article_ccv之后,虽然解决了全表扫描的问题,但是在order by排序的时候没有用到索引,MySQL居然还是用的Using filesort,为什么?
5、
我们试试把SQL修改为SELECT id,author_id FROM article WHERE category_id = 1 AND comments = 1 ORDER BY views DESC LIMIT 1;
看看SQL的执行计划。
推论:当comments > 1的时候order by排序views字段索引就用不上,但是当comments = 1的时候order by排序views字段索引就可以用上!!!
所以,范围之后的索引会失效。
6、
我们现在知道范围之后的索引会失效,原来的索引idx_article_ccv最后一个字段views会失效,那么我们如果删除这个索引,创建idx_article_cv索引呢????
/* 创建索引 idx_article_cv */ CREATE INDEX idx_article_cv ON article(category_id,views);
查看当前的索引
show index from 表名;
7、当前索引是idx_article_cv,来看一下SQL执行计划。
2.5.2 索引两表优化
建立两张表
CREATE TABLE IF NOT EXISTS class( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, card INT(10) UNSIGNED NOT NULL, PRIMARY KEY(id) ); CREATE TABLE IF NOT EXISTS book( bookid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, card INT(10) UNSIGNED NOT NULL, PRIMARY KEY(bookid) ); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
两表连接查询的SQL执行计划
1、不创建索引的情况下,SQL的执行计划。
book和class两张表都是没有使用索引,全表扫描,那么如果进行优化,索引是创建在book表还是创建在class表呢?下面进行大胆的尝试!
2、左表(book表)创建索引。
创建索引idx_book_card
/* 在book表创建索引 */ CREATE INDEX idx_book_card ON book(card);
在book表中有idx_book_card索引的情况下,查看SQL执行计划
3、删除book表的索引,右表(class表)创建索引。
创建索引idx_class_card
/* 在class表创建索引 */ CREATE INDEX idx_class_card ON class(card);
在class表中有idx_class_card索引的情况下,查看SQL执行计划
由此可见,左连接将索引创建在右表上更合适,右连接将索引创建在左表上更合适
所以:索引两表优化,左连接右表建索引,右连接左表建索引
2.5.3 索引三表优化
建立三张表:
(在前面中有两张表,现在多一个表即可)
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)));
三表连接查询SQL优化
1、不加任何索引,查看SQL执行计划。
2、根据两表查询优化的经验,左连接需要在右表上添加索引,所以尝试在book表和phone表上添加索引。
/* 在book表创建索引 */ CREATE INDEX idx_book_card ON book(card); /* 在phone表上创建索引 */ CREATE INDEX idx_phone_card ON phone(card);
再次执行SQL的执行计划
2.5.4 Join语句的优化
尽可能减少Join语句中的NestedLoop的循环总次数:“永远用小结果集驱动大的结果集”。
优先优化NestedLoop的内层循环
保证Join语句中被驱动表上Join条件字段已经被索引。
当无法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝惜JoinBuffer的设置。