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';

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容