MySQL“被動”性能優化彙總!

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"年少不知優化苦,遇坑方知優化難。 ——村口王大爺"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文內容導圖如下:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/72/72b6843fc546b594f619612bbccb35d5.png","alt":"image.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我之前有很多文章都在講性能優化的問題,比如下面這些:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/icjByl5g3GvkgqLGIYakgQ","title":""},"content":[{"type":"text","text":"《switch 的性能提升了 3 倍,我只用了這一招!》"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/KQEWT-GAUqRvSv6sDVkUxw","title":""},"content":[{"type":"text","text":"《String性能提升10倍的幾個方法!(源碼+原理分析)》"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/O08zPGhgnWFEBIqh5oUmWw","title":""},"content":[{"type":"text","text":"《局部變量竟然比全局變量快 5 倍?》"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/ZraWOaOdYAJA7TV3Zx60Xw","title":""},"content":[{"type":"text","text":"《池化技術到達有多牛?看了線程和線程池的對比嚇我一跳!》"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/4O9MNTqS8eulExeY-RFKag","title":""},"content":[{"type":"text","text":"《鏈表竟然比數組慢了1000多倍?(動圖+性能評測)》"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/Zz6mofCtmYpABDL1ap04ow","title":""},"content":[{"type":"text","text":"《HashMap 的 7 種遍歷方式與性能分析!》"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/mp/appmsgalbum?_biz=MzU1NTkwODE4Mw==&action=getalbum&albumid=1337160373983690752&subscene=158&scenenote=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU1NTkwODE4Mw%3D%3D%26mid%3D2247485176%26idx%3D1%26sn%3D961fcc0e4dd05a505ef0fc1eb5b10ac4%26chksm%3Dfbcc6bc0","title":""},"content":[{"type":"text","text":"更多性能優化文章"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然,本篇也是關於性能優化的,"},{"type":"text","marks":[{"type":"strong"}],"text":"那性能優化就應該一把梭子嗎?還是要符合一些規範和原則呢?"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/91/915bb95edff2e56f73605d0b6ba5d735.jpeg","alt":"代碼一把梭.jpg","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,在開始之前(MySQL 優化),咱們先來聊聊性能優化的一些原則。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"性能優化原則和分類"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"性能優化一般可以分爲:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主動優化"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"被動優化"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所謂的主動優化是指不需要外力的推動而自發進行的一種行爲,比如當服務沒有明顯的卡頓、宕機或者硬件指標異常的情況下,自我出發去優化的行爲,就可以稱之爲主動優化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/af/af04953632db9918b9f161728e788c0a.gif","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而被動優化剛好與主動優化相反,它是指在發現了服務器卡頓、服務異常或者物理指標異常的情況下,纔去優化的這種行爲。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"性能優化原則"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無論是主動優化還是被動優化都要符合以下性能優化的原則:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"優化不能改變服務運行的邏輯,要保證服務的"},{"type":"text","marks":[{"type":"strong"}],"text":"正確性"},{"type":"text","text":";"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"優化的過程和結果都要保證服務的"},{"type":"text","marks":[{"type":"strong"}],"text":"安全性"},{"type":"text","text":";"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"要保證服務的"},{"type":"text","marks":[{"type":"strong"}],"text":"穩定性"},{"type":"text","text":",不能爲了追求性能犧牲程序的穩定性。比如不能爲了提高 Redis 的運行速度,而關閉持久化的功能,因爲這樣在 Redis 服務器重啓或者掉電之後會丟失存儲的數據。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8d/8d4bccea52352ad2b9e69f8b1fd1ae5b.png","alt":"image.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上原則看似都是些廢話,但卻給了我們一個啓發,那就是我們"},{"type":"text","marks":[{"type":"strong"}],"text":"性能優化手段應該是:預防性能問題爲主+被動優化爲輔"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也就是說,我們應該"},{"type":"text","marks":[{"type":"strong"}],"text":"以預防性能問題爲主"},{"type":"text","text":",在開發階段儘可能的規避性能問題,而"},{"type":"text","marks":[{"type":"strong"}],"text":"在正常情況下,應儘量避免主動優化,以防止未知的風險"},{"type":"text","text":"(除非是爲了 KPI,或者是閒的沒事),尤其對生產環境而言更是如此,最後纔是考慮"},{"type":"text","marks":[{"type":"strong"}],"text":"被動優化"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PS:當遇到性能緩慢下降、或硬件指標緩慢增加的情況,如今天內存的佔用率是 50%,明天是 70%,後天是 90% ,並且絲毫沒有收回的跡象時,我們應該提早發現並處理此類問題(這種情況也屬於被動優化的一種)。"}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"MySQL 被動性能優化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以我們本文會"},{"type":"text","marks":[{"type":"strong"}],"text":"重點介紹 MySQL 被動性能優化的知識,根據被動性能優化的知識,你就可以得到預防性能問題發生的一些方法,從而規避 MySQL 的性能問題"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文我們會從問題入手,然後考慮這個問題產生的原因以及相應的優化方案。我們在實際開發中,通常會遇到以下 3 個問題:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"單條 SQL 運行慢;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"部分 SQL 運行慢;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"整個 SQL 運行慢。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/16/1692812895f5eaf97cb25e0e330d40d4.png","alt":"image.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"問題 1:單條 SQL 運行慢"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"問題分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"造成單條 SQL 運行比較慢的常見原因有以下兩個:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"未正常創建或使用索引;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"表中數據量太大。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解決方案 1:創建並正確使用索引"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"索引"},{"type":"text","text":"是一種能幫助 MySQL 提高查詢效率的主要手段,因此一般情況下我們遇到的單條 SQL 性能問題,通常都是由於未創建或爲正確使用索引而導致的,所以在遇到單條 SQL 運行比較慢的情況下,你"},{"type":"text","marks":[{"type":"strong"}],"text":"首先要做的就是檢查此表的索引是否正常創建"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果表的索引已經創建了,"},{"type":"text","marks":[{"type":"strong"}],"text":"接下來就要檢查一下此 SQL 語句是否正常觸發了索引查詢"},{"type":"text","text":",如果發生以下情況那麼 MySQL 將不能正常的使用索引:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"在 where 子句中使用 != 或者 <> 操作符,查詢引用會放棄索引而進行全表掃描;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"不能使用前導模糊查詢,也就是 '%XX' 或 '%XX%',由於前導模糊不能利用索引的順序,必須一個個去找,看是否滿足條件,這樣會導致全索引掃描或者全表掃描;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"如果條件中有 or 即使其中有條件帶索引也不會正常使用索引,要想使用 or 又想讓索引生效,只能將 or 條件中的每個列都加上索引才能正常使用;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"在 where 子句中對字段進行表達式操作。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"因此你要儘量避免以上情況"},{"type":"text","text":",除了正常使用索引之外,我們也可以"},{"type":"text","marks":[{"type":"strong"}],"text":"使用以下技巧來優化索引的查詢速度"},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"儘量使用主鍵查詢,而非其他索引,因爲主鍵查詢不會觸發回表查詢;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"查詢語句儘可能簡單,大語句拆小語句,減少鎖時間;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"儘量使用數字型字段,若只含數值信息的字段儘量不要設計爲字符型;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"用 exists 替代 in 查詢;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"避免在索引列上使用 is null 和 is not null。"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"回表查詢:普通索引查詢到主鍵索引後,回到主鍵索引樹搜索的過程,我們稱爲回表查詢。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解決方案 2:數據拆分"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當表中數據量太大時 SQL 的查詢會比較慢,你可以考慮拆分表,讓每張表的數據量變小,從而提高查詢效率。"}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"1.垂直拆分"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"指的是將表進行拆分,把一張列比較多的表拆分爲多張表。比如,用戶表中一些字段經常被訪問,將這些字段放在一張表中,另外一些不常用的字段放在另一張表中,插入數據時,使用事務確保兩張表的數據一致性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"垂直拆分的原則:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"把不常用的字段單獨放在一張表;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"把 text,blob 等大字段拆分出來放在附表中;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經常組合查詢的列放在一張表中。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"2.水平拆分"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"指的是將數據錶行進行拆分,表的行數超過200萬行時,就會變慢,這時可以把一張的表的數據拆成多張表來存放。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常情況下,我們使用取模的方式來進行表的拆分,比如,一張有 400W 的用戶表 users,爲提高其查詢效率我們把其分成 4 張表 users1,users2,users3,users4,然後通過用戶 ID 取模的方法,同時查詢、更新、刪除也是通過取模的方法來操作。"}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"表的其他優化方案:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"使用可以存下數據最小的數據類型;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"使用簡單的數據類型,int 要比 varchar 類型在 MySQL 處理簡單;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"儘量使用 tinyint、smallint、mediumint 作爲整數類型而非 int;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"儘可能使用 not null 定義字段,因爲 null 佔用 4 字節空間;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"儘量少用 text 類型,非用不可時最好考慮分表;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"儘量使用 timestamp,而非 datetime;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","text":"單表不要有太多字段,建議在 20 個字段以內。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"問題 2:部分 SQL 運行慢"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"問題分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"部分 SQL 運行比較慢,我們首先要做的就是先定位出這些 SQL,然後再看這些 SQL 是否正確創建並使用索引。也就是說,我們先要使用慢查詢工具定位出具體的 SQL,然後再使用問題 1 的解決方案處理慢 SQL。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解決方案:慢查詢分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"MySQL 中自帶了慢查詢日誌的功能,開啓它就可以用來記錄在 MySQL 中響應時間超過閥值的語句,具體指運行時間超過 long"},{"type":"text","marks":[{"type":"italic"}],"text":"query"},{"type":"text","text":"time 值的 SQL,則會被記錄到慢查詢日誌中。long"},{"type":"text","marks":[{"type":"italic"}],"text":"query"},{"type":"text","text":"time 的默認值爲 10,意思是運行 10S 以上的語句。默認情況下,MySQL 數據庫並不啓動慢查詢日誌,需要我們手動來設置這個參數,如果不是調優需要的話,一般不建議啓動該參數,因爲開啓慢查詢日誌會給 MySQL 服務器帶來一定的性能影響。慢查詢日誌支持將日誌記錄寫入文件,也支持將日誌記錄寫入數據庫表。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 "},{"type":"codeinline","content":[{"type":"text","text":"mysql> show variables like '%slow_query_log%';"}]},{"type":"text","text":" 來查詢慢查詢日誌是否開啓,執行效果如下圖所示:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/17/17b2cba222c0b98186a7860a39d9a566.png","alt":"image.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"slow"},{"type":"text","marks":[{"type":"italic"}],"text":"query"},{"type":"text","text":"log 的值爲 OFF 時,表示未開啓慢查詢日誌。"}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"開啓慢查詢日誌"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開啓慢查詢日誌,可以使用如下 MySQL 命令:"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"mysql> set global slow"},{"type":"text","marks":[{"type":"italic"}],"text":"query"},{"type":"text","text":"log=1"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過這種設置方式,只對當前數據庫生效,如果 MySQL 重啓也會失效,如果要永久生效,就必須修改 MySQL 的配置文件 my.cnf,配置如下:"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"slow"},{"type":"text","marks":[{"type":"italic"}],"text":"query"},{"type":"text","text":"log =1"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"slow"},{"type":"text","marks":[{"type":"italic"}],"text":"query"},{"type":"text","text":"log_file=/tmp/mysql_slow.log"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"當你開啓慢查詢日誌之後,所有的慢查詢 SQL 都會被記錄在 slow_query_log_file 參數配置的文件內,默認是 /tmp/mysql_slow.log 文件"},{"type":"text","text":",此時我們就可以打開日誌查詢到所有慢 SQL 進行逐個優化。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"問題 3:整個 SQL 運行慢"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"問題分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當出現整個 SQL 都運行比較慢就說明目前數據庫的承載能力已經到了峯值,因此我們需要使用一些數據庫的擴展手段來緩解 MySQL 服務器了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"解決方案:讀寫分離"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般情況下對數據庫而言都是“讀多寫少”,換言之,數據庫的壓力多數是因爲大量的讀取數據的操作造成的,我們可以採用數據庫集羣的方案,使用一個庫作爲主庫,負責寫入數據;其他庫爲從庫,負責讀取數據。這樣可以緩解對數據庫的訪問壓力。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"MySQL 常見的讀寫分離方案有以下兩種:"}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"1.應用層解決方案"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以通過應用層對數據源做路由來實現讀寫分離,比如,使用 SpringMVC + MyBatis,可以將 SQL 路由交給 Spring,通過 AOP 或者 Annotation 由代碼顯示的控制數據源。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"優點:路由策略的擴展性和可控性較強。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"缺點:需要在 Spring 中添加耦合控制代碼。"}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"2.中間件解決方案"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過 MySQL 的中間件做主從集羣,比如:Mysql Proxy、Amoeba、Atlas 等中間件都能符合需求。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"優點:與應用層解耦。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"缺點:增加一個服務維護的風險點,性能及穩定性待測試,需要支持代碼強制主從和事務。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"擴展知識:SQL 語句分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 MySQL 中我們可以使用 explain 命令來分析 SQL 的執行情況,比如:"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"explain select * from t where id=5;"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如下圖所示:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/86/8608d550fe3f3400480a43eab62836bc.png","alt":"image.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"id — 選擇標識符,id 越大優先級越高,越先被執行;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"select_type — 表示查詢的類型;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"table — 輸出結果集的表;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"partitions — 匹配的分區;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"type — 表示表的連接類型;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"possible_keys — 表示查詢時,可能使用的索引;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"key — 表示實際使用的索引;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"key_len — 索引字段的長度;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ref—  列與索引的比較;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"rows — 大概估算的行數;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"filtered — 按表條件過濾的行百分比;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Extra — 執行情況的描述和說明。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中最重要的就是 type 字段,type 值類型如下:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"all — 掃描全表數據;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"index — 遍歷索引;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"range — 索引範圍查找;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"index_subquery — 在子查詢中使用 ref;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"unique"},{"type":"text","marks":[{"type":"italic"}],"text":"subquery — 在子查詢中使用 eq"},{"type":"text","text":"ref;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ref"},{"type":"text","marks":[{"type":"italic"}],"text":"or"},{"type":"text","text":"null — 對 null 進行索引的優化的 ref;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"fulltext — 使用全文索引;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ref — 使用非唯一索引查找數據;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"eq_ref — 在 join 查詢中使用主鍵或唯一索引關聯;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"const — 將一個主鍵放置到 where 後面作爲條件查詢, MySQL 優化器就能把這次查詢優化轉化爲一個常量,如何轉化以及何時轉化,這個取決於優化器,這個比 eq_ref 效率高一點。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文我們介紹了 MySQL 性能優化的原則和分類,MySQL 的性能優化可分爲:主動優化和被動優化,但無論何種優化都要保證服務的正確性、安全性和穩定性。它帶給我們的啓發是應該採用:預防 + 被動優化的方案來確保 MySQL 服務器的穩定性,而被動優化常見的問題是:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"單條 SQL 運行慢;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"部分 SQL 運行慢;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"整個 SQL 運行慢。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此我們給出了每種被動優化方案的問題分析和解決方案,希望本文可以幫助到你。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章