MySQL 调优 金字塔

越向上难度越大,回报减少。
1.硬件和OS的调优,十分复杂,需要有对硬件和OS了解比较深的人员去进行化。
2.MySQL调优,调整表结构、优化SQL、恰当的使用索引,清除多余的索引等等。
3.架构调优,需要考虑实际的业务场景
- 可以将非数据库的任务,放到数据仓库,搜索引擎或者缓存。
2.评估并发量,决定是不是要做分布式。
3.根据读压力,考虑是否进行读写分离。
业务DBA:从业务需求讨论到表结构审核、SQL语句审核、上线、索引的更新。
什么是慢查询日志
记录查询花费大量时间的SQL的日志,就是慢查询日志。
long_query_time采数:该参数会设定一个阈值,超过该值的SQL,就是慢查询SQL。
SQL查询性能下降的原因
查询性能变低的最基础的原因,就是访问的数据太多了。
对于低效的查询,可以通过下面两个步骤分析:
1.确认是否在检索大量超过需要的数据。可能是访问了很多的行,也有可能是访问了很多的列。
2.确认MySQL服务层是否在分析大量超过需要的数据行
请求了不需要的数据
查询不需要的记录
MySQL会查询出全部的数据集(type : all),客户端应用程序会接收全部的数据集,然后抛弃其中大部分数据总是取出全部的列
select * …..,注意 是否需要真的返回全部的列。取出全部的列,会导致优化器无法完成索引覆盖扫描。
一些DBA是严格禁止使用 select *,尤其使用二级索引时,会导致回表,性能下降明显。
什么时候可以用 select *?
• 如果应用程序使用了某种缓存机制,可能就需要获取全部的数据进行缓存,这时候可以选择去用,但是要考虑付出的代价。重复查询相同的数据
不断重复同样的查询,返回相同的记录,也是十分消耗资源,影响MYSQL性能的,这个时候可以考虑进行缓存
扫描了过多的额外记录
如果确定了查询只返回需要的数据以后,接下来就应该看看查询时为了返回需要的数据,是否扫描了过多的数据。对于MySQL,最简单的衡量查询开销的三个指标:
1) 响应时间:服务时间+排队时间
* 服务时间:数据库处理这个查询时,真正花了多少时间。
* 排队时间:指的是服务器因为等待某些资源而没有真正执行查询的时间。有可能是等待行锁。
2) 扫描的行数和返回的行数
* 理想情况下,扫描的行数和返回的行数应该是相同的。
3) 扫描的行数和访问的类型
* 在Explain分析结果中,字段type反映了访问的类型。
* 访问类型,从慢到快:全表扫描 --> 索引扫描 --> 范围扫描 --> 唯一索引扫描 --> 主键扫描
MySQL中使用下面三种方式应用where条件,从好到坏:
1) 在索引中使用where条件,过滤不需要的数据,这个操作存储引擎中完成。
2) 使用覆盖索引扫描来返回记录,直接从索引中过滤不需要的记录并且返回命中的结果。是在MySQL的服务层完成,不需要再回表查询。
3) 从数据表中返回数据,存在回表,过滤不需要的数据,在MySQL的服务层完成的,MySQL需要从数据表读取数据然后进行过滤。
返回的行数少,扫描的行数多,优化方式:
1) 使用索引覆盖扫描,把所有需要用的列都放到索引中。
2) 改变库表的结构。
3) 重写复杂SQL。
慢查询日志分析
MySQL慢查询,全名叫做慢查询日志,作用是用来记录MySQL中响应时间超过设定的阈值的SQL语句。
默认情况不启动慢查询日志。
慢查询相关的参数
慢查询参数解释:
- slow_query_log:是否开启慢查询日志,ON (1)表示开启,OFF(0)表示关闭。
- slow_query_log_file:MySQL数据库慢查询日志的存储的路径。
- long_query_time:慢查询的阈值,当查询时间大于设定的阈值的时候,会记录到日志。
可以通过设置slow_query_log的值,来开启慢查询日志
1 | mysql> set global slow_query_log = 1; |
上面这种设置方式,只对当前的窗口有效的,MySQL重启之后就会失效。如果想要永久生效,需要修改my.cnf
1 | slow_query_log =1 |
重启MySQL让配置生效
1 | [root@localhost ~]# service mysqld restart |
开启慢查询日志之后,什么样的SQL才会记录到慢查询日志中呢?是由参数long_query_time来控制的,默认情况下值是10秒。
重新设置值:
1 | mysql> set global long_query_time=1; |
注意:设置完成后,需要重新连接MySQL才能看到修改后的值。
- log_output:
• 值为FILE,表示将日志存储到文件,默认值。
• 也可以设置存储到数据库:log_ouput=TABLE,但是耗费更多系统资源。 - log_queries_not_using_indexes:开启后,未使用索引的查询,也会被记录到慢查询日志中。
日志内容解析
1 | ----查看日志 tail -f /var/1ib/mysql/test-slow.1og: |
- Time: 执行时间
- User: 用户信息
- Query_time: 查询语句执行的时间
- Lock_time: 等待锁的时间
- Rows_sent: 查询结果行数
- Rows_examined: 查询扫描的行数
- SET timestamp: 事件戳
- SQL的具体信息
慢查询SQL的优化思路
SQL执行时间长的原因:
- 等待的时间长:大概率是由于锁表或锁冲突导致的,让查询一直处于等待状态。
- 执行的时间长
- 查询的SQL写的烂
- 索引失效
- 关联的JOIN太多
- 服务器调优及各个参数设置
慢查询优化的思路:
- 优先选择优化 高并发执行的SQL,因为高并发执行SQL发生问题带来的后果,更加严重。
- 比如下面两种情况:
- SQL1:每小时执行10000次,每次20个IO,优化后每次18个IO,每小时节省2万次IO。
- SQL2:每小时执行10次,每次20000个IO,优化后每次减少2000个IO,每小时节省2万次IO
- SQL2的并发度更高,SQL更难去优化,SQL1更好优化一些,但是SQL2属于高并发SQL,更加急需优化。
- 定位优化对象的性能瓶颈
- IO:数据访问的时候消耗了太多的时间,查看是否正确的使用了索引。
- CPU:数据运算花费了太多的时间,数据运算的分组、排序是不是有问题
- 网络带宽:如果网络带宽受限,数据传输的速度也将受到限制,从而导致查询的响应时间增加了。
- 明确优化的目标(最终的优化结果,就是给用户一个好的体验)
- 需要根据数据库当前的状态。
- 数据库中与当前该条SQL的关系。
- 当前SQL的具体功能。尽量选择优化那些对业务影响比较大的SQL。
- 从explain执行计划入手
- 只有explain能告诉你当前SQL的状态。
- 永远用小的结果集去驱动大的结果集
- 小的结果集驱动大的结果集,目的就是减少内层表读取的次数。
1 | for(int i = 0; i < 5; i++){ |
如果小的循环在外层,对于数据库的连接来说,就是只连接5次,进行5000次操作,否则反过来就是连接1000次,每次做5次操作,就会增加资源的浪费
- 尽可能的在索引中完成排序
- 因为索引本身就是排好序的,排序字段在索引中的话,速度是比较快的。
- 只获取自己需要的列
- 不要用select *
- 只去使用最有效的过滤条件
- 误区:where后面的条件越多越好,实际上应该是用最短的路径访问到数据才是最好的。
- 尽可能避免复杂JOIN和子查询
- 每条SQL的JOIN 操作,建议不超过3张表
- 将复杂的SQL拆分成多个小SQL 单个表执行,获取到结果 去程序中封装就可以。
- 合理设计并且利用索引
如何判断是否需要创建索引?
- 较为频繁的作为查询条件的字段,应该创建索引
- 唯一性太差的字段不适合单独的去创建索引。比如:性别 状态 类型字段,当一条query所返回的数据超过了全表的15%的时候,就不需要再使用索引扫描。
- 更新频繁的字段不适合创建索引。因为需要维护索引文件。
- 不会出现在where条件中的字段 不需要创建索引。
如何选择合适的索引?
- 对于单键索引,尽量选择针对于当前查询效果更好的索引。
- 选择联合索引时当前查询中过滤性最好的字段应该在 联合索引的最左侧。