一.表结构优化
二.列类型优化
三.索引优化策略
四.聚簇索引和非聚簇索引
五.理想的索引
怎么查看一个sql语句的执行效果
在sql语句前面加上 “explain”,如果加上"\\\\G",则能看到竖列效果 explain sql \\\\G
结果中的 key_len 就是在查询上所用到的索引的个数; using filesort就是用到了排序的意思;using index是用到了索引覆盖
排序非常耗资源,想分组,必须排序
测试运行需要的时间 time file.php
如果A\\\\B两张表结构是一样的,把A表里面的数据插入B表,
可以这样执行:insert into B select * from A;
如果要插入某个字段的数据
insert into B select id,name,age from A;
在数据库终端指令, 如果要查看前几个 sql语句运行时间,show profiles;这个时候只能看到小数点后两位,
如果要看到精确的运行时间。set profiling = on;
如果某个表查询的时候出现了乱码,在终端 set names utf8;
delimiter $ //在终端输入$才会结束一条语句
优化总则
1.少查(能不查就不查)
2.查少(少查点数据)
3.快点查(走索引)
4.少排序
一.表结构优化
1.定长和变长分离
如id int,占4个字节,char(4)占4个字段长度,也是定长,time 即每一个单元值占的字节是固定的
而varchar,text,blob,这种变长字段,适合单放一张表,用主键与核心表关联起来
2.常用字段和不常用字段分表,通过uuid将两张表关联。这个需要结合网站的具体业务来分析,分析字段的查询场景,查询频率低的字段,单拆出来(用空间来换取时间)
比如两张关联表,一个是板块表(cat),另一个是板块下回复的帖子。查询某个板块下有多少个回复
方法一:先查出有几个板块,再在for循环里面找每个板块下有多少回复
select * from cat; //比如10个板块
for($i=1; $i<=10; $i++){
select count(*) from post where cat_id = $i;
}
方法二,使用左右连接查询,比较消耗资源,不推荐
select cat.* from cat left join post on cat.cat_id = post.cat_id group by cat_id;
如果我们需要实时计算帖子的数量,可以在cat表里面再加一个num字段,每次有人发帖,让这个字段用update更新加1,这是最好的方法。
二.列类型优化
字段类型选择原则
整型>date,time,>enum,char>varchar>blob,text
列的特点分析:
整型:定长,没有国家/地区之分,没有字符集的差异
比如,tinyint 1,2,3,4,5 <--> char(1) a,b,c,d
从空间上,都是占1个字节,但是order by排序,前者快
原因:char需要考虑字符集与校对级(就是排序规则)
time 定长,运算快,节省空间,考虑时区时,写sql时不方便 where > '2016-10-11';
enum:定长,能起约束的目的,内部用整型来存储,但与char联查时,内部要经历串与值得转化
Char 定长,考虑字符集的转换与排序时的校对集
varchar,不定长,要考虑字符集的转换与排序时的校对集,速度慢
text/blob,无法使用内存临时表(排序等操作只能在磁盘上进行)
三.索引优化策略
1.索引类型
1.1Btree索引(二叉树就是索引)--适用于范围的查询
名叫btree索引,大的方面看,都是用的平衡树,但是具体的实现上,各引擎稍有不同
比如,严格说NDB引擎,使用的是T-tree
Mysiam,innodb中默认使用B-tree
但抽象一下,B-tree系统可以理解为"排好序的快速查找结构"
1.2hash索引(只在内存表里面使用hash索引)
在memory表里默认是hash索引,hash里的理论查询时间复杂度为0(1)(一次就能找到)
hash的特点:
1.随机性:hash函数计算后的结果,是随机的,如果是在磁盘上放置数据,
比如主键为id,那么随着id的增长,id对应的行,在磁盘上随机放置
2.无法对范围查询进行优化
3.(离散性)无法利用前缀索引,比如在btree中,field列的值"helloworld",加索引, hash(helloworld)和hash(hello),两者没有关系。
4.排序也法优化(它是随机分配数据的)
5.必须回行,也就是说,通过索引拿到数据位置,必须回到表中取数据
2.btree索引常见的误区
要查询的字段都建立了索引,并不是都可以使用得到。查询的时候遵循左前缀规则
2.索引的左前缀规则--比如index(a,b,c)
语句 | 索引是否发挥作用 |
---|---|
where a=3 | 是,只使用了a列 |
where a=3 and b=5 | 是,只用了a,b列 |
where a=3 and b=5 and c=4 | 是,使用了abc列 |
where b=3 / where c=4 | 否 |
where a=3 and c=4 | a列能发挥索引,c不能 |
where a=3 and b>10 and c=7 | A能利用,b能利用,c不能利用 |
同上,where a=3 and b like"xxx%" and c=7 | A能利用,b能利用,c不能利用 |
四.聚簇索引和非聚簇索引
Myisam用的是非聚簇索引:主键和次索引都指向物理行磁盘的位置
数据是数据 独立文件 xx.myd
索引是索引 独立文件 xx.myi
两者不掺和 这就是非聚簇
它的查找是先根据索引查找到主键的值,值旁边有一个地址,在地址上去查找你要的信息,这个过程叫“回行”。
但是innodb聚簇,值得旁边直接就是行信息。这就是他们的本质信息
innodb用的是聚簇索引:innodb的主索引文件上,直接存放该行数据,称为聚簇索引,次索引指向对主键的作用
注意:对innodb来说
1.主键索引 既存储索引,又在叶子中存储行的数据
2.如果没有主键(primary key),则会unique key做主键
3.如果没有unique,则系统生成一个内部的 rowid 做主键
4.像innodb中,主键的索引结构中,既存储了主键的值,又存储了行数据,这种结构称为"聚簇索引"
聚簇索引
优势:根据主键查询条目比较少时,不用回行(数据就在主节点下)
劣势:如果碰到不规则数据插入时,造成频繁的页分裂
索引覆盖
索引文件中,myi有你需要的字段,就不需要回行。这就是索引覆盖
explain sql \\\\G 如果在extra里面出现了"using index",就是用了索引覆盖,查询效率会很高
比如:如果表A中c1建立了索引
explain select c1 from A where c1>5 \\\\G; 这个时候就用到了索引覆盖
explain select c1,c2 from A where c1>5 \\\\G;如果c2没有索引 这个时候没用索引覆盖。因为c2在索引里面找不到,需要回行
五.理想的索引
1. sql查询中也可以直接使用函数,例如查询词典表中最长的两个单词:
select * from dict order by length(word) desc limit 2;
2.取10个,单词的左边只有一个值的:
select left(word,1) from dict limit 10;
3.取重复的
select distinct left(word,1) from dict limit 10;
select count( distinct left(word,1) ) from dict;
2.伪哈希索引技巧
用于存储url,有两种方法。1,列内容倒过来存储并建立索引。这样左前缀区分度大。
2.伪hash索引效果同时存url hash列
create table A( id int primary key,
url char(60) not null default '' );
insert into A values(1,"www.baidu.com"),
(2,"www.sina.com"),(3,"www.itxdl.com");
alert table A add urlcrc int unsigned not null;
在sql存储时,crccurl == crc32(url)
因为crc的结果是32位,int 无符号数,因此当数据超过40亿也会有重复,但这是是的的。(索引长度为int4个字节)
select crc32('str'); //可以把任何字符串转换成数字
商品建立索引,一般两种方式
以,栏目、价格 index(cat_id, price); 或者栏目、品牌、价格 index(cat_id,brand_id,shop_price);
索引与排序有两种情况
1.对于覆盖索引,直接在索引上查询时,就是有序的,using,index
在innodb引擎中,沿着索引字段排序,也是自然有序的,对于myisam引擎,如果按某种索引字段排序,如id,但去除的字段中未有索引字段,如goods_name,myisam的做法,不是 索引->回行,而是先去除所有的行,再进行排序
2.先去除数据,形成临时表做filesort(文件排序,但文件可能在磁盘上,也可能在内存中)