爲什麼你的MySQL性能差?死代碼牽連問題解決了嗎?

为什么你的MySQL性能差?死代码牵连问题解决了吗?

MySQL存儲過程、函數和觸發器是開發人員常打交道的三大名詞,但是,這三個過程可能會對數據庫性能造成嚴重影響,尤其是函數中出現“死”代碼時,其它兩個過程都會受到牽連。以下是作者進行的一些基本測試,通過對比更好地衡量三者對數據庫性能的影響。

觸發器和存儲過程對性能影響的重要原因是函數“死”代碼。儘管所有開發人員都知道“死”代碼(永遠不會被運行的代碼)不應該出現在代碼行中,但可能並不清楚一行“死”代碼到底可以對性能造成多大影響。

“死”代碼會顯著減緩函數運行、存儲過程和觸發器的響應時間,因此我們必須認真清理不需要的代碼,以下是具體示例:

分析MySQL存儲過程函數

讓我們比較以下四個簡單的存儲函數(在MySQL 5.7中):

函數1

为什么你的MySQL性能差?死代码牵连问题解决了吗?

該函數只是聲明一個r變量並返回它的值,這是一個虛函數。

函數2

为什么你的MySQL性能差?死代码牵连问题解决了吗?

該函數調用另一個函數 levenshtein_limit_n(計算levenshtein距離)。但是,由於IF 1=2條件永遠不會成立,因此這段代碼永遠不會被執行,這與函數1的返回結果類似。

函數3

为什么你的MySQL性能差?死代码牵连问题解决了吗?

函數3看起來很複雜,總共有四個條件,但因為這些條件永遠不會成立,因此同樣是“死”代碼調用,結果與函數2和函數1相同。

函數4

为什么你的MySQL性能差?死代码牵连问题解决了吗?

該函數與函數3情況相同,selectdoes_not_exit依舊不會被運行。

雖然所有函數最終返回的r值都為0,但這四段函數的性能有非常大的差異,為了衡量性能變化,我們使用benchmark功能對性能進行測試,在運行相同的1M次之後,四個函數的性能結果如下:

为什么你的MySQL性能差?死代码牵连问题解决了吗?

我們可以看到fun 1只需要運行1.75 sec,具有一個死代碼調用的fun 2需要運行2.45 sec,而func 3(有四個永不執行的死代碼調用)運行速度比func 1慢近3倍,func 3_nope的響應時間與func 3完全相同。

可視化函數的所有系統調用

要弄清楚函數調用過程發生了什麼,我們可以使用performance_schema / sys模式創建一個帶有 ps_trace_thread procedure的跟蹤。

獲取MySQL連接的thread_id:

为什么你的MySQL性能差?死代码牵连问题解决了吗?

在另一個連接中通過thread_id = 49運行ps_trace_thread:

为什么你的MySQL性能差?死代码牵连问题解决了吗?

此時切換到原始連接(thread_id = 49)並運行:

为什么你的MySQL性能差?死代码牵连问题解决了吗?

sys.ps_trace_thread收集數據(10秒鐘,在此期間我運行了select func1),然後它完成了集合並創建了點文件:

为什么你的MySQL性能差?死代码牵连问题解决了吗?

我為上面的所有函數重複了這些步驟,然後創建了命令圖表。

結果如下:

Func1:

为什么你的MySQL性能差?死代码牵连问题解决了吗?

Func2:

为什么你的MySQL性能差?死代码牵连问题解决了吗?

Func3:

为什么你的MySQL性能差?死代码牵连问题解决了吗?

正如我們所看到的,每個“if”檢查都有一個sp / jump_if_not調用,後跟一個打開表的語句。因此,解析“IF”條件就會對性能產生影響。

對於MySQL 8.0,我們還可以看到存儲過程的MySQL源代碼文檔,這些文檔記錄了它的實現方式,如下:

  • 流量分析優化;

  • 生成代碼後,優化低級sp_instr指令。

優化主要集中在兩方面:

  • 死代碼刪除;

  • 跳轉快捷方式。

這兩個優化是一起執行的,因為它們都涉及圖表中流量分析的問題,代表生成的代碼。實現這些優化的代碼是sp_head :: optimize。

但是,這並不能解釋為什麼會執行“opening tables”,因此,我向官網提交了一個bug——MySQL Bug #91585。

MySQL Bug #91585鏈接:

https://bugs.mysql.com/bug.php?id=91585

“死”代碼如何影響觸發器性能?

當然,如果我們執行的函數不太多,我們可能注意不到這些性能上的差異,但是它會在觸發器內部產生影響。假設我們在表上有一個觸發器,每次更新該表時,它都會執行觸發器來更新字段。比如,假設我們有一個名為“form”的表,我們只需要更新它的創建日期:

为什么你的MySQL性能差?死代码牵连问题解决了吗?

現在我們創建一個觸發器,它將調用我們的虛函數func1:

为什么你的MySQL性能差?死代码牵连问题解决了吗?

請記住:重複更新不會更改新的結果,因為我們沒有在觸發器中做任何事情。

为什么你的MySQL性能差?死代码牵连问题解决了吗?

如果再添加一個虛擬觸發器就會增加兩倍的開銷,即便觸發器沒有運行任何函數,同樣會引起減速:

为什么你的MySQL性能差?死代码牵连问题解决了吗?

現在,讓我們使用func3(它具有“死”代碼,但功能相當於func1):

为什么你的MySQL性能差?死代码牵连问题解决了吗?

如果,我們從觸發器內的函數func3運行代碼(而不是調用函數)將加快更新速度:

为什么你的MySQL性能差?死代码牵连问题解决了吗?

內存分配

即使代碼永遠不會被運行,MySQL仍然需要分析每次執行解析存儲的過程或觸發器代碼,並預先為其留出足夠的內存,這可能會導致內存洩漏,比如MySQL Bug #86821所描述的。

參考鏈接:https://bugs.mysql.com/bug.php?id=86821

結論

存儲過程和觸發器事件在執行時會被解析,即使是永不運行的“死”代碼也會顯著影響批量操作的性能(例如,在觸發器內運行時)。這意味著通過設置“flag”來禁用觸發器(例如,if @trigger_disable =0 then ...)仍然會影響批量操作的性能。

http://blog.itpub.net/31077337/viewspace-2158337

dbaplus社群歡迎廣大技術人員投稿,投稿郵箱:[email protected]

为什么你的MySQL性能差?死代码牵连问题解决了吗?

更多數據庫領域實戰乾貨,盡在2018 Gdevops全球敏捷運維峰會北京站!峰會議題覆蓋AIOps演進、DevOps落地、數據庫選型、SQL優化、技術管理等多方面實戰,全方位為你充電!


分享到:


相關文章: