概述
高级MySQL
高级MySQL包括:MySQL内核、sql优化攻城狮、mysql服务器的优化、各种参数常量设定、查询语句优化、主从复制、软硬件升级、容灾备份、sql编程
参考此篇文章
二进制日志log-bin
# Replication Master Server (default)
# binary logging is required for replication
log-bin=mysql-bin
错误日志log-error
默认是关闭的。记录严重的警告和错误信息,每次启动和关闭的详细信息等
查询日志log
默认关闭,记录查询的sql语句,如果开启会减低mysqI的整体性能,因为记录日志也是需要消耗系统资源的
数据文件
总体概览
连接层
最上层是一些客户端和连接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于tcp/ip的通信。主要完成-些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
服务层
第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化及部分内置函数的执行。所有跨存储引擎的功能也在这一层实现, 如过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定查询表的顺序,是否利用索引等,最后生成相应的执行操作。如果是select语句,服务器还会查询内部的缓存。如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。
引擎层
存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信。不同的存储引擎具有的功能不同,这
样我们可以根据自己的实际需要进行选取。后面介绍MyISAM和InnoDB
存储层
数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
查看存储引擎命令
mysql> show engines;
mysql> show variables like '%storage_engine%';
MyISAM和InnoDB
阿里、淘宝用的存储引擎
性能下降SQL慢、执行时间长、等待时间长
原因:
SQL的执行顺序
手写SQL
SELECT DISTINCT
<select_list>
FROM
<left_table> <join_type>
JOIN <right_table> ON <join_condition>
WHERE
<where_condition>
GROUP BY
<group_by_list>
HAVING
<having_condition>
ORDER BY
<order_by_condition>
LIMIT <limit_number>
机器读取的SQL
FROM
<left_table> <join_type>
JOIN <right_table> ON <join_condition>
WHERE
<where_condition>
GROUP BY
<group_by_list>
HAVING
<having_condition>
SELECT
DISTINCT <select_list>
ORDER BY
<order_by_condition>
LIMIT <limit_number>
总结
七种Join图
测试7种Join
建表sql
USE db01;
CREATE TABLE tbl_dept(
id INT(11) NOT NULL AUTO_INCREMENT,
deptName VARCHAR(30) DEFAULT NULL,
locAdd VARCHAR(40) DEFAULT NULL,
PRIMARY KEY(id)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE tbl_emp(
id int(11) NOT NULL AUTO_INCREMENT,
name VARCHAR(20) DEFAULT NULL,
deptId INT(11) DEFAULT NULL,
PRIMARY KEY(id),
KEY fk_dept_id(deptId)
#CONSTRAINT 'fk_dept_id' FOREIGN KEY('deptId) REFERENCES 'tbl_dept'('id)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO tbl_dept(deptName,locAdd) VALUES('RD',11);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('HR',12);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('MK',13);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('MIS',14);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('FD',15);
INSERT INTO tbl_emp(NAME,deptId) VALUES('z3',1);
INSERT INTO tbl_emp(NAME,deptId) VALUES('z4',1);
INSERT INTO tbl_emp(NAME,deptId) VALUES('z5',1);
INSERT INTO tbl_emp(NAME,deptId) VALUES('w5',2);
INSERT INTO tbl_emp(NAME,deptId) VALUES('w6',2);
INSERT INTO tbl_emp(NAME,deptId) VALUES('s7',3);
INSERT INTO tbl_emp(NAME,deptId) VALUES('s8',4);
INSERT INTO tbl_emp(NAME,deptId) VALUES('s9',51);
inner join
只查询两个共有的,其余的不显示
left join
左表都查询,右表没有对应的就置空
right join
右表都查询,左表没有对应的就置空
right join where left.id is null
查询右表独有的数据
left join where right.id is null
查询左表独有的数据
full outer join
说明,此连接对mysql无效,对Oracle有用,但是为了达到效果,还是要编写sql
# union去重+合并
select * from tbl_emp a left join tbl_dept b on a.deptId = b.id
union
select * from tbl_emp a right join tbl_dept b on a.deptId = b.id;
只显示两个独有的
select * from tbl_emp a left join tbl_dept b on a.deptId = b.id where b.id is null
union
select * from tbl_emp a right join tbl_dept b on a.deptId = b.id where a.deptId is null;
MySQL官方定义:索引是帮助MySQL高效获取数据的数据结构
索引的本质:数据结构
索引是排好序的快速查找的数据结构 => 索引的作用:排序和快速查找
在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构就是索引。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上
一种索引方式示例:
我们平常所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉的)结构组织的索引。其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一 索引默认都是使用B+树索引,统称索引。当然,除了B+树这种类型的索引之外,还有哈稀索引(hash index)等。
优势
劣势
单值索引:一个索引只包含单个列,一个表可以有多个单列索引
建议:一张表建的索引最多不要超过5个
唯一索引:索引列的值必须唯一,单允许空值
复合索引:即一个索引包含多个列
基本语法
创建
CREATE [UNIQUE] INDEX indexName ON mytable(columnname(length));
ALTER mytable ADD [UNIQUE] INDEX [indexName] ON (columnname(length))
删除
DROP INDEX [indexName] on mytable;
查看
SHOW INDEX FROM table_name;
ALTER命令
有四种方式来添加数据表的索引:ALTER TABLE tbl_name ADD PRIMARY KEY (column_list); 该语句添加一个主键,这意味着索引值必须是唯一-的, 且不能为NULL。ALTER TABLE tbl_name ADD UNIQUE index_name (column_list); 这条语句创建索引的值必须是唯一的 (除了NULL外,NULL可能会出现多次)。ALTER TABLE tbl_name ADD INDEX index_name (column_list); 添加普通索引,索引值可出现多次。ALTER TABLE tbl name ADD FULLTEXT index_name (column_list); 该语句指定了索引为FULLTEXT,用于全文索引。
BTree索引
检索原理
【初始化介绍】
一颗b+树,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35, 包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。
真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。
非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、 35并不真实存在于数据表中。
【查找过程】
如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用分查找确定29在17间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的I0)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内有,发生第二次IO,29在26和30之间, 锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。
真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。
【其他类型索引】
哪些情况需要创建索引
哪些情况不要创建索引
MySQL Query Optimizer
MySQL常见瓶颈
Explain是什么(查看执行计划)
Explain的作用
Explain的用法
用法:Explain + SQL语句
执行计划包含的信息
执行计划包含的信息的字段解释
id:select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序。分三种情况:
select_type:查询的类型,主要用于区别普通查询、联合查询、子查询等的复杂查询
table:显示这一行的数据是关于哪些表的
type:type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是:system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>All,常用的访问类型排序:system>const>eq_ref>ref>range>index>All
system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计
const:表示通过索引一次就找到了,const用于比较primary key或则unique索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL就能将该查询转换为一个常量
eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
ref:非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体
range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引。一般就是在你的where语句中出现了between、<、>、in等的查询。这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不会扫描全部索引
index:Full Index Scan,index与All区别为index类型只遍历索引树。这通常比All快,因为索引文件通常比数据文件小。(也就是说虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)
all:Full Table Scan,将遍历全表以找到匹配的行。
一般来说,得保证查询至少达到range级别,最好能达到ref
possible_keys:显示可能应用在这张表中的索引,一个或多个。查询涉及到的字段上若存在索引,则该索引将被列出。但不一定被查询实际使用
key:实际使用的索引。如果为NULL,则没有使用索引。查询中若使用了覆盖索引,则该索引仅出现在key列表中,不会出现在possible_keys列表中。(覆盖索引:查询的字段与建立的复合索引的个数一一吻合)
key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好。
key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的
ref:显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。查询中与其它表关联的字段,外键关系建立索引
rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
Extra:包含不适合在其他列中显示但十分重要的额外信息
Using filesort:说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作成为“文件排序”
Using temporary:使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询group by
Using index:表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错!如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表明索引用来读取数据而非执行查找动作
Using where:表明使用了where过滤
Using join buffer:使用了连接缓存
impossible where:where子句的值总是false,不能用来获取任何元组。(查询语句中where的条件不可能被满足,恒为False)
select tables optimized away:在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化
distinct:优化distinct操作,在找到第一匹配的元组后即停止找相同值的动作
热身Case
写出下列sql的执行顺序
结果:
单表
建表SQL
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'),(3,3,3,3,'3','3'); SELECT * FROM ARTICLE;
案例
查询category_ id 为1且comments大于1的情况下,views最多的article_ id。
-- 查询sqlSELECT id,author_id FROM article WHERE category_id=1 AND comments>1 ORDER BY views DESC LIMIT 1;-- 分析性能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,也是最坏的情况。优化是必须的。
开始优化
添加索引
# 添加索引create index index_article_ccv on article(category_id,comments,views);# 继续查看索引性能EXPLAIN SELECT id,author_id FROM 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类型查询字段后面的索引无效。
删除第一次的索引,第二次新建索引,这次的索引不包含range类型查询所涉及的字段
# 删除索引drop index index_article_ccv on article;# 创建新的索引create index index_article_ccv on article(category_id,views);# 查看查询的性能EXPLAIN SELECT id,author_id FROM article WHERE category_id=1 AND comments>1 ORDER BY views DESC LIMIT 1;
结论:可以看到,type变成了ref,Extra中的Using filesort也消失了,结果还是非常理想的。
删除测试的索引
两表
建表sql
CREATE TABLE IF NOT EXISTS `class`(`id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,`card` INT (10) UNSIGNED NOT NULL);CREATE TABLE IF NOT EXISTS `book`(`bookid` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,`card` INT (10) UNSIGNED NOT NULL);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)));
案例
# 开始explain分析explain select * from book left join class on book.card = class.card;
结论:type有ALL,不是很理想
添加索引优化
alter table class add index Y(card);
explain性能分析
结论:可以看到第二行的type变为ref,rows也变成了优化比较明显。
原因:这是由于左连接特性决定的,LEFT JOIN条件用于确定如何从右表搜索行,左边一定都有,所有右边是我们的关键点,所以所有要在右表建立。同理,右连接的关键在左边,所以索引要在左边建立。
总结:左连接建右表,右连接建左表。理由:以左连接为例,左表的信息全都有,所以右表需要查找,所以建立右表index
三表
建表SQL(在二表的基础上,清除所有索引)
CREATE TABLE IF NOT EXISTS `phone`(`phoneid` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,`card` INT (10) UNSIGNED NOT NULL)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)));
案例
explain select * from class left join book on book.card = class.card left join phone on book.card = phone.card;
分析:sql都是左连接,class是全部加载的,索引book需要一个索引,继续左连接phone,phone是右表也需要一个索引
实施:
create index Y on phone(card); create index Y on book(card); explain select * from class left join book on book.card = class.card left join phone on book.card = phone.card;
结论:后两行的type都是ref且总rows优化很好,效果不错。因此所有最好设置在需要经常查询的字段中。
Join语句的优化
建表SQL
CREATE TABLE staffs(id INT PRIMARY KEY AUTO_INCREMENT,`name` VARCHAR(24)NOT NULL DEFAULT'' COMMENT'姓名',`age` INT NOT NULL DEFAULT 0 COMMENT'年龄',`pos` VARCHAR(20) NOT NULL DEFAULT'' COMMENT'职位',`add_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'入职时间')CHARSET utf8 COMMENT'员工记录表';insert into staffs(NAME,age,pos,add_time) values('z3',22,'manager',NOW());insert into staffs(NAME,age,pos,add_time) values('July',23,'dev',NOW());insert into staffs(NAME,age,pos,add_time) values('2000',23,'dev',NOW());ALTER TABLE staffs ADD INDEX index_staffs_nameAgePos(`name`,`age`,`pos`);
索引失效案例
全值匹配我最爱
建立几个复合索引字段,最好就用上几个字段。且按照顺序来用
最佳左前缀法则
如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。最前缀法则,必须有车头,中间车厢不能断
不在索引列上作任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
存储引擎不能使用索引中范围条件右边的列
尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select
mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
is null,is not null 也无法使用索引
like以通配符开头(‘%abc…’)mysql索引失效会变成全表扫描的操作
问题:解决like ‘%字符串%’时索引不被使用的方法
案例
建表SQL
CREATE TABLE tbl_user(`id` INT(11) NOT NULL AUTO_INCREMENT,`name` VARCHAR(20) DEFAULT NULL,`age`INT(11) DEFAULT NULL,`email` VARCHAR(20) DEFAULT NULL,PRIMARY KEY(`id`))ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;insert into tbl_user(NAME,age,email) values('1aa1',21,'[email protected]');insert into tbl_user(NAME,age,email) values('2aa2',222,'[email protected]');insert into tbl_user(NAME,age,email) values('3aa3',265,'[email protected]');insert into tbl_user(NAME,age,email) values('4aa4',21,'[email protected]');# 创建索引create index index_nameAge on tbl_user(name,age);
经过测试得到结论:利用覆盖索引解决两边%的优化问题
explain select * from tbl_user where name like '%aa%';explain select id,name,age from tbl_user where name like '%aa%';explain select id,name,age,email from tbl_user where name like '%aa%';
字符串不加单引号会索引失效
结论:都能查询出结果,但是不带单引号的会导致索引失效,原因是不带单引号的mysql底层会做一个隐式的类型转换。
少用or,用它连接是会索引失效
小结
全值匹配我最爱,最左前缀要遵守
带头大哥不能丢,中间兄弟不能断
索引列上少计算,范围之后全失效
like百分写最右, 覆盖索引不写星
不等空值还有or,索引失效要少用
var引号不能丢,SQL高级也不难
小总结:
建表SQL
create table test03(id int primary key not null auto_increment,c1 char(10),c2 char(10),c3 char(10),c4 char(10),c5 char(10));insert into test03(c1,c2,c3,c4,c5) values ('a1','a2','a3','a4','a5');insert into test03(c1,c2,c3,c4,c5) values ('b1','b2','b3','b4','b5');insert into test03(c1,c2,c3,c4,c5) values ('c1','c2','c3','c4','c5');insert into test03(c1,c2,c3,c4,c5) values ('d1','d2','d3','d4','d5');insert into test03(c1,c2,c3,c4,c5) values ('e1','e2','e3','e4','e5');create index idx_test03_c1234 on test03(c1,c2,c3,c4);
分析sql
explain select * from test03 where c1 = 'a1';explain select * from test03 where c1 = 'a1' and c2 = 'a2';explain select * from test03 where c1 = 'a1' and c2 = 'a2' and c3 = 'a3';explain select * from test03 where c1 = 'a1' and c2 = 'a2' and c3 = 'a3' and c4 = 'a4';# 查询顺序与首页创建的不一样explain select * from test03 where c2 = 'a2' and c1 = 'a1' and c3 = 'a3' and c4 = 'a4';# 查询顺序与首页创建的不一样explain select * from test03 where c4 = 'a4' and c3 = 'a3' and c2 = 'a2' and c1 = 'a1';
结论:当查询为常量查询,且查询顺序与索引创建的顺序不一样,由图可知,同样也使用了索引。原因:MySQL在服务层的时候会自动把SQL进行优化,会根据我们所有创建的顺序来优化查询条件的顺序。
explain select * from test03 where c1 = 'a1' and c2 = 'a2' and c4 > 'a4' and c3 = 'a3';explain select * from test03 where c1 = 'a1' and c2 = 'a2' and c3 > 'a3' and c4 = 'a4';
explain select * from test03 where c1 = 'a1' and c2 = 'a2' and c4 = 'a4' order by c3;explain select * from test03 where c1 = 'a1' and c2 = 'a2' order by c3;
结论:c3的作用在排序而不是查找,用到了但是没有统计在结果中
explain select * from test03 where c1 = 'a1' and c2 = 'a2' order by c4;
结论:出现了Using filesort,原因:索引的使用跳过了c3,自己创建的索引不能达到需求,索引有了文件排序
explain select * from test03 where c1 = 'a1' and c5 = 'a5' order by c2,c3;
结论:只用c1一个字段索引,但是c2、c3用于排序且按照索引创建顺序来使用,所以无filesort
explain select * from test03 where c1 = 'a1' and c5 = 'a5' order by c3,c2;
结论:出现了filesort,我们建的索引是1234,它没有按照顺序来,3和2颠倒了,所以出现filesort
explain select * from test03 where c1 = 'a1' and c2 = 'a2' and c5 = 'a5' order by c3,c2;
结论:本例有常量c2的情况,因此排序就相当于order by c3,常量,所以没有出现filesort的情况
explain select * from test03 where c1 = 'a1' and c4 = 'a4' group by c2,c3;explain select * from test03 where c1 = 'a1' and c4 = 'a4' group by c3,c2;
结论:第一句sql用到了3个索引,一个用于查询,2个用于排序
第二局sql 用到了一个索引,用于查询,用于排序的字段与索引创建的顺序不同,所以出现了filesort
其中group by基本上都需要排序的,可能还会有临时表产生
- 定值、范围还是排序,一般order by是给个范围
- group by基本上都需要进行排序,会有临时表产生
- like后面看是否常量开头,是则使用索引,后续也能用,不是就断了