mysql优化-原理

原因:

性能低、执行时间太长、等待时间太长、SQL语句欠佳(连接查询)、索引失效、服务器参数设置不合理(缓冲、线程数)

SQL解析过程

编写过程:
selectdinstinct..from..join..on..where..groupby...having..orderby..limit..

解析过程:
from..on..join..where..groupby....having...selectdinstinct..orderbylimit...

索引

主要就是在优化索引
索引:相当于书的目录
索引:index是帮助MYSQL高效获取数据的数据结构。索引是数据结构(树:B树(默认)、Hash树...)

索引的弊端:
1.索引本身很大,可以存放在内存/硬盘(通常为硬盘)
2.索引不是所有情况均适用:a.少量数据b.频繁更新的字段c.很少使用的字段
3.索引会降低增删改的效率(增删改查)

索引的优势:
1.提高查询效率(降低IO使用率)
2.降低CPU使用率(...orderbyagedesc,因为B树索引本身就是一个好排序的结构,因此在排序时可以直接使用)

https://www.cnblogs.com/annsshadow/p/5037667.html

分类:
主键索引:不能重复。id不能是null
唯一索引:不能重复。id可以是null
单值索引:单列,age;一个表可以多个单值索引,name。
复合索引:多个列构成的索引(相当于二级目录:z:zhao)(name,age)(a,b,c,d,...,n)
创建索引:
方式一:create索引类型索引名on表(字段)
单值:
createindexdept_indexontb(dept);
唯一:
createuniqueindexname_indexontb(name);
复合索引
createindexdept_name_indexontb(dept,name);

方式二:altertable表名索引类型索引名(字段)
单值:
altertabletbaddindexdept_index(dept);
唯一:
altertabletbadduniqueindexname_index(name);
复合索引
altertabletbaddindexdept_name_index(dept,name);

注意:如果一个字段是primarykey,则改字段默认就是主键索引

删除索引:
dropindex索引名on表名;
dropindexname_indexontb;

查询索引:
showindexfrom表名;
showindexfrom表名\G

SQL性能问题

a.分析SQL的执行计划:explain,可以模拟SQL优化器执行SQL语句,从而让开发人员知道自己编写的SQL状况
b.MySQL查询优化其会干扰我们的优化

https://dev.mysql.com/doc/refman/5.5/en/optimization.html

查询执行计划:

explain+SQL语句
explainselect*fromtb;

id:编号
select_type:查询类型
table:表
type:类型
possible_keys:预测用到的索引
key:实际使用的索引
key_len:实际使用索引的长度
ref:表之间的引用
rows:通过索引查询到的数据量
Extra:额外的信息

准备数据:
createtablecourse
(
cidint(3),
cnamevarchar(20),
tidint(3)
);
createtableteacher
(
tidint(3),
tnamevarchar(20),
tcidint(3)
);

createtableteacherCard
(
tcidint(3),
tcdescvarchar(200)
);

insertintocoursevalues(1,'java',1);
insertintocoursevalues(2,'html',1);
insertintocoursevalues(3,'sql',2);
insertintocoursevalues(4,'web',3);

insertintoteachervalues(1,'tz',1);
insertintoteachervalues(2,'tw',2);
insertintoteachervalues(3,'tl',3);

insertintoteacherCardvalues(1,'tzdesc');
insertintoteacherCardvalues(2,'twdesc');
insertintoteacherCardvalues(3,'tldesc');

查询课程编号为2或教师证编号为3的老师信息
explain+sql:

(1)id:

id值相同,从上往下顺序执行。
t3-tc3-c4
tc3--c4-t6
表的执行顺序因数量的个数改变而改变的原因:笛卡儿积
abc
432=23=64=24
34=122=24
数据小的表优先查询;

id值不同:id值越大越优先查询
(本质:在嵌套子查询时,先查内层再查外层)
查询教授SQL课程的老师的描述(desc)
explainselecttc.tcdescfromteacherCardtc,coursec,teachertwherec.tid=t.tid
andt.tcid=tc.tcidandc.cname='sql';

将以上多表查询转为子查询形式:

explainselecttc.tcdescfromteacherCardtcwheretc.tcid=
(selectt.tcidfromteachertwheret.tid=
(selectc.tidfromcoursecwherec.cname='sql')
);

