SQL 优化
一、各种SQL执行效率
# 查看当前session中所统计参数的值:
Show status like ‘Com_%’;
Show status like ‘Innodb%’;
Show status like ‘Connections%’;
Show status like ‘Uptime%’;
Show status like ‘Slow_queries%’;
二、定位执行效率低的SQL语句
- 通过查询慢日志定位
- Show processlist 命令查看当前MySQL在进行的线程。
三、通过Explan分析低效SQL的执行计划
通过 explain 或者 desc 命令获取MySQL如何执行select 语句的信息。
参数说明:
- select_type:表示select的类型,常见取值simple(简单表,即不使用表连接或者子查询)、primary(主查询,即外层的查询)、union(union中的第二个或者后面的查询语句)、subquery(子查询中的第一个select)等
- table:输出结果集的表
- type:表示MySQL在表中找到所需行的方式,或者叫访问类型,常见:all,index,range,ref,eq_ref,[const,system],null;从左到右,性能最差到最好。
All: 全盘扫描
Index:索引全扫描
Range:索引范围扫描,常见 < , > , <= , >= , between 等操作符
Ref:使用非唯一索引扫描或唯一索引的前缀扫描,返回匹配某个单独值的记录行
Eq_ref:唯一索引,简单说,就是多表连接中使用primary key 或者 unique index作为关联条件
Const/System:单表中最多有一行匹配行,如primary key 或 unique index
Null:不用访问表或者索引,直接获得结果
4.possible_keys:表示查询时可能使用的索引。
5.key:表示实际使用的索引
6.key_len: 显示MySQL决定使用的键长度。可以反映出一个多重主键里MySQL实际使用了哪部分。
7.ref: 显示那个字段或常熟与key一起被使用。
8.rows:扫描行的数量
9.Extra:执行情况的说明和描述,包含不适合在其他列中显示但是对执行计划非常重要的额外信息。Only index 信息只能用索引树中的信息检索;where used 表示使用了 where限制,但是用索引还不够;impossible where 表示通过收集到的同级信息判断出不可能存在的结果。还有可能的值 Using filesort、using temporary、select tables optimized way。
四、通过show profile分析SQL
# 查看是否支持profile
默认 profiling是关闭的,通过set 语句在 session 级别开启 profiling
Show profiles; 和 show profile [for query query_id];不加默认是最后一个id
注意:sending data状态表示MySQL线程开始访问数据行并把结果返回给客户端,而不仅仅是返回结果给客户端。由于在sending data 状态下,MySQL线程往往需要做大量的磁盘读取操作,所以经常是整个查询中耗时最长的状态。
进一步支持all,cpu,block io,context,switch,page faults等明细类型来查看MySQL在使用什么资源上耗费了过高的时间。
五、通过trace分析优化器如何选择执行计划
使用方式:首先打开trace,设置格式为json,设置trace最大能够使用的内存大小,避免解析过程中因为默认内存过小而不能够完整显示。
Set optimizer_trace = ‘enabled=on’ , end_markers_in_json=on;
Set optimizer_trace_max_men_size = 1000000;
接着执行想做trace的sql语句
最后检查 information_schema.optimizer_trace 知道MySQL如何执行sql
六、简单实用的优化方法
1.定期分析表和检查表
# 分析表语句
Analyze [local | no_write_to_binlog] table table_name [, table_name]…
# 检查表语句
Check table table_name [,table_name]…[option]…option = {QUICK|FAST|MEDIUM|EXTENDEN|CHANGED}
检查一个或多个表是否有错误。对myisam 和 Innodb表有作用。也可以用于视图
2.定期优化表
Optimize [local | no_write_to_binlog] table table_name [,table_name] …
如果已经删除了表的一大部分,或者如果已经对含有可变长度行的表(含有varchar、blob或text列的表)进行了很多改动,则要用optimize table 命令进行表优化。 可将表中的空间碎片进行合并,并且可以删除由于删除或者更新造成的空间浪费,但次命令只对myisam 、bdb和Innodb表起作用。
注意analyze、check、optimize、alter table 执行期间将对表进行锁定,在数据库不繁忙的时候执行。
七、简单SQL的优化
1.大批量插入数据
Alter table table_name disable keys;
Load data infile “ xxx.txt” into table table_name;
Alter table table_name enable keys;
说明:
打开或者关闭MyISAM表非唯一索引的更新。如导入一个非空的MyISAM表示,设置这两个命令可以提高导入效率。对于空的表,默认就是先导入数据后创建索引的。
提高innodb的导入速度:
1.对于innodb此方法无效,因为innodb类型的表默认是按照主键的顺序保存的,所以导入的数据按照主键的顺序排列,可以有效的提高导入效率。
2.导入前执行 set unique_checks = 0, 关闭唯一性校验,导入结束后,set unique_checks = 1,恢复唯一性校验;
3.如果应用是自动提交,导入前set autocommit=0 ,导入后 set autocommit=1,相对快一些。
2.优化insert语句
如果从同一客户端插入很多行,尽量使用多个值表的insert语句,大大缩减客户端与数据库之间的链接、关闭等消耗,比分开执行的单个insert语句块,大部分情况下,快上好几倍。
Insert into table_name values (xxx),(xxxx),(xxxxx)…
如果从不同客户端插入很多行,使用insert delayed 语句得到更高的速度。 Delayed的含义是让insert语句马上执行,其实数据都被放在内存的队列中,并没有真正写入磁盘,这比每条语句分别插入要快的多。Low_priority刚好相反,在所有其他用户对表的读写完成后再进行插入。
将索引文件和数据文件分在不同的磁盘上存放(利用建表中的选项)
如果进行批量插入,可以通过增加bulk_insert_buffer_size 变量值的方法来提高速度,但是只对MyISAM使用。
3.优化order by 语句
- 1. 通过有序索引顺序扫描直接返回有序数据
- 2. 通过对返回数据进行排序,filesort排序(不是通过索引直接返回顺序结果的排序)
所以,尽量减少额外的排序,通过索引直接返回有序数据。Where和order by 使用相同的索引,并且order by 的顺序和索引顺序相同,并且order by 的字段都是升序或者都是降序。否则可定需要额外的排序操作。
Filesort的优化:
针对filesort排序,mysql有两种排序算法:
通过max_length_for_sort_data的大小和query语句取出的字段总大小判断使用哪一种算法,如果max_length_for_sort_data更大,使用第二种算法。
Filesort排序尽量只使用必要的字段,select具体的字段名,不要用*,可以减少排序区的使用,提高sql性能。
4.优化group by语句
默认情况下,mysql对group by col1,col2.。。的字段进行排序。与在查询中指定order by col1,col2.。。类似。因此如果显示包含一个order by 子句,则对mysql的实际执行性能没有什么影响。
如果查询包括group by 但用户要避免排序结果的消耗,可以指定order by null 禁止排序。
5.优化嵌套查询
子查询可以使用一个select语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。
有些情况下子查询可以被更有效率的链接join替代,因为mysql不需要再内存中创建临时表来完成这个逻辑上需要多个步骤的查询工作。
6.优化 or 条件
含有or条件的查询子句,如果要利用索引,则or之间的每个条件列都必须用到索引,如果没有索引,则应该考虑增加索引。
MySQL在处理含有or字句的查询时,实际是对or的各个字段分别查询后的结果进行union操作。
7.优化分页查询
Limit 1000,20 语句,mysql排序出前1020条记录后仅仅返回需要的第1001到1020条的记录,前1000条记录会被抛弃,查询和排序的代价非常高。
a) 第一种优化思路:
在索引排序完成排序分页的操作,最后根据主键关联回原表查询所需要的其他列内容。
b) 把limit查询转换成某个位置的查询
例如,第41页最后一行的rental_id = 15640
如上,把limit m,n 转换成 limit n的查询,注意:只适合排序字段不会出现重复值的特定环境。
8.使用sql提示
简单来说就是在sql语句中加入一些人为的提示来达到优化操作的目的。
常见的sql提示:
a) Use index (index_name)
放在表名的后面,提供希望mysql去参考的索引列表,不在考虑其他可用的索引。
b)Ignore index (index_name)
忽略一个或者多个索引
c) Force index (index_name)
强制让mysql使用一个特定的索引
常用sql技巧
- 正则表达式的使用
Mysql利用regexp命令提供给用户扩展的正则表达式功能。
Where email regexp “@163[,.]com$”
- 巧用rand()提取随机行
Order by rand() 完成随机抽取某些行,即把数据随机排序
- 利用group by 的 with rollup子句
with rollup子句可以检索出更多的分组聚合信息,对聚合分类后的结果再进行汇总。也就是说一个group by 语句执行完成后可以满足用户想要得到的任何一个分组以及分组组合的聚合信息值。
注意:Order by 和 with rollup相互排斥。Limit 用在 With rollup 后面。
- 用bit group functions做统计
Bit_add()、bit_or()函数
- 数据库名、表名大小写问题
操作视同的大小写的敏感性决定了数据库名和表名对大小写的敏感性。
列、索引、存储子程序和触发器名在任何平台上对大小写不敏感。
- 使用外键需要注意的问题
除innodb存储引擎,其他用创建外键语句没有效果,只最为备忘录或者注释来提醒用户目前正定义的列指向另一个表中的一列。
八、优化数据库对象
- 优化表的数据类型
Procedure analyze()对当前应用的表进行分析,对数据表中的列的数据类型提出优化建议。
出现枚举,是因为分析的数据太少,因此函数觉得我们使用枚举类型会更合理;如果是对一个大表进行分析会更准确。
不要为那些值多于16个或者256字节的enum类型提出建议。
- 通过拆分(数据表拆分)提高表的访问效率
MyISAM有两种拆分:
a) 垂直拆分
把主码和一些列放到另一个表,然后把主码和另外的列放到另一个表。
使用于:某些列常用,而另外一些列不常用。
注意:查询所有需要联合(join)
b) 水平拆分
根据一列或多列数据的值把数据行放到两个独立的表中。
查询所有用 (union)
- 逆规范化
增加冗余列、增加派生列、重组表、分割表
- 使用中间表提高统计查询速度
即 创建一个和原表数据结构一样的表,把要统计的数据插入到中间表,在中间表中进行统计。且可以在中间表中灵活的增加索引或增加临时用的新字段。