【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的表現是天差地別的。

相關文章
相關標籤/搜索