MySQL 慢怎麼辦html
若是遇到 MySQL 慢的話,你的第一印象是什麼,MySQL 數據庫若是性能不行,你是如何處理的?前端
我諮詢了一些同行, 獲得瞭如下反饋:mysql
第一反應是再試一次linux
第二個反應是優化一下 SQLios
第三個反應是調大 buffer pool,而後開始換硬件了,換一下 SSDgit
最後實在不行了找個搜索引擎搜索一下「MySQL 慢怎麼辦」。github
若是你們用的是國內的搜索引擎的話,搜索引擎會推薦某某知道或者某某乎, 推薦一些 MySQL 調優經驗, 調大參數 A, 調低參數 B, 諸如此類,相似的網站能告訴你 MySQL 慢怎麼辦。sql
咱們來分析一下這些現象背後隱藏的意義:數據庫
若是再試一次可以成功的話, 意味着你可能碰到了不可復現的外界因素的影響,致使 MySQL 會慢。緩存
若是優化 SQL 能解決,就意味着 SQL 的執行復雜度遠遠大於它的需求複雜度。
若是調大 buffer pool 能解決,就意味着 MySQL 碰到了自身的某些限制。
若是換 SSD 能解決,那麼意味着服務器資源受到了必定的限制。
若是須要搜索引擎,意味着調優這事已經變成了玄學。
本文向你們分享我對 MySQL 慢的診斷思路,以及向你們介紹系統觀測工具。
MySQL 慢的診斷思路
MySQL 慢的診斷思路,通常會從三個方向來作:
MySQL 內部的觀測
外部資源的觀測
外部需求的改造
下面依次看一下這幾個思路。
MySQL 內部觀測
經常使用的 MySQL 內部觀測手段是這樣的:
第一步是 Processlist,看一下哪一個 SQL 壓力不太正常;
第二步是 explain,解釋一下它的執行計劃;
第三步要作 Profilling,若是這個 SQL 能再執行一次的話, 就作一個 Profilling;
高級的 DBA 會直接動用 performance_schema ,MySQL 5.7 之後直接動用 sys_schema,sys_schema 是一個視圖,裏面有便捷的各種信息,幫助你們來診斷性能;
再高級一點,會動用 innodb_metrics 進行一個對引擎的診斷。
除了這些手段之外,還有一些亂七八糟的手段就不列在這了,這些是常規的 MySQL 內部狀態觀測的思路。
外部資源觀測
這裏引用國外一個大神寫的文章,標題是《60 秒的快速巡檢》(參考連接在文末)。咱們來看一下它在 60 秒以內對服務器到底作了一個什麼樣的巡檢。一共十條命令,下面一條一條來看一下。
uptime,uptime 告訴咱們這個機器活了多久,以及它的平均負載是多少。
dmesg -T | tail,告訴咱們系統日誌裏邊有沒有什麼報錯。
vmstat 1,告訴咱們虛擬內存的狀態,頁的換進換出有沒有問題,swap 有沒有使用。
mpstat -P ALL 1,告訴咱們 CPU 壓力在各個核上是否是均勻的。
pidstat 1,告訴咱們各個進程的對資源的佔用大概是什麼樣子。
iostat-xz 1,查看 IO 的問題。
free-m 內存使用率;
sar-n DVE 1,
sar-n TCP, ETCP 1,8 和 9 兩條按設備網卡設備的維度,看一下網絡的消耗狀態,以及整體看 TCP 的使用率和錯誤率是多少。
top,看一下大概的進程和線程的問題。
這個就是對於外部資源的診斷,這十條命令揭示了應該去診斷哪些外部資源。
外部需求改造
第三個診斷思路是外部的需求改造,在這裏引用了 MySQL 官方文檔中的一章,《Examples of Common Queries 》( https://dev.mysql.com/doc/mysql-tutorial-excerpt/5.5/en/examples.html),文檔中介紹了常規的 SQL 怎麼寫, 給出了一些例子。
下面看一下它其中提到的一個例子。
這張表有三列,article、dealer、price。它作的事情是從這個表裏選取每一個做者最貴的商品列在結果集中,這是它最原始的 SQL,很是符合業務的寫法,可是它是個關聯子查詢。
關聯子查詢成本是很貴的,因此上面的文檔會教你快速地把它轉成一個非關聯子查詢,你們能夠看到中間的子查詢和外邊的查詢之間是沒有關聯性的。
第三步,會教你們直接把子查詢拿掉,而後轉成這樣一個 SQL,這個就叫業務改造,先後三個 SQL 的成本都不同,把關聯子查詢拆掉的成本,拆掉之後 SQL 會跑得很是好,但這個 SQL 已經不能良好表義了,只有在診斷到 SQL 成本比較高的狀況下才建議你們使用這種方式。
爲何它可以把一個關聯子查詢拆掉?
這背後的原理是關係代數,全部的 SQL 均可以被表達成等價的關係代數式,關係代數式之間有等價關係,這個等價關係經過變換能夠把關聯子查詢拆掉。
總結一下,對於 MySQL 慢的診斷思路以下:
第一,MySQL 自己提供了不少命令來觀察 MySQL 自身的各種狀態,從上往下檢通常能檢到 SQL 的問題或者服務器的問題。
第二,從服務器的角度,咱們從巡檢的腳本角度入手,服務器的資源就這幾種,觀測手法也就那麼幾種,把服務器的資源所有都觀察一圈就能夠了。
第三,若是實在搞不定,需求方必定要按照數據庫容易接受的方式去寫 SQL,這個成本會降低的很是快,這個是常規的 MySQL 慢的診斷思路。
下面重點介紹爲你們介紹系統觀測工具。
系統觀測工具介紹
先從診斷思路的討論切換到系統的觀測工具,首先了解什麼叫系統觀測工具而且看一下它的舉例,而後再回到診斷思路上,看看新的工具的引入能爲咱們的思路到底帶來怎樣的改變。
什麼叫系統觀測工具
這裏也參考了一篇外國人寫的文檔:
https://jvns.ca/blog/2017/07/05/linux-tracing-systems/
把這個文檔拆開,中間描述了三件事情:
系統觀測工具的數據源來自於哪裏;
數據採集過程,由於採集的是系統的運行情況,因此到底如何採集這是一個難點;
應該怎麼看數據,是用圖來看,仍是用表來看,它就叫數據處理前端。
第一步,咱們來看一下數據源,Linux 給咱們提供的數據源包括操做系統內核態提供的觀測點和用戶態提供的觀測點,MySQL 很早以前就提供了用戶態的觀測點。
第二步,如何把數據抽出來。如下這些工具中你們最熟悉的應該是 perf 和 ftrace,sysdig 也有人在用,其它的可能有所耳聞,這是從操做系統裏抽取數據的方法。
第三步,數據處理前端,前端經常使用的也是 perf 和 ftrace。若是你們對 perf 很熟悉的話會知道 perf 出來的數據是一個樹形的數據,並能夠跟這棵樹進行交互,好比說: 查看某個函數運行了多久,哪個函數的時間最長,這個是數據處理前端。
咱們來對比一下常規的四類系統觀測工具:ftrace, perf_events,eBPF 和 Systemtap,這四個工具到底有什麼不一樣,看看 Linux 爲何提供這麼多觀測工具。
ftrace:ftrace 是 sysfs 中的一個樁,經過這個樁內核提供了一種觀測的形式——把想觀測的函數簽名打到這個樁裏,而後操做系統就會提供這個函數運行的情況。ftrace 的結構如左圖, 數據處理前端和採集端是 ftrace, 數據源是下面這一堆。
perf:經常使用的 perf 的原理是操做系統提供了一個系統調用能夠將數據寫到一個緩存中, 而後客戶端把這些數據端抽取出來而後呈如今顯示器上。
eBPF:這是本文想重點推薦的。以上兩種方案一種是操做系統提供的文件系統上的樁,一種是操做系統提供的系統調用,而 eBPF 是將一段代碼直接插到操做系統內核某一個位置上的機制。
Systemtap:它的原理是將一段 C 的代碼編譯成一個內核模塊,而後將這個模塊嵌到內核裏邊去,它不是由內核提供的一個機制,而是由內核的模塊機制提供的一種功能。
介紹了這四種觀測工具的不一樣,你們在選取觀測工具的時候就知道應該怎麼選。
這四種觀測工具對系統傷害最輕的是誰?
對系統傷害最輕的是系統調用,這是系統承諾出來的服務。而後是 ftrace,這是系統在文件系統層面提供的一個口,告訴你能夠經過這個口跟系統交互。
對系統侵入性最強的是誰?
對系統侵入性最強的應該是 eBPF,由於它直接將一根代碼嵌入到系統裏邊去作,最不穩定的應該是 System Tap,由於它是系統的一個模塊, 又提供了很是複雜的功能。
上圖是 eBPF 的架構圖,eBPF 先將一段程序編譯成二進制代碼,而後插入到操做系統裏,操做系統運行這段代碼的時候,將採集到的數據吐到操做系統自己的空間裏,而後再作統一返回。
eBPF 結構最核心的部分在於把代碼插入到操做系統中運行,它須要作各類安全保護才能完成這一點,因此這也是這個機制複雜的地方。
下面引用一個開源的 eBPF 腳本集 bcc, 快速看一下 eBPF 能作什麼, 這些功能都是開箱即用的。
bcc (eBPF 腳本集) 使用舉例
MySQL 的請求延遲分析:
一個 MySQL 承擔了不少業務,上千個併發˙中,哪個 SQL 最慢,到底有哪些 SQL 在一秒以上,除了 slow log 之外,還能夠用這種方法來看。
這個命令的結果分爲三列,它的第一列是請求的延遲,指數級遞增,單位是微秒,中間一列是它的命中數,若是有一個請求命中了 64-127 微秒這個區間,命中數會加一,最後一列是它的分佈圖,它在同一個報告裏提供了數值的方式和圖的方式,能夠很容易看到結果。
對於這臺服務器來講,我下了一個 select 的性能壓力,它大部分的請求集中於 64 到 127 微秒之間。這個數據庫的性能可能還不錯。
再來看另一種壓力,我下了一個 select+insert 的混合壓力在一個數據庫裏,它的圖又變了,它呈現了一個很是好的雙峯圖,我將兩個峯值用另一種顏色標明,這兩個峯值的意思是頗有可能有混合壓力在一個數據庫裏,或者是上面的這部分壓力是命中了某些緩存,而下面的某些壓力是因爲沒有命中緩存,致使這部分請求更慢一些, 造成另外一個峯值,因此經過這種峯值分析能夠看到數據庫大概的一個運行狀態。
若是能作得更好,你能夠抽檢本身的數據庫而後作環比圖,好比今天和昨天一樣的時間,一樣的業務壓力下對數據庫的延遲進行分析,若是數據庫的延遲峯一直在日後延,就意味着數據庫的狀態在變得更糟糕一些。這是 bcc 第一個能作的事情,須要再次強調的是它開箱即用直接下載過來就可使用。
MySQL 的慢查詢:
MySQL 自己提供很好的慢查詢,爲何還要用另一個機制來獲取 MySQL 的慢查詢呢?
MySQL 的慢查詢可能很難作,與 MySQL 的慢日誌相比, 它能夠低成本地完成:
獲取少許慢查詢
獲取某種模式的慢查詢
獲取某個用戶的慢查詢
好比說獲取少許的慢查詢,爲何是少許呢?由於不肯定如今的線上延遲是多少,慢查詢只開一秒可能日誌瞬間就被堆上去,性能就會下來,可是若是慢查詢開個十秒左右,沒有請求在這個區間命中,因此要一點一點的去調這個值,好比說線上 1% 的最慢的查詢可以命中,可是在這個腳本里面,能夠取必定區間的最大的幾個查詢把它拎出來。
經過腳本還能夠命中某種模式的慢查詢, 好比說咱們只關心 update 的慢查詢, 那麼獲取 select 的結果就沒有太大的意義,或者是我必定要獲取某一些特定表的相關的查詢,我均可以經過腳原本作。
第三種狀況,想獲取某個用戶的慢查詢,這個通常對於多租戶系統,由於多租戶系統只想針對某一個用戶進行慢查詢分析的時候,這種腳本就比較好用。
VFS 延遲分析:
對 VFS 作延遲分析,這是對數據庫進行了一個寫壓力,能夠明顯看到一個雙峯圖,這是寫的兩個峯,是數據庫對於內核的寫壓力的反饋。
這個意味着什麼呢?這個可能意味着由於這部分的寫是命中了操做系統文件系統的緩存,而下面這部分寫是真正的寫穿到設備的,因此他們倆的延遲不同,這是一個典型的雙峯圖,你們須要把兩個峯拆開來去行這樣的分析。
換一個說法,若是寫壓力都集中在這裏,而沒有第二個峯的狀況下,需不須要去更換物理設備?有可能不須要,由於全部的東西都命中了操做系統的緩存。
短生命週期的臨時文件檢測:
這個不必定常見,MySQL 會在某些狀況下動用臨時表, 若是 SQL 沒寫好就會建立臨時表,這些臨時表的生命週期很短,可是量很大,因此必定要寫文件而不能內存裏。
在這種狀況下會對操做系統形成一些壓力,而這個壓力又不太好診斷,由於臨時文件的生存週期短,因此這個腳本能夠幫你們提供一個方案,這個方案的結果是這樣子。
我作了一個臨時表,這個臨時表活了 5.3 秒左右,因而它展示在了腳本的結果裏。若是掃描本身的線上 MySQL 發現這裏有大量的東西說明在大量的使用臨時表,若是 IO 壓力在此時比較大, 就可能受了臨時表的影響。
短鏈接分析:
好一點的應用都會用鏈接池,可是咱們不少的時候沒有那麼好的運氣,老碰到那麼好的應用,因此常常業務會扔過來大量的短鏈接。
這個例子中, sysbench 上了一個大併發,可是隻活了 300 多毫秒,這些鏈接都只活了 300 多毫秒,反覆運行這個 sysbench 就能夠將數據庫打死,創建一千個鏈接,300 毫秒之後也會銷燬,再創建一千個鏈接,你的業務就會忽上忽下,經過這個腳本就能夠抓到這個壓力從哪一個服務器來的,哪一個端口來的,而後把它搞定就能夠了。
長鏈接分析:
除了短鏈接分析,還有長鏈接分析,哪個業務端老在搞個人數據,老在往裏寫,總在往裏讀,搞的網絡特別慢。
能夠幫你們提供這樣一個視角,它有讀有寫。
以上幾個 bcc 相關的例子都是現成的腳本。bcc 能夠觀測操做系統的各個方面,好比說若是有東西被 OOM kill 掉了,內存有泄露的也能夠看,它基本上是咱們這幾年發現的一個寶庫,你們直接調用這些腳本就能夠完成不少的別人完成不了的分析,它的技術用的是 eBPF,直接在 github 上直接搜就好了。
eBPF 使用方法 / 限制
若是這裏邊腳本知足不了要求, 那能夠本身寫。這裏介紹一下腳本的寫法以及 eBPF 的限制。
拿上面提到過的 MySQL 延遲分析舉例,一個 MySQL 上面有一千個 query,這些 query 大概都落在哪一個延遲時間裏面那張圖,爲了完成這個需求, 我須要寫兩段程序,其中第一段程序是運行在內核裏邊的程序。
這段程序的邏輯是這樣的:
在 query 開始的時候截獲一下,讓它記錄一個時間戳;
請求結束的時候再截獲一下記錄一個時間戳;
把兩個時間戳相減得到一個延遲;
把這個延遲扔到結果集裏邊去,程序就完成了。
我用結束時間減開始時間,減一下獲得一個延遲,而後把延遲扔到一個統計容器裏面,這個事就結束了。這是我要寫的第一個程序,是嵌到內核裏的程序,可是須要一個外殼的程序負責嵌入。
這個外殼程序的邏輯也很是簡單,把剛纔那段內核的程序嵌到 MySQL 的觀測點上,嵌到內核裏面去,而後把結果集拿出來,打印出來就結束了,這是如何寫一個 eBPF 的腳本,你們惟一須要作的事情就是這兩個程序,而後運行一下。
這個程序的核心只有 45 行,中間忽略了負責差錯處理的一部分。只須要把如今的腳本拿下來抄一抄,改一改就能夠完成不少的功能了。
這麼好的方法爲何不少人不知道呢?
操做系統內核的限制,這個功能是 Linux 4.4 引進來的,可是在 Linux 4.4 上存在統計的 bug,咱們推薦的是 Linux 4.9+,部分好用的功能是在 4.13+ 上纔開放,這個是 eBPF 最大的限制。怎麼辦呢?只能祝你們長壽吧!活到 Linux 4.x 內核能在生產環境上使用的那一天。
它的第二個最大的限制是 MySQL 的編譯參數,MySQL 雖然在很早的時候已經提供了 dtrace 的觀測點,這些觀測點是公用的,可是它在默認的編譯出來的官方發佈的包裏邊是不帶觀測點編譯的,因此在直接官方發佈的二進制的包裏邊是用不了這個功能的,你們須要本身編譯一下。編譯的時候須要帶這個參數,這個可能也是屬於一個比較大的限制。
因此若是你們受到限制,再推薦換一個工具:systemtap。
Linux 2.6 就已經有了,可是它的機制是寫一個內核模塊,這種機制其實不是特別穩定,它爲了解決不是特別穩定的問題增長了若干限制,好比說能在內核中使用的內存大小有限制,採集頻率也有限制,對整個內核性能的影響百分比也有限制,在這些限制參數都開起來的狀況下,它仍是比較安全的。
可是不少觀測功能就必需要把這些限制關掉,一旦關掉內核就不是很穩定,因此這個工具,我沒有敢把它的缺點寫在上面由於確實是個好的工具,咱們也很難說它的這個缺點是個致命的缺陷,可是不太推薦在生產環境上使用,可是在測試環境上確實是很是好玩的一個工具,若是你們用不了 eBPF 的話能夠用 systemtap 來作一些診斷。