1. 程式人生 > 實用技巧 >20丨MySQL:資料庫級監控及常用計數器解析(上

20丨MySQL:資料庫級監控及常用計數器解析(上

資料庫是一個非常大的話題,我們在很多地方,都會看到對資料庫的效能分析會包括以下部分。

但其實呢,以上這些內容都是我們應該具備的基礎知識,所以我今天要講的就是,具備了這些基礎知識之後我們應該幹什麼事情。

也就是說,從效能瓶頸判斷分析的角度入手,才是效能從業人員該有的邏輯。每次我分析一個性能問題時,邏輯總是這樣的:

  1. 先畫出整個系統的架構圖。
  2. 列出整個系統中用到了哪些元件。這一步要確定用哪些監控工具來收集資料,具體的內容你可以看下之前講到的監控設計相關的內容。
  3. 掌握每個元件的架構圖。在這一步中需要列出它們的關鍵效能配置引數。
  4. 在壓力場景執行的過程中收集狀態計數器。
  5. 通過分析思路畫出效能瓶頸的分析決策樹。
  6. 找到問題的根本原因。
  7. 提出解決方案並評估每個方案的優缺點和成本。

這是我一直強調的分析決策樹的建立邏輯。有了這些步驟之後,即使不熟悉一個系統,你也可以進行效能分析。

對於MySQL資料庫來說,我們想對它進行分析,同樣也需要看它的架構圖。如下圖所示(這是MySQL5版本的架構示意圖):

這裡就有一個問題了:看架構圖是看什麼?這個圖夠細嗎?

首先,看架構圖,一開始肯定是看大而全的架構。比如說上圖,我們知道了,MySQL中有Connection Pool、SQL Interface、Parser等這些大的模組。

其次,我們得知道這些模組的功能及執行邏輯。比如說,我們看到了這些模組之後,需要知道,當一個SQL通過Connection Pool進到系統之後,需要先進入SQL Interface模組判斷這個語句,知道它是一個什麼樣的SQL,涉及到了什麼內容;然後通過Parser模組進行語法語義檢查,並生成相應的執行計劃;接著到Optimizer模組進行優化,判斷走什麼索引,執行順序之類的;然後就到Caches中找資料,如果在Caches中找不到資料的話,就得通過檔案系統到磁碟中找。

這就是一個大體的邏輯。但是知道了這個邏輯還不夠。還記得前面我們說的對一個元件進行“全域性—定向”的監控思路吧。

這裡我們也得找工具實現對MySQL的監控,還好MySQL的監控工具非常多。

在講MySQL的監控工具之前,我們先來了解下MySQL中的兩個Schema,分別是information_schemaperformance_schema

為什麼呢?

information_schema儲存了資料庫中的所有表、列、索引、許可權、配置引數、狀態引數等資訊。像我們常執行的show processlist;就來自於這個schema中的processlist表。

performance_schema提供了資料庫執行時的資源消耗情況,它以較低的代價收集資訊,可以提供不少效能資料。

所以這兩個Schema對我們來說就非常重要了。

你沒事的時候,也可以查一下它們相關的各個表,一個個看著玩。監控工具中的很多資料來自於它們。

還有兩個命令是你在分析MySQL時一定要學會的:SHOW GLOBAL VARIABLES;SHOW GLOBAL status;。前一個用來檢視配置的引數值,後一個用來查詢狀態值。當你沒有其他工具可用的時候,就可以用這兩個命令的輸出結果來分析。對於全域性監控來說,這兩個命令絕對夠用。

對於MySQL的監控工具有很多,但我主要講的是以下幾個工具:
mysqlreport、pt-query-digest、mysql_exportor+Prometheus+Grafana。

今天我們先來說一下mysqlreport。

全域性分析:mysqlreport

這個工具執行之後會生成一個文字檔案,在這個文字檔案中包括瞭如下這些內容。

我覺得這個工具是屬於既不浪費資源,又能全域性監控MySQL的很好的工具。

在我們執行效能場景時,如果想讓mysqlreport抓取到的資料更為準確,可以先重啟一下資料庫。如果你覺得重啟資料庫這個動作實在是有點大,可以先把狀態計數器、開啟表、查詢快取等資料給重新整理一下。

我認為mysqlreport有一些重要的知識點需要你知道,在這裡我找一個例子給你解釋一下。

索引報表

_ Key _________________________________________________________________
Buffer used     5.00k of   8.00M  %Used:   0.06
  Current       1.46M            %Usage:  18.24

請注意,這裡所指的Key Buffer是指MyISAM引擎使用的Shared Key Buffer,InnoDB所使用的Key Buffer不在這裡統計。

從上面的資料來看,MySQL每次分配的Key Buffer最大是5K,佔8M的0.06%,還是很小的。下一行中的資料可以看到的是當前只用了1.46M,佔8M的18.24%。

顯然這個Key Buffer是夠用的,如果這個使用率高,你就得增加key_buffer_size的值了。

操作報表

__ Questions ___________________________________________________________
Total         126.82M    32.5/s
  +Unknown     72.29M    18.5/s  %Total:  57.00
  Com_         27.63M     7.1/s           21.79
  DMS          26.81M     6.9/s           21.14
  COM_QUIT     45.30k     0.0/s            0.04
  QC Hits      38.18k     0.0/s            0.03
Slow 2 s        6.21M     1.6/s            4.90  %DMS:  23.17  Log:
DMS            26.81M     6.9/s           21.14
  SELECT       20.73M     5.3/s           16.34         77.30
  INSERT        3.68M     0.9/s            2.90         13.71
  UPDATE        1.43M     0.4/s            1.13          5.33
  DELETE      983.11k     0.3/s            0.78          3.67
  REPLACE           0       0/s            0.00          0.00
Com_           27.63M     7.1/s           21.79
  admin_comma  11.86M     3.0/s            9.35
  set_option   10.40M     2.7/s            8.20
  commit        5.15M     1.3/s            4.06

從這個資料可以看到的資訊量就有點大了,它可以反應出來這個資料庫現在忙不忙。

從32.5每秒的操作量上來說,還是有點忙的。你還可以看到下面有運算元的細分,其實我不太願意看下面的這些細分,描述上除了QC HitsDMS的意思比較清晰之外,其他的幾個值理解起來比較費勁。我也不建議你看下面那幾個,因為它們對效能分析來說沒起到什麼正向的作用。

而Slow 那這一行就很重要了,從這行可以看出slow log的時間是設定為2秒的,並且每秒還出現1.6個的慢日誌,可見這個系統的SQL的慢日誌實在是有點多。

DMS部分可以告訴我們這個資料庫中各種SQL所佔的比例。其實它是具有指向性的,像我們的這個例子中,顯然是SELECT多,那如果要做SQL優化的話,肯定優先考慮SELECT的語句,才會起到立竿見影的效果。

查詢和排序報表

__ SELECT and Sort _____________________________________________________
Scan            7.88M     2.0/s %SELECT:  38.04
Range         237.84k     0.1/s            1.15
Full join       5.97M     1.5/s           28.81
Range check   913.25k     0.2/s            4.41
Full rng join  18.47k     0.0/s            0.09
Sort scan     737.86k     0.2/s
Sort range     56.13k     0.0/s
Sort mrg pass 282.65k     0.1/s

這個報表具有著絕對的問題指向性。這裡的Scan(全表掃描)和Full join(聯合全表掃描)在場景執行過程中實在是太多了,這顯然是SQL寫得有問題。

Range範圍查詢很正常,本來就應該多。

查詢快取報表

__ Query Cache _________________________________________________________
Memory usage  646.11k of   1.00M  %Used:  63.10
Block Fragmnt  14.95%
Hits           38.18k     0.0/s
Inserts         1.53k     0.0/s
Insrt:Prune    2.25:1     0.0/s
Hit:Insert    24.94:1

在這部分中,我們看的關鍵點是,Query Cache沒用!因為各種query都沒有快取下來。同時這裡我們還要看一個關鍵值,那就是Block Fragment,它是表明Query Cache碎片的,值越高,則說明問題越大。

如果你看到下面這樣的資料,就明顯沒有任何問題。

__ Query Cache ______________________________________________________
Memory usage   38.05M of 256.00M  %Used:  14.86
Block Fragmnt   4.29%
Hits           12.74k    33.3/s
Inserts        58.21k   152.4/s
Insrt:Prune  58.21k:1   152.4/s
Hit:Insert     0.22:1

這個資料明顯看到快取了挺多的資料。Hits這一行指的是每秒有多少個SELECT語句從Query Cache中取到了資料,這個值是越大越好。

而通過Insrt:Prune的比值資料,我們可以看到Insert遠遠大於Prune(每秒刪除的Query Cache碎片),這個比值越大就說明Query Cache越穩定。如果這個值接近1:1那才有問題,這個時候就要加大Query Cache或修改你的SQL了。

而通過下面的Hit:Insert的值,我們可以看出命中要少於插入數,說明插入的比查詢的還要多,這時就要去看這個效能場景中是不是全是插入了。如果我們查看了,發現SELECT語句還是很多的,而這個比值又是Hit少,那麼我們的場景中使用的資料應該並不是插入的資料。其實在效能場景的執行過程中經常這樣。所以在效能分析的過程中,我們只要知道這個值就可以了,並不能說明Query Cache就是無效的了。

表資訊報表

__ Table Locks _________________________________________________________
Waited              0       0/s  %Total:   0.00
Immediate         996     0.0/s


__ Tables ______________________________________________________________
Open             2000 of 2000    %Cache: 100.00
Opened         15.99M     4.1/s

這個很明顯了,表鎖倒是不存在。但是你看現在table_open_cache已經達到上限了,設定為2000,而現在已經達到了2000,同時每秒開啟表4.1個。

這些資料說明了什麼呢?首先開啟的表肯定是挺多的了,因為達到上限了嘛。這時候你會自然而然地想到去調table_open_cache引數。但是我建議你調之前先分析下其他的部分,如果在這個效能場景中,MySQL的整體負載就會比較高,同時也並沒有報錯,那麼我不建議你調這個值。如果負載不高,那再去調它。

連線報表和臨時表

__ Connections _________________________________________________________
Max used          521 of 2000      %Max:  26.05
Total          45.30k     0.0/s


__ Created Temp ________________________________________________________
Disk table    399.77k     0.1/s
Table           5.81M     1.5/s    Size:  16.0M
File            2.13k     0.0/s

這個資料連線還完全夠用,但是從臨時表建立在磁碟(Disk table)和臨時檔案(File)上的量級來說,還是有點偏大了,所以,可以增大tmp_table_size

執行緒報表

__ Threads _____________________________________________________________
Running            45 of   79
Cached              9 of   28      %Hit:  72.35
Created        12.53k     0.0/s
Slow                0       0/s


__ Aborted _____________________________________________________________
Clients             0       0/s
Connects            7     0.0/s


__ Bytes _______________________________________________________________
Sent          143.98G   36.9k/s
Received       21.03G    5.4k/

當Running的執行緒數超過配置值時,就需要增加thread_cache_size。但是從這裡來看,並沒有超過,當前配置了79,只用到了45。而這裡Cached的命中%Hit是越大越好,我們通常都希望在99%以上。

InnoDB快取池報表

__ InnoDB Buffer Pool __________________________________________________
Usage           1.87G of   4.00G  %Used:  46.76
Read hit      100.00%
Pages
  Free        139.55k            %Total:  53.24
  Data        122.16k                     46.60 %Drty:   0.00
  Misc            403                      0.15
  Latched                                  0.00
Reads         179.59G   46.0k/s
  From file    21.11k     0.0/s            0.00
  Ahead Rnd         0       0/s
  Ahead Sql                 0/s
Writes         54.00M    13.8/s
Flushes         3.16M     0.8/s
Wait Free           0       0/s

這個部分對MySQL來說是很重要的,innodb_buffer_pool_size為4G,它會儲存表資料、索引資料等。通常在網上或書籍裡,你能看到有人建議將這個值設定為實體記憶體的50%,當然這個值沒有絕對的,還要在具體的應用場景中測試才能知道。

這裡的Read hit達到100%,這很好。

下面還有些其他的讀寫資料,這部分的資料將和我們在作業系統上看到的I/O有很大關係。有些時候,由於寫入的過多,導致作業系統的I/O wait很高的時候,我們不得不設定innodb_flush_log_at_trx_commit引數(0:延遲寫,實時刷;1:實時寫,實時刷;2:實時寫,延遲刷)和sync_binlog 引數(0:寫入系統快取,而不刷到磁碟;1:同步寫入磁碟;N:寫N次系統快取後執行一次重新整理操作)來降低寫入磁碟的頻率,但是這樣做的風險就是當系統崩潰時會有資料的丟失。

這其實是我們做測試時,儲存效能不高的時候常用的一種手段,為了讓TPS更高一些。但是,你一定要知道生產環境中的儲存是什麼樣的能力,以確定在生產環境中應該如何配置這個引數。

InnoDB鎖報表

__ InnoDB Lock _________________________________________________________
Waits          227829     0.1/s
Current             1
Time acquiring
  Total     171855224 ms
  Average         754 ms
  Max            6143 ms

這個資訊就有意思了。顯然在這個例子中,鎖的次數太多了,並且鎖的時間都還不短,平均時間都能達到754ms,這顯然是不能接受的。

那就會有人問了,鎖次數和鎖的平均時間多少才是正常呢?在我的經驗中,鎖平均時間最好接近零。鎖次數可以有,這個值是累加的,所以資料庫啟動時間長,用得多,鎖次數就會增加。

InnoDB其他資訊

__ InnoDB Data, Pages, Rows ____________________________________________
Data
  Reads        35.74k     0.0/s
  Writes        6.35M     1.6/s
  fsync         4.05M     1.0/s
  Pending
    Reads           0
    Writes          0
    fsync           0


Pages
  Created      87.55k     0.0/s
  Read         34.61k     0.0/s
  Written       3.19M     0.8/s


Rows
  Deleted     707.46k     0.2/s
  Inserted    257.12M    65.9/s
  Read        137.86G   35.3k/s
  Updated       1.13M     0.3/

這裡的資料可以明確告訴你的一點是,在這個效能場景中,插入佔有著絕對的量級。

總結

好了,我們拿一個mysqlreport報表從上到下看了一遍之後,你是不是覺得對MySQL有點感覺了?這裡我給一個結論性的描述吧:

  1. 在這個效能場景中,慢日誌太多了,需要定向監控看慢SQL,找到慢SQL的執行計劃。
  2. 在這個插入多的場景中,鎖等待太多,並且等待的時候又太長,解決慢SQL之後,這裡可能會解決,但還是要分析具體的原因的,所以這裡也是指向了SQL。

這裡為什麼要描述得這麼細緻呢?主要是因為當你看其他一些工具的監控資料時,分析思路是可以共用的。

但是有人說這裡還有一個問題:SQL怎麼看?

其實對於我們分析的邏輯來說,在資料庫中看SQL就是在做定向的分析了。請你不要相信一些人所吹噓的那樣,一開始就把所有的SQL執行時間統計出來,這真的是完全沒有必要的做法。因為成本太高了。

在下一篇文章裡,我們換個工具來看看SQL的執行時間到底應該怎麼分析。