【MySQL】为何不要问我DB极限QPS/TPS

为何不要问我DB极限QPS/TPS

背景 相信不少开发都会有这个疑问,DB到底能够支撑多大的业务量,如何去评估?对于这个很专业的问题,DBA也没有办法直接告诉你,更多的都是靠经验提供一个看似靠谱的结果,这里主要说明数据库容量评估的难点。mysql

定性分析

借用学校时候作物理题的一个思考方法 -- 极限法;咱们假设两种极限场景: 极限场景一,全部SQL 都是主键等值查询。极限场景二,全部SQL 都是走不上索引的全表扫描。这两种场景下你们都可以一眼看出数据库的支撑能力,在场景一和场景二下会有很大的差异。固然,咱们现实的业务场景,位于两种极限场景之间,这个时候很难简单粗暴的说当前实例能够支撑多少业务量,由于缺乏信息输入。DBA 同窗必定会和研发同窗进行详细的沟通,确认数据库运行SQL 的类型,以及不一样类型SQL 的执行频率,所涉及表的数据量状况,综合评估一个能够支撑的性能区间,做为上线前的基本容量建设模型。sql

随着系统上线后,数据库系统就会一直处于一种变化的状态。变化一,SQL 类型,随着业务逻辑不断丰富,运行SQL 开始逐渐变得更复杂,从最开始设计的几类SQL 到 十几类SQL 甚至 几十类SQL,这就要求咱们对新上线的SQL 书写质量有一个保障机制,就是你们熟悉的SQL Review,当前有人工Review 和 IDB 自动Review 两种保障机制。变化二,业务表的数据量也在不断增加,针对小表很高效的SQL,随着数据量的增加,运行效率会逐步降低,出现大促业务流量突增时,就是一个稳定性风险。DBA 针对这几类问题都有相应的处理策略,这不是今天的重点,会在后续的系列文章中逐一和你们介绍。数据库

常见影响性能案例

大规模数据导出功能

相信不少业务都遇到过数据导出,明细展现这方面的需求,sql基本上都是先求一个数据的总和而后,limit n,m分页查询,这样的问题就在于,在扫描前面的数据时是不会有性能问题的,当n值越大,偏移量越多,扫描的数据就越多,这个时候就会产生问题,一个原本不的sql就会变成慢sql,致使DB性能降低。针对这种问题DBA都会建议开发将limit n,m改成id范围的查询,或者进行业务改造对于一些没必要要的场景只展现前几百条,只须要进行一次分页便可。express

相似sql模式:缓存

   select count(*) from table_name_1;
   select * from table_name_1 limit n,m;(n值越大性能越差)
   建议改形成:
   select * from table_name_1 where id>? and id<?
ERP类系统使用聚合函数或者分组排序

相似仓库内管理系统会须要展现不少统计信息,不少开发会选择在DB端计算出结果直接展现,问题在于sum,max,min类的聚合函数在DB端执行会消耗到CPU资源,若是这个时候还遇到索引不合理的状况,每每会带来灾难性的后果。这种状况DB端除了增长索引,对CPU的消耗是没法优化的,因此DB性能必然降低。通常这种状况DBA会建议能在程序端计算的就不要放在DB端,或者直接接搜索引擎。less

相似sql模式:函数

    select sum(column_name) as column_1 from table_name_1;
    or
    select distinct cloumn_name  from table_name_1 group by column_name_1 order by column_name_1;
错误使用子查询

在DB端执行去重,join以及子查询等操做的时候,mysql会自动建立临时表oop

DB自动建立临时表的状况有以下几种性能

1. Evaluation of UNION statements.
2.  Evaluation of some views, such those that use the TEMPTABLE algorithm, UNION, or aggregation.
3. Evaluation of derived tables (subqueries in the FROM clause).(这个是本节关注的重点)
4. Tables created for subquery or semi-join materialization (see Section 8.2.1.18, “Subquery Optimization”).
5. Evaluation of statements that contain an ORDER BY clause and a different GROUP BY clause, or for which the ORDER BY or GROUP BY contains columns from tables other than the first table in the join queue.
6. Evaluation of DISTINCT combined with ORDER BY may require a temporary table.
7. For queries that use the SQL_SMALL_RESULT option, MySQL uses an in-memory temporary table, unless the query also contains elements (described later) that require on-disk storage.
8. Evaluation of multiple-table UPDATE statements.
9. Evaluation of GROUP_CONCAT() or COUNT(DISTINCT) expressions.

在mysql中,对于子查询,外层每执行一次,内层子查询要重复执行一次,因此通常建议用join代替子查询。优化

下面举一个子查询引发DB性能问题的例子

Query1:select count(*) from wd_order_late_reason_send wrs left join wd_order_detail_late_send wds on wrs.store_code = wds.store_code;

下面是执行计划:

*************************<strong> 1. row </strong>***********************<strong>
       id: 1
       select_type: SIMPLE
       table: wrs
       type: ALL
       possible_keys: NULL
       key: NULL
       key_len: NULL
       ref: NULL
       rows: 836846
       Extra: NULL
 </strong>***********************<strong> 2. row </strong>*************************
       id: 1
       select_type: SIMPLE
       table: wds
       type: ALL
       possible_keys: NULL
       key: NULL
       key_len: NULL
       ref: NULL
       rows: 670612
       Extra: Using where; Using join buffer (Block Nested Loop)
 

Query2:select count(*) from (select wrs.store_code from wd_order_late_reason_send wrs left join wd_order_detail_late_send wds on wrs.store_code = wds.store_code) tb;

