最近接到反饋,客戶端偶爾出現接口異常。經過初步的日誌排查發現是MySQL的問題,因而針對此次的異常對MySQL進行了一次性能問題追蹤和優化。事情搞定後就一直想寫一篇總結記錄下追蹤的過程以及優化的思路,最後在磨蹭了一個星期後有了這遍筆記。(筆記裏的數據表名和數據都爲過後在本地模擬,可能難以反映出線上當時的真實耗時狀況)
首先說一下問題的情況:客戶端反應的情況是mysql間歇性鏈接超時。問題並不能明顯地反饋在某個sql上,經過查看監控日誌,發現內存間歇性飆升。
最開始懷疑是定時任務的問題,由於業務在後臺有不少定時任務,其中不乏對數據庫大量數據作聚合操做的任務,有一些任務在實現時沒有考慮其對性能的影響,每每會產生間歇性的數據庫性能不穩定。可是經過觀察發現此次內存飆升的時間間隔並不固定,初步排除是定時腳本的問題。
會不會是慢SQL的問題,因爲項目迭代開發速度的問題缺少很好的質量檢測,通常狀況下,這個問題是最常出現的性能問題,此次也不例外,第一時間將慢SQL日誌調出來查看。而後。。。嗯?此次的慢SQL有點多啊,而且有點繁雜,反應出來就是其中某個表 partner
,跟這個表相關的sql所有被標記爲慢SQL。漸漸意識到問題有點嚴重呀!
而後隨便拿出一條慢SQL執行,嗯? 8ms,這是啥慢SQL,這已經要比數據庫的絕大多數哦SQL快了。索性在有一次問題復現時根據當時的 [show processlist]() 反饋,發現有會話數量很不穩定,忽高忽低,在內存飆升的延後幾秒中內每每會有大量的進程。在案發現場,定位到一個條可疑SQL,(根據進程的狀態、執行SQL的時間)。拿出來執行一下,發現確實是慢SQL,而後懷疑是當前內存不足的緣由致使的慢SQL,因而將這條SQL拿到寫數據庫中執行,發現依舊是很慢,因而定位到一個問題,具體是否是它致使了所有問題須要先優化了它再分析,保存了當時的processlist後對這條sql進行優化。
先來看一下這條SQL:select
uid from partners where
id in (1,2,3,"4","5",...)
SQL的結構很簡單,就是根據id搜索partners表的uid,其中uid和id都有索引,id爲主鍵。問題SQL找到了下面就是SQL優化三板斧的工做了。
先進一步經過 profiles 來進一步定位問題。
mysql
Status | Duration | Block_ops_in | Block_ops_out |
---|---|---|---|
starting | 0.000046 | 0 | 0 |
checking permissions | 0.000027 | 0 | 0 |
Opening tables | 0.000049 | 0 | 0 |
init | 0.000038 | 0 | 0 |
System lock | 0.000027 | 0 | 0 |
optimizing | 0.000028 | 0 | 0 |
statistics | 0.000037 | 0 | 0 |
preparing | 0.000030 | 0 | 0 |
executing | 0.000025 | 0 | 0 |
Sending data | 0.146700 | 64 | 1144 |
end | 0.000077 | 0 | 0 |
query end | 0.000027 | 0 | 0 |
closing tables | 0.000031 | 0 | 0 |
freeing items | 0.000049 | 0 | 0 |
cleaning up | 0.000028 | 0 | 0 |
能夠很清楚的看到,語句在Sending data模塊消耗太多時間,而且進行了大量的IO操做。sending data表示收集+發送數據,一般產生的狀況有一下幾種:sql
因爲SQL返回結果只有1條,且不存在大字段,很容易定位到是數據收集階段的問題,因而懷疑到了索引上。
再使用 explain或desc 看一下執行計劃:數據庫
type | possible_key | key | extra |
---|---|---|---|
index | PRIMARY | idx_uid | Using where; Using index |
發現並無使用主鍵也就是id索引。因而問題已經很明顯了。最後給出優化方案。性能優化
在Mysql中id字段爲int型,where條件中使用 in(1, 2, 3)
是能夠命中索引的,使用 in("1","2","3") 經過mysql優化器的優化爲int型後也能夠命中索引,但當二者混用時,mysql不會對它進行優化,致使索引失敗。後面就是業務層的事情了。服務器