查询语句写的烂
索引失效
create index idx_user_name on user(name) 做索引之前是杂乱无章的,做之后是有顺序的
单值索引 某表的某个字段做的索引
create index idx_user_nameEmail on user(name,email)
复合索引 某表的多个字段做的索引
关联查询有太多的join (设计缺陷或不得已的需求)
服务器调优及各个参数的配置(缓冲,线程数等)
sql的执行顺序
手写:
机读 : from..on.. ..join.. where... group by.... having....select distinct ... order by ... limit....
索引的本质是一种数据结构,帮助mysql高效获取数据
索引的目的在于提高查询效率
排好序的快速查找数据结构,索引会影响where后面的查找,和order by 后面的排序的两大功能
在数据之外,数据库系统还维护着特定的查找算法的数据结构,这种数据结构以某种方式指向数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引
维护一个二叉查找树
数据删除没有真正delete,只是修改为未激活状态。尽量不删,为了数据分析,第二 为了索引。
数据频繁修改,索引需要重建。
数据修改不仅要改数据本身,而且要修改索引的指向,所以会慢
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引通常以文件形式存储在磁盘上。
提高了检索的效率,降低了io成本
对数据排序,降低了数据排序成本,降低了运算的CPU损耗
索引也是一张表,保存了主键与索引字段,并指向实体表的记录,索引列也是占空间的
会降低更新表的速度
若mysql有大数据量的表,就需要花时间建立最优秀的索引,索引不是一朝一夕的建立,需要根据业务不断变更。
一张表建的索引最多不要超过五个
一个索引只包含单个列,一个表可以有多个单列索引
索引列的值必须唯一,可以允许有空值
一个索引包含多个列
create [unique] index 索引名 on 表名(字段,字段)
alter 表名 add [unique] index 索引名 on (字段,字段)
drop index 索引名 on mytable;
show index from 表名
初始化介绍:
一颗B+树,一个磁盘块可以有集合数据项和指针
真实的数据存在于叶子节点
非叶子节点值不存储真实 的数据,值存储指引搜索方向的数据项
查找过程:
若想查询28,则先把磁盘一加载到内存,发生一次io,在内粗中二分查找确定数据位置,锁定指针(内存时间很短)
磁盘一的指针所指向的磁盘地址吧磁盘3由磁盘加载到内存中,发生第二次io,二分查找并锁定指针。
磁盘三的指针所指向的此岸加载到内存,发生第三次io,同时二分查找
结束查询,3次io
真实情况:3层的B+树,可以表示上百万的数据
1.负责优化select 语句的优化器,通过分析统计信息,为客户端提供他认为的最优的执行计划
2.首先对整条query进行优化,处理一些常量表达式的预算转化为常量,并对查询条件进行简化和转换,去除一些显著且无用的条件,结构调整等
使用explain关键字可以模拟优化器查询语句,从而知道mysql时如何处理你的sql语句。
explain select* from books;
表的读取顺序
数据读取操作的操作类型
哪些索引可以使用
哪些索引被实际使用
表之间的引用
每张表多少行被优化器查询
explain + sql
执行计划包含的信息:
id select_type table type possible_keys key key_len ref rows extra
select查询的序列号,包含一组数字,表示查询select 子句或操作表的顺序
三种情况:
id相同,执行顺序从上到下
id不同,如果是子查询,id序号会递增,id越大优先级越高,越先执行
id相同不同,同时存在 数字大先执行,平级顺序执行
derived[id] 衍生表
常见值:
查询的类型,主要是用于区别不同查询,联合查询,子查询等复杂查询
显示着一行的数据时关于哪张表的
访问类型排序
从最好到最差依次是
system const re_ref ref range index all
若type出all,且数据量百万之上,请一定优化,表名检索全部扫描
一般来说,得保证查询至少达到range级别,最好到ef。
邮件 红色圈圈标出
这条sql跑了一下explqin分析,会出现全表扫描的情况
经过优化:修改为 效果变成。
显示可能应用在这种表的索引,一个或多个
显示涉及到字段上若存在索引,则该索引将被列出,但不一样被使用
实际使用的索引,若为null则没有使用索引或索引失效
查询中若使用了覆盖索引,则该索引仅出现在key列表中
覆盖索引:就是select查询的字段和建的符合索引的个数和顺序吻合。
理论上没用到,实际上也没有。
理论上没用到,实际上用到了。覆盖索引…
理论上应该用,实际是没用到。两个表连接时…
表示索引使用的字节数,在不损失精度的情况下,长度约断越好,值为索引字段最大可能长度,而不是使用长度,计算得出的。
同样查询结果条件下,精度约小越好
显示索引的那一列被使用了,若是一个常数,哪些列表和常量则被用于查询索引列上的值
根据表统计信息及索引选用情况,大致法估算出找到所需的记录所需要读取的行数,越少越好
包含不适合在其他列但是又非常重要的信息
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 VARCHAR(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,1,1,3,'3','3');
#查询category_id=1 且comments 大于1的情况下,views最多的author_id
select author_id from article where category_id=1 and comments > 1 order by views desc limit 1;
建立索引,这里comments为范围,若建立ccv索引,会导致comments及以后字段索引失效,故隔过comments
create index idx_article_cv on article(category_id,views);
create table if not exists class(
id int UNSIGNED primary key not null auto_increment,
card int UNSIGNED not null);
create table if not exists book(
bookid int UNSIGNED primary key not null auto_increment,
card int UNSIGNED not null);
-- 随机插入数据
insert into book(card) values(floor(1+(rand()*20)));
insert into class(card) values(floor(1+(rand()*20)));
-- 索引应该是加在左表还是右表
select * from class left join book on class.card = book.card;
-- 左连接相反加,右连接也是相反加
-- 左连接特性,左边全有,则重点就是在右边搜索不满足条件的行。故需要在右边添加索引
create index idx_book_card on book(card);
explain select * from class left join book on class.card = book.card;
create table if not exists phone(
phoneid int unsigned not null primary key auto_increment,
card int UNSIGNED not null
);
insert into phone(card) values(floor(1+(rand()*20)));
drop index idx_book_card on book;
select * from class left join book
explain select * from class LEFT join book on book.card = class.card left join phone on phone.card = book.card;
create index idx_book_card on book(card);
create index idx_phone_card on phone(card);
尽可能减少join语句中nestedloop的循环总次数,永远用小结果集驱动大结果集
优先优化nestedloop的内层循环
保证join语句中被驱动表上join条件字段已经被索引
当无法保证被签到表的join条件字段被索引且内存资源充足的前提下,不要太吝惜joinbuffer的设置
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());
select * from staffs;
create index idx_staffs_nameAgePos on staffs(name,age,pos);
如果索引了多列,要遵循此法则,查询从索引的最左前列开始并且不跳过索引中的列。
带头大哥不能死,中间兄弟不能断。
索引列上不计算
范围之后全失效
like%加右边
若非使用%%,则使用覆盖索引。
字符串里有引号
mysql会对不添加单引号的数字进行隐式转换,违反第三条规则
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');
order by c3 c2;会出现文件内排序
但是 where c2 = ‘1’ order by c3 c2,不会出现因为这里c2为常量值则不参与排序了
group by表面上是分组,但是分组之前必排序,和order by排序的法则和索引优化原则是一致的
group by c3,c2 会导致临时文件产生,还有文件内排序
对于单键索引,尽量选择针对当前query过滤性更好的索引
在选择组合索引的时候,当前query中过滤性最好的字段在索引字段顺序中,位置越靠前越好
在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更多的字段的索引
尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的