MySQL优化
- 增加冗余字段,空间换时间
- varchar(10)和varchar(255) 都存储相同的数据,连表查询时,varchar(255)更占内存
- 字段避免存null,不走索引
索引类型
-
Btree 索引
- 表的索引会生成一个单独的数据结构,类似于二叉树结构存储,每个节点存储key(索引)和指向数据的地址(真正表中的数据地址) - btree索引常见误区:把where后常用列都加上索引,如果where id =1 and age > 20,其实最终查询只会用到一个索引,mysql的独立索引同时只能用到一个。 - 建议建立多列联合索引,但是要注意,联合索引要满足左前缀原则 注:group by ,order by 也要遵循左前缀原则 - 可以提高排序速度,因为索引树本来就是有顺序,根据顺序从表中以此取出 - 可以提高分组速度,要分组必须先排序
-
hash 索引
每条数据有对应的hash,查询单一数据效率高,但是查询范围效率低,无法利用前缀索引,每次查询都必须回行,索引树只查询数据位置索引类型
-
聚簇索引 innodb
主键索引和行数据一起存放,找到索引就找到数据,不需要回行,
主键索引和数据存放在一起,如果通过主键索引查找,找到索引就等于找到数据,如果通过其他索引,二级索引或者次索引,会找到对主键索引的引用,然后获取数据- 默认主键索引
- 没有主键,用Unique key 做主键
- 没有Unique key 默认生成 rowId
-
非聚簇索引 myisam
索引树和数据是分开存放,先去索引树找到数据位置,然后再去表中找数据,
索引指向行在磁盘上的位置 -
索引覆盖
如果执行计划里最后 一个参数Extra是Using Index,那么说明这次查询走了索引覆盖,只在索引树上就找到了要查询的数据,不用回行去表中取数据 -
理想的索引
- 查询频繁
- 区分度高
- 长度下
- 尽可能覆盖常用查询字段
-
索引与排序
如果在排序语句中,执行计划Extra=Using filesort 这种情况出现了排序,该语句效率不理想
避免order by 和 where 都使用了单独索引,或者字段不一样,就会出现 排序 -
explain 详解
- select_type
simple –简单查询
primary –主要查询
subquery –非from子查询
derived –from子查询
union –链接
unino_result –链接查询返回临时表 - table
实际查询的表名,也有可能是表的别名,derived 派生表 - type(效率由低到高)
all –全表扫描
index –扫描所有的索引节点
range –范围查找
ref –精准查询
eq_ref –只有一条精准查询
const,system –常量 - possible_keys
可能用到的索引 - key
实际用到的索引 - key_len
索引用到的最大长度 - ref
多表链接查询的链接字段 - rows
估计要扫描的行数 - extra
Using index –用到索引覆盖,效率最高
Using where –索引配合where
Using temporary –产生临时表
Using filesort – 文档排序(肯能在内存,可能在磁盘)注:如果取出的列含 text等大的列,排序 filesort会发生在磁盘上 show table like '%table%'; 查询当前用到多少次临时表,磁盘表 Created_tmp_disk_tables --创建了磁盘表 Created_tmp_tables --创建了临时表
- select_type
in 查询 优化
-
正常情况下,内层循环parent_id 用到索引,外层cat_id 用到索引,但其实查询执行计划的时候,只有内层用到索引,外层全表扫描
1
select goods_id,goods_name,cat_id from goods where cat_id in (select cat_id from catgory where parent_id = '6')
-
原因:mysql 在执行in查询的时候,是外层表取一条,然后取出in的字段,在去内层表中查,并且拼上内层的条件,所以,外层表会全表每条都查一遍,并且都会和内层表去匹配,这样就导致,如果外层表条数越多,效率越低.
mysql伪查询执行如下://先查询外层goods表中一条数据,取出in 内表的字段 1:select cat_id from goods limit 1; //比如cat_id为1 //把外层表取出的cat_id 拿着去内层表中去匹配 2:select * from catgory where cat_id = 1 and parent_id = 6; 如果匹配到,返回,然后继续第一步,外层goods表总有多少条数据,以上步骤就会执行多少次
-
解决:用inner join 链接查询,把内层查出来映射成临时表,用外层链接临时表,这样,两张表都走索引
1
select goods_id,goods_name,cat_id from goods inner join (select cat_id from catgory where parent_id = '6') temp on temp.cat_id = goods.cai_id
之前内层表catgory用索引 parent_id 查询,外层表用cat_id链接查询结果,走索引。
exists
底层查询会和in来回转换,所有也会存在in出现的问题
from型子查询
内层form查询返回的临时表是没有索引的,所以一定要保证内层的查询尽可能的返回少的数据
limit 优化
备注:命令 set profiling=1; 开启sql性能分析,show profiles 查看执行的语句,show profile for query 5;
limit查询原理:(默认主键id索引) select * from user limit 1000010; 查询1000010条数据,然后把前1000000条丢弃,只取最后10条,所以,随着limit页数越大,查询效率越低一般很少有网站可以翻到1000000页,百度最多只翻到76页,这里只说优化场景
解决:区间查询 select * from user where id > 1000000 limit 10; id走索引,然后取10条,速度特别快,但前提是表的主键是连续自增并且没有删除的
如果主键不连续,有删除,用索引覆盖加延时查询:
select * from user inner join (select id from user limit 1000000,10) tmp on tmp.id = user.id
先用内层查询出主键id,这时是走索引覆盖,效率高,找到具体id后,在次连user表查询具体信息