这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战
概述
性能调优往往是程序开发过程中不可避免的步骤,对于复杂和高并发的项目,性能调优变的尤其重要。
影响性能的因素
- 业务需求以及业务的复杂度
- 存储定位
不适合放进mysql的数据
1)二进制的多媒体数据
2)流水队列数据
3)超大文本数据
需要放进放进缓存的数据
1)系统的各项配置规则
2)活跃用户的基本信息
3)准实时的统计信息
4)其他一些访问频繁但是变更少的数据
- schema设计对系统的影响
- 尽量减少对数据库访问的请求
- 尽量减少无用数据的查询请求
性能分析
mysql常见瓶颈
- CPU:cpu在饱和的时候一般发生在数据装入内存或从磁盘
- IO:磁盘IO瓶颈发生在装入数据远大于内存容量的时候
- 服务器硬件性能:top、free、iostat和vmstat查看系统性能状态
性能下降、sql慢、执行时间长、等待时间长原因分析
- 查询语句写的不合理
- 索引失效
- 关联查询太多
- 服务器参数设置不合理
常见性能分析手段
慢日志查询
- mysql提供一种日志御用记录mysql中响应的时间超过设定阈值的语句,具体是指将运行时间超过long_query_time值的收集起来,会记录到慢查询日志中
- 默认情况下,mysql数据库没有开启慢查询日志,需要手动设置参数开启
- 查看开启状态
show variables like '%slow_query_log%' - 开启慢日志查询
-- 临时设置
mysql> set global slow_query_log= 'ON';
mysql> set global slow_query_log_file='/path/to/slow.log';
mysql> set global long_query_time=2;
-- 永久配置,修改my.cnf或者my.ini
[mysqld]
slow_query_log = ON
slow_query_log_file = /path/to/slow.log
long_query_time = 3
explain 执行计划
概述
使用explain关键字可以模拟优化器执行sql查询语句,从而知道mysql是如何处理你的sql语句
可以做什么
- 表的读取顺序
- 数据读取操作的操作类型
- 那些索引可以使用
- 那些索引被实际使用
- 表之间的引用
- 每张表多少行被执行优化查询
explain各个字段的意义
id
select查询的序列号,包含一组数字,表示查询中执行select子句或者操作表的顺序
- id相同,执行顺序从上往下
- id全不同,如果是子查询,id的序号会递增,id值越大优先级越高
- id部分相同,执行顺序是按照数字大的先执行,然后数字相同的按照从上到下的顺序执行
select_type
查询类型 用于区分普通查询、联合查询、子查询等复杂查询
- SIMPLE: 简单的select查询,不含子查询或者union
- PRIMARY : 查询中若包含复杂的子部分,最外层被标记为PRIMARY
- SUBQUERY: 在select或where列表中包含子查询
- DERIVED: 早from列表中包含的子查询被标记为derived,mysql会递归执行这些子查询,将结果放置在临时表中
- UNION:若第二个select出现在union之后,则被标记为union,若union包含在from子句的子查询中,外层select被标记为derived
- UNION RESULT : 从union表获取结果的select
table
显示这一行数据的对应查询的表
type
显示查询使用了那种类型,从最好到最差的排列
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > all
一般来说,得保证查询至少在range级别 最好达到ref级别
- system:表只有一行记录,是const类型的特例,一般不会出现
- const:表示通过索引一次就找到了,const用于比较primary key或union索引,因为只要匹配一行数据,所以很快,如将主键至于where列表中,mysql就能将该查询转换为一个常量
- eq_ref : 唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主键或者唯一索引扫描
- ref:非唯一索引扫描,范围匹配某个单独值的所有行。本质上也是一种索引访问,他返回所有匹配某个单独值的行
- range:只检索给定返回的行,使用一个索引来选择行。key列显示使用哪个索引,一般是在where语句中出现between、<、>、in等的查询
- index:full index scan ,index与all的区别为index类型值遍历索引树,通常比all快,因为索引文件通常比数据文件小。index和all都是全表扫描,但是index是从索引中读取,all是从磁盘中读取
- all : Full table scan ,将遍历全表找到匹配行
possible keys
显示可以能应用在这个表中的索引,一个或者多个,查询设计到的字段,若存在索引,则该索引被列出,但实际上不一定用到。
key
实际使用的索引,若未null,则没有使用索引
key_len
- 表示索引中使用的字节数,可以通过该列计算查询中使用的索引长度。 在不损失精确性的情况下,长度越短越好
- key_len显示的值为索引字段的最大可能长度,并非实际的使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的
ref
显示索引那一列被使用了,如果可能的话,是一个常数。哪些列或者常量被用于查找索引上的值。
rows
根据表统计信息及索引选用户情况,大致估计找到所找记录需要读取的行数
extra
表示一些额外的信息
性能分析
索引优化
- 全值匹配是最优选择
- 最佳左前缀法则
- 不在索引列上做任何操作(计算,函数,类型转换),会导致索引失效转而进行全表扫描
- 存储引擎不能使用索引中范围条件右边的列
- 尽量使用覆盖索引,减少select
- is null,is not null会导致索引失效,进而进行全表扫描
- like 'xxx%'可以使用索引,而like '%xxx%'则会导致索引失效,进而全表扫描
- 字符串不加单引号,会导致索引失效
- 少用or,用它来链接时会导致索引失效
- <=、>=、BETWEEN、IN可以使用到索引,<>、not in、!=则不行,会导致全表扫描
查询优化
永远小表驱动大表
-- 当表A > 表B,使用in
select * from A where id in (select id from B)
-- 当A表 < B表,使用exists
select * from A where exists (select 1 from B where B.id = A.id)
数据类型优化
- 更小的通常更好。一般情况,尽量使用可以正确存储数据的最小数据类型。
- 简单最好,简单的数据类型通常需要更少的CPU周期。例如:整形类型比字符串操作代价更低,因为字符串集合校对规则比整形要复杂
- 尽量避免使用null
order by 关键字优化
- mysql支持两种方式的排序:index和FileSort;index效率高(扫描索引本身完成排序),FileSort效率低
- order by子句尽量使用index方式排序,避免使用filesort方式排序
满足一下两种情况,会使用index排序
1)order by语句使用索引最左前列
2)使用where子句或者order by子句条件组合满足索引最左前列
- filesort方式排序有两种算法
双路排序:mysql4.1之前是用改排序,两次扫描磁盘,最终得到数据
单路排序:从磁盘读取查询需要的所有列,按照order by列在buffer对他们进行排序,然后扫描排序后的列进行输出,效率高于双路排序
- 优化策略
1)增大 sort_buffer_size的参数设置
2)增大max_length_for_sort_data的参数设置
group by关键字优化
- group by实际是先排序后进行分组,遵照最左匹配原则
- 当无法使用索引时,增大max_length_for_sort_data和sort_buffer_size的参数设置
- where高于having,能在where限定的条件下就不要去having限定了。