4.3 重點是該SQL為什麼運作時間這麼久呢?我們通過運作SQL擷取執行計劃和我們采用perf top+top -H的方式來看看内部的函數調用。發現如下:
- 結合資源占用情況可以看到占用cpu資源最大的函數是:JOIN_CACHE::read_record_field
- 結合執行計劃看sub_part、tsf、cat使用了臨時表和join_buffer,試圖分析i_s.files無果,該試圖中大多是資料字典,無法通路。
- 這可能和我們系統中存在大量ibd檔案有關。肯定很多同學會問,為什麼會有這麼多ibd,因為我們大量使用分庫、分表、分區。
我們知道join cache 一般用在兩表join連接配接,被驅動表沒有索引的情況下,将驅動表的資料放到join cache中,當join cache滿了以後驅動一次被驅動表,以此來減少被驅動表全表掃描的次數,進而提高性能。
其次我們需要知道的MySQL雖然某一個線程負載高但是,一個線程隻能使用CPU核心,我們監控監控的是整體的CPU,是以雖然一個CPU已經達到99%的高負載,但是整體平均下來也不那麼明顯,這是我行以後監控需要持續改進的地方。
5. checking permissions的疑惑
很明顯上面的分析我們發現語句實際已經執行了,但是為什麼處于checking permissions狀态呢?為了解開這個疑惑,我們需要将斷點放到THD::enter_stage和JOIN_CACHE::read_record_field上,當然這部分我們沒有深入的研究,隻看debug狀态,debug 什麼呢,隻要證明狀态 executing 後進入了checking permissions狀态且在checking permissions狀态下執行了JOIN_CACHE::read_record_field即可如下:
這裡證明了語句已經進入了執行階段,但是每次讀取一行join cache的記錄轉換一次狀态為checking permissions,棧如下:
我們來看紅色函數的注釋如下:
INFORMATION_SCHEMA picks metadata from new DD using system views.
顯然這裡和通路information_schema中的資料有關,因為這裡涉及到information_schema和資料字典的實作,過于龐大,我們不做研究了。
但是我們得出一個結論,對于通路字典視圖,出現比較奇怪的狀态,我們應該用perf top或者pstack擷取資訊,而不能停留在正常的認知上。
6. 探索優化思路
- 肯定有同學想問mysqldump為什麼要執行上面這個SQL呢?這個還需要備份廠商來解釋了,該SQL在備份中還不能短時間改善,銀行是非常注重備份,不可能等廠商改,是以我們放棄該思路
- 既然廠商無法調整,那我們就從資料庫本身着手,SQL屬于内部試圖,我等源碼基礎也不好,無奈啊!隻能從執行計劃和占用高資源得函數着手,既然join_cache這麼高,且執行計劃中提示使用了join_buffer和臨時表,那麼我們來試圖調整join_buffer_size,目前預設是2M,将join_buffer_size調整後再次執行SQL,SQL運作1min7s,效果明顯。
至此,我們已經找到優化思路,調整資料庫join_buffer_size來解決,肯定有同學問,這個也不能随便調整啊,因為這是一個session級别的參數,可能導緻MySQLD使用記憶體大幅增加。但是,我們架構中設計的這個庫是專門用于全備的,沒有任何應用連接配接,是以可以調整該參數。再次發起資料庫備份,觀察幾天時間,該問題不再發現。透過事物看本質發現,mysql中在有大量的表或分區情況下,在通過内部試圖、資料字典讀取作業系統中檔案時可能會存在有各種性能問題,對于某些查詢操作我們可以在備庫進行,盡量減少對主庫的沖擊。
7. 補充:關于幾個timeout參數生效點
接下來我們也研究了幾個timeout參數, 如果出現逾時遇到了日志是Got timeout reading communication packets,而不是Got an error reading communication packets。實際上幾個timeout 參數都是通過poll的timeout參數實作的,我們稍微總結了一下如下:
- 連接配接進行握手,連接配接時poll的timeout受到connect_timout影響
- loop 進入死循環
- wait_timeout/interactive_timeout 參數設定poll timeout參數
- 堵塞等待指令來到
- 指令來到退出堵塞。
- 指令執行期間讀寫更改poll的參數,受net_read_timout和net_write_timeout參數影響
- 指令執行
- 指令執行完成後,再次做wait_timeout/interactive_timeout參數檢查并且恢複
- goto loop
是以總結一下:
- Got timeout reading communication packets:可能和參數connect_timout,net_read_timout,wait_timeout/interactive_timeout 有關
- Got timeout writing communication packets:可能和參數net_write_timeout有關