MySQL性能优化可从如下几个方面着手
- SQL优化
- 索引优化
- 数据库(表)结构优化
- 系统配置优化
- 服务器硬件优化
- explain语句各个属性的含义
SQL优化
-
开启慢查询记录日志,查找症状(很多时候都是一些慢查询拖累了整个数据库的性能)
- 在配置文件中配置(my.cnf),配置完毕需要重启,不适合线上数据库
#path可修改为绝对或者相对路径 log-slow-queries=slow-log-path #l查询时间超过2s记录 long_query_time=2 #没有使用索引的查询记录 log-queries-not-using-indexes
- mysql命令行下配置
#查看log_query_time变量的值 show variables like "%long%"; #如果long_query_time的值不是期望值,重新设定 set global long_query_time=2; #查询 slow_query_log 和 slow_query_log_file的值 show variables like "%slow%"; #开启慢查询日志 on或者ON都可以,不区分大小写 set global slow_query_log='on'; #慢查询日志文件路径可修改 set global slow_query_log='/data/mysql/slow.log'
慢查询日志分析工具有官方的mysqldumpslow 和pt-query-digest,后者更加精确详细
-
explain 分析sql的执行如explain select * from test1 where id=1;会出现:id selecttype table type possible_keys key key_len ref rows extra各列。
- table 查询的数据表
- type (可能的值 const, eq_reg, ref, range, index,all)
主键或者唯一索引一般是const,性能最好,一次就找到了eq_reg 是一种范围查找,唯一索引,主键可能是此种查找
ref常见于连接查询,一个表基于另外一个索引的查找
range 基于索引的范围查找
index通常是对index的扫描
All 是表扫描 - possible_keys 查询中可以使用的索引
- key 查询中实际使用到的索引,为null表示没有使用索引
- key_len 索引长度,越小越好
- ref 显示索引的那一列被使用了,最好是一个常数
- rows 扫描的行数
- extra
出现using filesort 查询需要优化(group by),出现using temporary需要优化(order by 时容易出现)
-
掌握一些sql的优化方法
max, count,子查询,group by,limit
索引优化
- 选择合适的列建立索引(在where中经常出现的查询条件的列应当创建索引,group by ,order by,on)
- 索引字段越小越好
- 离散度大的列放在联合索引的前面(离散度越大,过滤的数据越多)
判断列的离散度可以根据select count(distinct col1), count(distinct col2) from table
- 索引优化SQL的方法
增加索引会影响写入效率(insert,update,delete)
删除重复和冗余的索引
使用工具pt-duplicate-key-checker分析
使用pt-index-usage 工具配合慢查询日志来分析不再使用的索引(注意主从库的时候无法使用此工具精确判断)
数据库(表)结构优化
- 选择合适的(列)数据类型
- 选择可以存下数据的最小的数据类型
- 选择尽量简单的数据类型
- 尽可能对列加上not null(Innodb特性),给出default
- 尽快能不使用text等大的数据类型,如果要用,尽量和其他字段分离,单独成表
- 表的范式和反范式
- 表的垂直拆分
把原来有很多列的表拆分成多个表,降低表的宽度
拆分原则:不经常使用的字段放在一个表,很大的字段放在一个表,常用的字段放在一个表 - 表的水平拆分
水平拆分解决单表数据量过大的问题,水平拆分之后的每一张表结构相同
常用拆分方法:取模,hash等
分表带来的挑战:跨分区表数据查询;统计及后台操作。使用汇总表,前后台业务分开
系统配置的优化
- 修改/etc/sysctl.conf,优化系统网络参数
- 修改/etc/security/limits.conf 优化打开文件数量
- 硬件防火墙代替软件防火墙防止网络性能消耗
- mysql配置文件
- innodb_buffer_pool_size
- innodb_buffer_pool_instances
mysql 5.5引入,默认一个 - ... 参数不在一一列举
- 第三方工具优化mysql配置
http://tools.percona.com/wizard
硬件优化
- cpu选择
核数不能超过32,mysql对多核的支持并不是特别优秀 - 磁盘IO(RAID,常用RAID1+RAID0)
explain语句各个属性的含义
- id :select查询的序列号
- select_type:select查询的类型,主要是区别普通查询和联合查询、子查询之类的复杂查询。
a. SIMPLE :查询中不包含子查询或者 UNIONb. 查询中若包含任何复杂的子部分,最外层查询则被标记为: PRIMARYc. 在 SELECT 或 WHERE 列表中包含了子查询,该子查询被标记为: SUBQUERYd. 在 FROM 列表中包含的子查询被标记为: DERIVED (衍生)e. 若第二个 SELECT 出现在 UNION 之后,则被标记为 UNION ;若 UNION 包含在 FROM 子句的子查询中,外层 SELECT 将被标记为: DERIVEDf. 从 UNION 表获取结果的 SELECT 被标记为: UNION RESULT
- table :输出的行所引用的表。
- type :联合查询所使用的类型,表示MySQL在表中找到所需行的方式,又称“访问类型”。
type显示的是访问类型,是较为重要的一个指标,结果值从好到坏依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL ,一般来说,得保证查询至少达到range级别,最好能达到ref。
- ALL: 扫描全表
- index: 扫描全部索引树
- range: 扫描部分索引,索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行,常见于between、<、>等的查询
- ref: 非唯一性索引扫描,返回匹配某个单独值的所有行。常见于使用非唯一索引即唯一索引的非唯一前缀进行的查找
- eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
const, system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量。system是const类型的特例,当查询的表只有一行的情况下, 使用system。
NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引。
如下所示:
- possible_keys:指出MySQL能使用哪个索引在该表中找到行。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用。如果是空的,没有相关的索引。这时要提高性能,可通过检验WHERE子句,看是否引用某些字段,或者检查字段不是适合索引。
- key :显示MySQL实际决定使用的键。如果没有索引被选择,键是NULL。
- key_len:显示MySQL决定使用的键长度。表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。如果键是NULL,长度就是NULL。文档提示特别注意这个值可以得出一个多重主键里mysql实际使用了哪一部分。
注:key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。
ref:显示哪个字段或常数与key一起被使用。
- rows:这个数表示mysql要遍历多少数据才能找到,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数,在innodb上可能是不准确的。
- Extra:包含不适合在其他列中显示但十分重要的额外信息。
Only index,这意味着信息只用索引树中的信息检索出的,这比扫描整个表要快。
using where是使用上了where限制,表示MySQL服务器在存储引擎受到记录后进行“后过滤”(Post-filter),如果查询未能使用索引,Using where的作用只是提醒我们MySQL将用where子句来过滤结果集。
impossible where 表示用不着where,一般就是没查出来啥。
Using filesort(MySQL中无法利用索引完成的排序操作称为“文件排序”)当我们试图对一个没有索引的字段进行排序时,就是filesoft。它跟文件没有任何关系,实际上是内部的一个快速排序。
Using temporary(表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询),使用filesort和temporary的话会很吃力,WHERE和ORDER BY的索引经常无法兼顾,如果按照WHERE来确定索引,那么在ORDER BY时,就必然会引起Using filesort,这就要看是先过滤再排序划算,还是先排序再过滤划算。
最后,再看一个查询计划的例子:
第一行:id列为1,表示第一个select,select_type列的primary表示该查询为外层查询,table列被标记为<derived3>,表示查询结果来自一个衍生表,其中3代表该查询衍生自第三个select查询,即id为3的select。[select d1.name......]
第二行:id为3,表示该查询的执行次序为2(4→3),是整个查询中第三个select的一部分。因查询包含在from中,所以为derived。[select id,name from t1 where other_column='']
第三行:select列表中的子查询,select_type为subquery,为整个查询中的第二个select。[select id from t3]
第四行:select_type为union,说明第四个select是union里的第二个select,最先执行。[select name,id from t2]
第五行:代表从union的临时表中读取行的阶段,table列的<union1,4>表示用第一个和第四个select的结果进行union操作。[两个结果union操作]
关于MySQL执行计划的局限性:
EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况
EXPLAIN不考虑各种Cache
EXPLAIN不能显示MySQL在执行查询时所作的优化工作
部分统计信息是估算的,并非精确值
EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看。
备注:
filesort是通过相应的排序算法,将取得的数据在内存中进行排序。
MySQL需要将数据在内存中进行排序,所使用的内存区域也就是我们通过sort_buffer_size 系统变量所设置的排序区。这个排序区是每个Thread 独享的,所以说可能在同一时刻在MySQL 中可能存在多个 sort buffer 内存区域。
在MySQL中filesort 的实现算法实际上是有两种:
双路排序:是首先根据相应的条件取出相应的排序字段和可以直接定位行数据的行指针信息,然后在sort buffer 中进行排序。
单路排序:是一次性取出满足条件行的所有字段,然后在sort buffer中进行排序。
在MySQL4.1版本之前只有第一种排序算法双路排序,第二种算法是从MySQL4.1开始的改进算法,主要目的是为了减少第一次算法中需要两次访问表数据的IO操作,将两次变成了一次,但相应也会耗用更多的sortbuffer 空间。当然,MySQL4.1开始的以后所有版本同时也支持第一种算法。
MySQL主要通过比较我们所设定的系统参数 max_length_for_sort_data的大小和Query 语句所取出的字段类型大小总和来判定需要使用哪一种排序算法。如果 max_length_for_sort_data更大,则使用第二种优化后的算法,反之使用第一种算法。所以如果希望 ORDER BY 操作的效率尽可能的高,一定要注意max_length_for_sort_data 参数的设置。如果filesort过程中,由于排序缓存的大小不够大,那么就可能会导致临时表的使用。
原文链接:https://segmentfault.com/a/1190000002505750
参考http://blog.csdn.net/xifeijian/article/details/19773795