子查询+多表:
explainselectt.tname,tc.tcdescfromteachert,teacherCardtcwheret.tcid=tc.tcid
andt.tid=(selectc.tidfromcoursecwherecname='sql');

id值有相同,又有不同:id值越大越优先;id值相同,从上往下顺序执行

(2)select_type:查询类型

PRIMARY:包含子查询SQL中的主查询(最外层)
SUBQUERY:包含子查询SQL中的子查询(非最外层)
simple:简单查询(不包含子查询、union)
derived:衍生查询(使用到了临时表)
a.在from子查询中只有一张表
explainselectcr.cnamefrom(select*fromcoursewheretidin(1,2))cr;

b.在from子查询中,如果有table1uniontable2,则table1就是derived,table2就是union
explainselectcr.cnamefrom(selectfromcoursewheretid=1unionselectfromcoursewheretid=2)cr;
union:上例
unionresult:告知开发人员,那些表之间存在union查询

system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>ALL

(3)type:索引类型、类型

system>const>eq_ref>ref>range>index>all,要对type进行优化的前提:有索引
其中:system,const只是理想情况;实际能达到ref>range
system(忽略):只有一条数据的系统表;或衍生表只有一条数据的主查询

createtabletest01
(
tidint(3),
tnamevarchar(20)
);

insertintotest01values(1,'a');
commit;
增加索引
altertabletest01addconstrainttid_pkprimarykey(tid);
explainselectfrom(selectfromtest01)twheretid=1;

const:仅仅能查到一条数据的SQL,用于Primarykey或unique索引(类型与索引类型有关)
explainselecttidfromtest01wheretid=1;
altertabletest01dropprimarykey;
createindextest01_indexontest01(tid);

eq_ref:唯一性索引:对于每个索引键的查询,返回匹配唯一行数据(有且只有1个,不能多、不能0)
select...from..wherename=....常见于唯一索引和主键索引。

altertableteacherCardaddconstraintpk_tcidprimarykey(tcid);
altertableteacheraddconstraintuk_tciduniqueindex(tcid);

explainselectt.tcidfromteachert,teacherCardtcwheret.tcid=tc.tcid;

以上SQL,用到的索引是t.tcid,即teacher表中的tcid字段;
如果teacher表的数据个数和连接查询的数据个数一致(都是3条数据),则有可能满足eq_ref级别;否则无法满足。

ref:非唯一性索引,对于每个索引键的查询,返回匹配的所有行(0,多)
准备数据:
insertintoteachervalues(4,'tz',4);
insertintoteacherCardvalues(4,'tz222');

测试:
altertableteacheraddindexindex_name(tname);
explainselect*fromteacherwheretname='tz';

range:检索指定范围的行,where后面是一个范围查询(between,><>=,特殊:in有时候会失效,从而转为无索引all)
altertableteacheraddindextid_index(tid);
explainselectt.fromteachertwheret.tidin(1,2);
explainselectt.
fromteachertwheret.tid<3;

index:查询全部索引中数据
explainselecttidfromteacher;--tid是索引,只需要扫描索引表,不需要所有表中的所有数据

all:查询全部表中的数据
explainselectcidfromcourse;--cid不是索引,需要全表所有,即需要所有表中的所有数据

system/const:结果只有一条数据
eq_ref:结果多条;但是每条数据是唯一的;
ref:结果多条;但是每条数据是是0或多条;

(4)possible_keys:可能用到的索引,是一种预测,不准。

altertablecourseaddindexcname_index(cname);

explainselectt.tname,tc.tcdescfromteachert,teacherCardtc
wheret.tcid=tc.tcid
andt.tid=(selectc.tidfromcoursecwherecname='sql');

如果possible_key/key是NULL,则说明没用索引

explainselecttc.tcdescfromteacherCardtc,coursec,teachertwherec.tid=t.tid
andt.tcid=tc.tcidandc.cname='sql';

(5)key:实际使用到的索引

(6)key_len:索引的长度;

作用:用于判断复合索引是否被完全使用(a,b,c)。
createtabletest_kl
(
namechar(20)notnulldefault''
);
altertabletest_kladdindexindex_name(name);
explainselect*fromtest_klwherename='';--key_len:60
在utf8:1个字符站3个字节

