20丨MySQL:資料庫級監控及常用計數器解析(上
資料庫是一個非常大的話題,我們在很多地方,都會看到對資料庫的效能分析會包括以下部分。
但其實呢,以上這些內容都是我們應該具備的基礎知識,所以我今天要講的就是,具備了這些基礎知識之後我們應該幹什麼事情。
也就是說,從效能瓶頸判斷分析的角度入手,才是效能從業人員該有的邏輯。每次我分析一個性能問題時,邏輯總是這樣的:
- 先畫出整個系統的架構圖。
- 列出整個系統中用到了哪些元件。這一步要確定用哪些監控工具來收集資料,具體的內容你可以看下之前講到的監控設計相關的內容。
- 掌握每個元件的架構圖。在這一步中需要列出它們的關鍵效能配置引數。
- 在壓力場景執行的過程中收集狀態計數器。
- 通過分析思路畫出效能瓶頸的分析決策樹。
- 找到問題的根本原因。
- 提出解決方案並評估每個方案的優缺點和成本。
這是我一直強調的分析決策樹的建立邏輯。有了這些步驟之後,即使不熟悉一個系統,你也可以進行效能分析。
對於MySQL資料庫來說,我們想對它進行分析,同樣也需要看它的架構圖。如下圖所示(這是MySQL5版本的架構示意圖):
這裡就有一個問題了:看架構圖是看什麼?這個圖夠細嗎?
首先,看架構圖,一開始肯定是看大而全的架構。比如說上圖,我們知道了,MySQL中有Connection Pool、SQL Interface、Parser等這些大的模組。
其次,我們得知道這些模組的功能及執行邏輯。比如說,我們看到了這些模組之後,需要知道,當一個SQL通過Connection Pool進到系統之後,需要先進入SQL Interface模組判斷這個語句,知道它是一個什麼樣的SQL,涉及到了什麼內容;然後通過Parser模組進行語法語義檢查,並生成相應的執行計劃;接著到Optimizer模組進行優化,判斷走什麼索引,執行順序之類的;然後就到Caches中找資料,如果在Caches中找不到資料的話,就得通過檔案系統到磁碟中找。
這就是一個大體的邏輯。但是知道了這個邏輯還不夠。還記得前面我們說的對一個元件進行“全域性—定向”的監控思路吧。
這裡我們也得找工具實現對MySQL的監控,還好MySQL的監控工具非常多。
在講MySQL的監控工具之前,我們先來了解下MySQL中的兩個Schema,分別是information_schema
和performance_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 Hits
和DMS
的意思比較清晰之外,其他的幾個值理解起來比較費勁。我也不建議你看下面那幾個,因為它們對效能分析來說沒起到什麼正向的作用。
而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有點感覺了?這裡我給一個結論性的描述吧:
- 在這個效能場景中,慢日誌太多了,需要定向監控看慢SQL,找到慢SQL的執行計劃。
- 在這個插入多的場景中,鎖等待太多,並且等待的時候又太長,解決慢SQL之後,這裡可能會解決,但還是要分析具體的原因的,所以這裡也是指向了SQL。
這裡為什麼要描述得這麼細緻呢?主要是因為當你看其他一些工具的監控資料時,分析思路是可以共用的。
但是有人說這裡還有一個問題:SQL怎麼看?
其實對於我們分析的邏輯來說,在資料庫中看SQL就是在做定向的分析了。請你不要相信一些人所吹噓的那樣,一開始就把所有的SQL執行時間統計出來,這真的是完全沒有必要的做法。因為成本太高了。
在下一篇文章裡,我們換個工具來看看SQL的執行時間到底應該怎麼分析。