执行计划以下

  *************************<strong> 1. row </strong>***********************<strong>
       id: 1
       select_type: PRIMARY
       table: <derived2>
       type: ALL
       possible_keys: NULL
       key: NULL
       key_len: NULL
       ref: NULL
       rows: 561198969752
       Extra: NULL
 </strong>***********************<strong> 2. row </strong>***********************<strong>
       id: 2
       select_type: DERIVED
       table: wrs
       type: ALL
       possible_keys: NULL
       key: NULL
       key_len: NULL
       ref: NULL
       rows: 836846
       Extra: NULL
</strong>***********************<strong> 3. row </strong>*************************
       id: 2
       select_type: DERIVED
       table: wds
       type: ALL
       possible_keys: NULL
       key: NULL
       key_len: NULL
       ref: NULL
       rows: 670612
       Extra: Using where; Using join buffer (Block Nested Loop)

这两个sql结果相同,惟一不一样的是第二条sql使用了子查询。经过执行计划能够看出(排除没有索引部分)两个sql最大的差异就是第二个sql有derived table而且rows是561198969752,出现这个数值是由于在select count(*)每次计数的时候子查询的sql都会执行一遍,因此最后是子查询join的笛卡尔积。由于内存中用于进行join操做的空间有限,这个时候就会使用磁盘空间来建立临时表,因此当第二种sql频繁执行的时候会有磁盘被撑爆的风险。 想要了解更多关于子查询的优化能够参考下面这个连接link

慢sql

这里咱们所说的慢sql主要指那些因为索引使用不正确或没有使用索引产生的,通常能够经过增长索引。一个合理的索引对一条sql性能的影响是很是巨大的。索引的主要目的是为了减小读取的数据块,也就是咱们常说的逻辑读,读取的数据块越少,sql效率越高。另外索引在必定程度上也能够减小CPU的消耗,例如排序,分组,由于索引原本就是有序的。

说到逻辑读,对应的就会有物理读,在mysql服务端是有buffer pool来缓存硬盘中的数据,可是这个buffer pool的大小跟磁盘中数据文件的大小是不等的,每每buffer pool会远远小于磁盘中数据的大小。buffer pool会有一个LRU链表,当从磁盘中加载数据块到内存中(这个就是物理读)发现没有空间的时候会优先覆盖LRU链表中的数据块。当一条sql没有合理的索引须要扫描大量的数据的时候,不光要扫描内存中的许多数据块,还可能须要从磁盘中加载不一样不存在的数据块到内存中进行判断,当这种状况频繁发生的时候,sql性能就会急剧降低,于是也影响了DB实例的性能。

如下表格是访问不一样存储设备的rt,因而可知一个合理的索引的重要性。

类别 吞吐量 响应时间
访问L1 Cache 0.5ns
访问L2 Cache 7ns
内存访问 800M/s 100ns
机械盘 300M/s 10ms
SSD 300M/s 0.1~0.2ms
日志刷盘策略不合理

目前集团mysql大部分使用的都是innodb存储引擎,所以在每条DML语句执行时不光会记如binlog还有记录innodb特有的redo log和undo log。这些日志文件都是先写入内存中而后在刷新到磁盘中。在server端有两个参数分别控制他们的写入速度。innodb_flush_log_at_trx_commit控制redo log写入模式,sync_binlog控制binlog写入模式

经过以上表格能够了解到,在使用线上默认配置的状况下每次commit都会刷redo log到磁盘,也就是说每次写入都会伴随着日志刷盘的操做,须要消耗磁盘IO,因此在高TPS或者相似业务大促状况下,DBA能够调整这个参数,来提高DB支撑TPS的能力。

BP设置太小

前面已经提到sql在读写数据的时候不会直接跟磁盘交互,而是先读写内存数据,由于这样最快。可是考虑到成本问题BP(buffer pool)大小是有限的,不可能跟数据文件同等大小,因此若是BP设置不合理就会致使DB的QPS TPS始终上不去。下面咱们具体分析一下。

mysql buffer pool中包含undo page,insert buffer page,adaptive hash index,index page,lock info,data dictionary等等DB相关信息,可是这些page均可以归为三类free page,clean page,dirty page.buffer pool中维护了三个链表:free list,dirty list,lru list

  • free page:此page未被使用,此种类型page位于free链表中
  • clean page:此page被使用,对应数据文件中的一个页面,可是页面没有被修改,此种类型page位于lru链表中
  • dirty page:此page被使用,对应数据文件中的一个页面,可是页面被修改过,此种类型page位于lru链表和flush链表中

当BP设置太小的时候,好比BP 10g 数据文件有200g 这个时候有大量的select或者dml语句,mysql就会频繁的刷新lru list或者dirty list 到磁盘,大部分时间消耗在刷磁盘上,而不是业务sql处理上,这个时候就会致使业务TPS QPS始终上不去,伴随着DB内存命中率下降。一般这个时候的解决办法是须要DBA调整一下实例BP的大小。

硬件问题

就像生活中会有意外同样,在排除了以前那些因素以后,还会存在由于硬件故障或者参数设置不合理致使DB性能抖动的状况,若是不能当即修复,DBA通常只能经过迁移实例的方式来消除影响。

写在后面

通过上面几个情景的描述,咱们能够把影响线上DB性能的因素归为三类:一、业务逻辑问题 二、DB端设置问题 三、硬件问题。由于硬件问题属于小几率事件,因此影响线上DB性能的主要是前面两类因素,也所以不一样的业务场景下,DB的表现是天差地别的。

相关文章
相关标签/搜索