altertabletest_kladdcolumnname1char(20);--name1可以为null

altertabletest_kladdindexindex_name1(name1);
explainselect*fromtest_klwherename1='';
--如果索引字段可以为Null,则会使用1个字节用于标识。

dropindexindex_nameontest_kl;
dropindexindex_name1ontest_kl;

增加一个复合索引
altertabletest_kladdindexname_name1_index(name,name1);

explainselectfromtest_klwherename1='';--121
explainselect
fromtest_klwherename='';--60

varchar(20)
altertabletest_kladdcolumnname2varchar(20);--可以为Null
altertabletest_kladdindexname2_index(name2);

explainselectfromtest_klwherename2='';--63
20
3=60+1(null)+2(用2个字节标识可变长度)=63

utf8:1个字符3个字节
gbk:1个字符2个字节
latin:1个字符1个字节

(7)ref:注意与type中的ref值区分。

作用:指明当前表所参照的字段。
select....wherea.c=b.x;(其中b.x可以是常量,const)

altertablecourseaddindextid_index(tid);

explainselect*fromcoursec,teachertwherec.tid=t.tidandt.tname='tw';

(8)rows:被索引优化查询的数据个数(实际通过索引而查询到的数据个数)

explainselect*fromcoursec,teachertwherec.tid=t.tid
andt.tname='tz';

(9)Extra:

(i).usingfilesort:性能消耗大;需要“额外”的一次排序(查询)。常见于orderby语句中。
排序:先查询

10个人根据年龄排序。

createtabletest02
(
a1char(3),
a2char(3),
a3char(3),
indexidx_a1(a1),
indexidx_a2(a2),
indexidx_a3(a3)
);

explainselect*fromtest02wherea1=''orderbya1;

a1:姓名a2:年龄

explainselect*fromtest02wherea1=''orderbya2;--usingfilesort
小结:对于单索引,如果排序和查找是同一个字段,则不会出现usingfilesort;如果排序和查找不是同一个字段,则会出现usingfilesort;
避免:where哪些字段,就orderby那些字段2

复合索引:不能跨列(最佳左前缀)
dropindexidx_a1ontest02;
dropindexidx_a2ontest02;
dropindexidx_a3ontest02;

altertabletest02addindexidx_a1_a2_a3(a1,a2,a3);
explainselectfromtest02wherea1=''orderbya3;--usingfilesort
explainselect
fromtest02wherea2=''orderbya3;--usingfilesort
explainselectfromtest02wherea1=''orderbya2;
explainselect
fromtest02wherea2=''orderbya1;--usingfilesort
小结:避免:where和orderby按照复合索引的顺序使用,不要跨列或无序使用。

(ii).usingtemporary:性能损耗大,用到了临时表。一般出现在groupby语句中。
explainselecta1fromtest02wherea1in('1','2','3')groupbya1;
explainselecta1fromtest02wherea1in('1','2','3')groupbya2;--usingtemporary
避免:查询那些列,就根据那些列groupby.

(iii).usingindex:性能提升;索引覆盖(覆盖索引)。原因:不读取原文件,只从索引文件中获取数据(不需要回表查询)
只要使用到的列全部都在索引中,就是索引覆盖usingindex

例如:test02表中有一个复合索引(a1,a2,a3)
explainselecta1,a2fromtest02wherea1=''ora2='';--usingindex

dropindexidx_a1_a2_a3ontest02;

altertabletest02addindexidx_a1_a2(a1,a2);
explainselecta1,a3fromtest02wherea1=''ora3='';

如果用到了索引覆盖(usingindex时),会对possible_keys和key造成影响:
a.如果没有where,则索引只出现在key中;
b.如果有where,则索引出现在key和possible_keys中。

explainselecta1,a2fromtest02wherea1=''ora2='';
explainselecta1,a2fromtest02;

(iii).usingwhere(需要回表查询)
假设age是索引列
但查询语句selectage,namefrom...whereage=...,此语句中必须回原表查Name,因此会显示usingwhere.

explainselecta1,a3fromtest02wherea3='';--a3需要回原表查询

(iv).impossiblewhere:where子句永远为false
explainselect*fromtest02wherea1='x'anda1='y';

你可能感兴趣的:(mysql优化-原理)