数据库优化
高效使用索引
尽可能避免全表扫描
减少无效数据的查询
索引创建规则
idx_表名_字段名(字段名可以是多个,eg. idx_table_name,idx_table_nameAge)
Mysql所认为的执行顺序
FROM
ON
WHERE
GROUP BY
HAVING
SELECT
DISTINCT
ORDER BY
LIMIT
哪些情况需要建索引
1.主键自动建立唯一索引
2.频繁作为查询条件的字段应该创建索引
3.查询中与其他表关联的字段
4.where条件里用不到的字段不创建索引
5.统计分组排序字段
哪些情况不需要建索引
1.表记录太少(百万以下不用考虑建索引,一般数据够300万就要建索引)
2.频繁更新的字段不适合建索引
3.数据重复且分布平均的字段不建索引,比如性别,不是男就是女。索引是区别越大,效率越高
SQL优化
1.生产上出现慢SQL,首先要做的就是复现
2.设置阈值,开启慢日志查询(慢日志影响性能,生产上一定要关闭。比如超过5秒就是慢查询要这么抓出来)
3.explain + 慢日志分析
4.show profile分析(本地Navicat就可以看到,生产上只能用命令)
5.专业人士进行SQL服务器参数调优
Explain
首先要知道explain有哪些信息。id,select_type,table,partitions,type,possible_keys,key,key_len,ref,rows,filtered,Extra这些是基本的,还有些后来加的
id
id是用来表示执行顺序的东西。id相同就按顺序执行,id不同值越大;优先级越高,越先执行
一般出现id不同就是有子查询
select_type
就是select查询的类型
SIMPLE
最简单的查询语句,不包含子查询或union
PRIMARY
当查询语句中包含任何复杂的子部分,最外层查询则被标记为PRIMARY
SUBQUERY
当select或where列表中包含了子查询,该子查询被标记为SUBQUERY
DERIVED
表示包含在from子句中的子查询的select,在我们的from列表中的包含的子查询会被标记为DERIVED
UNION
如果union后边又出现的select语句,则会被标记为union;若union包含在from子句的子查询中。外层select被标记为DERIVED
UNION RESULT
代表从union的临时表中读取的数据,table列的
table
并不一定是真实的表,有别名,也有临时表
partitions
查询时匹配到的分区信息,对于非分区表值为NULL,查询的是分区表时,partitions显示分区表命中的分区情况
type
system>const>eq_ref>ref>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>All
system就是表中只有一行记录,相当于系统表,不需要磁盘IO,速度非常快
const表示查询时命中primary key主键或unique唯一索引,或者被连接的部分是一个常量const值,查询速度非常快
eq_ref对于每一个索引,表中只有一条记录与之匹配
ref返回匹配某个单独值的所用行,也算是一种索引访问
ref_or_null类似于ref,会额外搜索null的行
index_merge使用了索引合并,查询使用了两个以上的方法
unique_subquery该类型替换了下面形式的IN子查询的ref:value IN (SELECT primary_key FROM single_table WHERE some_expr),返回不重复值
index_subquery和unique_subquery类似,返回重复值
range使用索引检索范围内的行betwee,>,<,in
index遍历索引树,也很慢但比All强
All全表扫描,性能最差但驱动表避免不了
possible_keys
可能使用到的索引,如果是覆盖索引,他可以为null
key
实际用到的索引,使用到则标记出索引,没有使用到则为NULL
key_len
越短当然越好,越长就越精准。需要注意的是key_len只计算where条件中用到的索引长度,而排序和分组即便用到了索引也不会计算到key_len中
ref
常见的有null,func,const,字段名
使用常量等值查询,显示const
当关联查询时,会显示相应关联表的关联字段
如果查询条件使用了表达式,函数或者内部隐式转换可能显示为func
其他情况为null
rows
rows以表的统计信息和索引使用情况,计算需要读取多少行,rows越小代表扫描的行数越少
filtered
代表表中符合条件的记录数
Extra
一些重要的额外信息
using filesort,部分使用到了索引,一般是order by的字段没有索引,需要优化
using temporary,使用中间表保存中间结果,一般和group by,order by没用到索引,需要优化
using index,使用到了覆盖索引,使用了覆盖索引,覆盖索引就是索引列和查询列一致,顺序不同无所谓。这样就不需要查询数据行直接查索引就好,推荐使用
using where,查询时未找到可用索引,进而通过where条件过滤获取
using join buffer,在我们连表查询如果连接条件没有用到索引,需要一个连接缓冲区来存储中间结果,join太多了,可以考虑加索引或调大join buffer的参数
impossible where ,where后面的条件不正确
No tables used ,没有表或者用了虚表
单表优化
1.最左前缀匹配法则
2.不在索引列上做任何操作(计算,函数,显式或者隐式转换)
3.范围条件in,>,<等索引会失效,考虑把范围放最后或者使用between
4.尽量使用覆盖索引,不使用select *
5.使用!=或<>无法使用索引,这个看版本高版本的都是range,低版本建议不要在相关字段上设置索引
6.is null,is not null不走索引
7.like只有用 xx%可以走索引,这个也看版本的,数据实在过大(过亿了这种)考虑走搜索引擎Es,Solr。如果数据量很少刚刚几千条别想了直接%xx%。如果数据量也不算小,建议使用内置函数INSTR(str,substr)
8.字符串不加单引号不走索引,MySQL会帮你隐式转换但是不走索引
9.or使索引失效,低版本会,高版本肯定不会失效
10.拼装where后的条件少用1=1,在mybatis用where标签,好多项目都用这个,不用的也有类似解决方案
11.极端情况下,可以考虑使用hint(ignore index,force index加在from后面)优化语句。所谓极端可以是某个语句他不走索引了,查询慢到开花,强制让他走索引,留时间出来分析语句。这个慎用,搞不好会影响到其他业务,数据库版本升级也会对他造成很大打击
12.别用select *,不可能一张表里所有字段都是索引的
13.能用where,不用having,因为where能提前筛选出来,就会少很多数据
多表优化
LEFT JOIN索引加在从(右)表的关联字段上,RIGHT JOIN索引加在主(左)表的关联字段上,为什么这么说呢?就比如说左连接会把主(左)表的所有关联数据都拿去做匹配。如果索引加在主(左)表就会导致要扫描一些没必要查的行,导致效率下降,右连接也是同理。但还是要以小表驱动大表,不然得出来的结果还是有很大差距的
阿里手册开发手册也提过join的次数最好不要超过三次,超过了非常影响效率(想起我以前写SQL,5表查询我写了5个left join我还很自豪.....),关于这个我有两种解决方案:1.根据业务在表中设计冗余字段,你想要的某个字段已经存好放在同一张表里了,那就不需要联表了 2.设计中间表,中间表只存其他表对应记录的id,做业务时顺便往中间表里插入其他表的id确保他的id不会为空,让需要多次JOIN的表跟中间表关联上,这样可以根据中间表id查出中间表上的其他表的id字段,再根据各个id字段查出所需要的对应信息,也就是说分两步走
索引生效图鉴
我们的索引长这样idx_abc(a,b,c),是个复合索引,一般实际使用都是用的复合索引
慢日志分析
使用慢日志的分析工具mysqldumpslow
以下是几条用的较多的命令
得到返回记录集最多的10条SQL
mysqldumpslow -s -r -t 10 慢日志地址具体到文件(/var/lib/mysql/slow.log)| more
得到访问次数最多的10条SQL
mysqldumpslow -s c -t 10 慢日志地址具体到文件 | more
得到按照时间排序的前10条里面包含有左连接的SQL
mysqldumpslow -s t -t 10 -g "left join" 慢日志地址具体到文件 | more
show profile
其实只要注意几个点就好了
converting to HEAP to MyISAM 查询结果太大,内存不够用往磁盘上搬
creating tmp table 创建临时表,拷贝数据到临时表用完了再删除,这个非常的影响效率
copying to tmp table on disk 把内存中临时表复制磁盘,这个就是必须要优化了
locked 锁表了,这个要看是为什么锁,众所周知DML加了各种锁,锁了正常那就不用管,不正常就看是不是常用的业务,不是常用的kill,常用的看看是kill好还是等到满足解锁条件自动解锁好