淺談關於SQL優化的思路
零、為什麼要優化
- 系統的吞吐量瓶頸往往出現在資料庫的訪問速度上
- 隨著應用程式的執行,資料庫的中的資料會越來越多,處理時間會相應變慢
- 資料是存放在磁碟上的,讀寫速度無法和記憶體相比
一、觀察
MySQL優化≠SQL語句優化,理解這一點非常重要,雖然大部分時候我們都在調優SQL語句。
然而,MySQL的優化卻是始於觀察,而且有時候觀察幾分鐘,幾小時就能得出結論的,可能要觀察一天以上。
這麼做的目的很明顯,就是為了幫助我們定位問題所在。 比如:MySQL的負載會在固定的某個時間節點突然暴漲,或者一請求某幾個頁面就產生了較為明顯的延遲,甚至影響了隨後的各個請求等。
觀察的手段有多種多樣,阿里雲有強大的RDS
shell
指令碼,收集MySQL
執行狀況。
觀察的指標也不盡相同,最知名的 show status
命令列出的指標就能不下200
個,所以觀察也要有所取捨。
經常受人關注的指標有當前連線數以及最大連線數,當前執行的執行緒數,慢查詢數量等。
二、分析
將觀察的結果做進一步分析,也就形成了不同的解決思路。
可能是某個時間節點快取失效,導致MySQL的負載激增,可以設法將快取失效的時間節點儘可能均勻的分攤在一天24小時中,或者找個訪問量較少的時段重新整理快取。
可能是SQL語句存在潛在問題,在某些情況下會有效能問題,可以用 show full processlist
explain
分析語句執行計劃。可能加個索引能解決問題,也有可能join太多表,需要拆分查詢,也有可能單表體量過大,要拆表了。
可能是機器本身效能問題,所謂“巧婦難為無米之炊”,這個時候要考慮擴容了。
三、解決
在分析階段已經提及了大部分解決手段了,最後總結一下:
1、引入快取,當然,這是一把雙刃劍,要想用的恰如其分,還是需要一定的功力。快取也分兩方面的,一方面是MySQL的內部快取機制,MySQL提供了多種快取引數的配置,比如查詢的結果集快取,結果集排序的快取,可根據實際情況進行調整。另一方面是MySQL之外的快取,比如Redis+MySQL的架構,開啟了Hibernate(Mybatis)的快取功能。快取的引入無非是想減輕MySQL的查詢負擔,但是必須在效能穩定性與資料時效性之間取得平衡。
2、SQL語句有效能問題,這種情況時有發生,通常是上線之前未能做一個完整的基準測試,而只是簡單的功能性測試。當資料量積累到一定程度之後,SQL效能問題就集中爆發出來了。所以,在寫完SQL之後,要養成explain
的習慣,將潛在的效能問題扼殺在萌芽中。當然,我們也要避免“過度優化”,我們要預見得到一張表是讀取次數多,還是更新次數多,資料量會不會爆發性增長,還是增長十分緩慢。當然,寫SQL語句也要遵循一定的原則,比如什麼時候用IN查詢,什麼時候用EXISTS
謂詞,在JOIN
之前是不是可以精簡一部分表資料,建立的索引能否正確派上用場……
3、必要的時候,可以對機器進行擴容,當然系統的整體架構也可以考慮進行優化,搭建MySQL叢集,可靠性和可用性都能得到大幅提升。
四、補充:SQL正規化
1NF
每一個分量必須是不可分的資料項。
特點:
- 有主鍵,且主鍵不能為空。
- 欄位不能再分。
2NF
在正規化一的基礎上,且每一個非主屬性完全函式依賴於碼。
特點:
- 滿足第一正規化。
- 表中的每一個非主屬性,必須完全依賴於本表碼。
- 只有當一個表中,主碼由兩個或以上的屬性組成的時候,才會出現不符合第二正規化的情況。
3NF
在滿足第二正規化的基礎上,且每一個非主屬性既不部分依賴於碼也不傳遞依賴於碼。
特點:
- 滿足第二正規化。
- 非主屬性不能傳遞依賴於碼。
** BCNF**
在滿足第三正規化的基礎上,且不允許主鍵的一部分被另一部分或其它部分決定。
特點:
- 滿足第三正規化。
- 所有非主屬性對每一個碼都是完全函式依賴。
- 所有的主屬性對每一個不包含它的碼,也是完全函式依賴。
- 沒有任何屬性完全函式依賴於飛碼的任何一組屬性。
以上是對MySQL優化的框架性思考。