1. 程式人生 > 實用技巧 >MySQL效能優化

MySQL效能優化

  在面試中MySQL效能優化是經常被問到的問題,所以有必要去了解MySQL的效能優化。

一、優化思路

  Mysql效能優化就算通過合理安排資源,調整系統引數使MYSQL執行更快,更節省資源。MYSQL效能優化包括查詢速度優化,更新速度優化,mysql伺服器優化等等。

  1. 選擇合適的資料庫引擎
  2. SQL優化
  3. 索引優化
  4. 優化排序
  5. 讀寫分離
  6. 表結構優化
  7. 硬體升級
  8. 使用表分割槽

二、具體分析

  首先來檢視SQL查詢語法順序和執行順序,下面是MySQL查詢語法順序:

  • SELECT
  • FROM
  • LEFT JOIN
  • ON
  • WHERE
  • GROUP BY
  • HAVING
  • ORDER BY
  • LIMIT

  接下來是MySQL查詢執行順序,用一個SQL例項:

  SELECT name,max(age) as age FROM student  s LEFT JOIN grades g ON s.id = g.id WHERE g.math >90  GROUP BY s.name HAVING age > 15 ORDER BY s.age LIMIT 0,10
  1. FROM(將最近的兩張表,進行笛卡爾積)—VT1
  2. ON(將VT1按照它的條件進行過濾)—VT2
  3. LEFT JOIN(保留左表的記錄)—VT3
  4. WHERE(過濾VT3中的記錄)–VT4
  5. GROUP BY(對VT4的記錄進行分組)—VT5
  6. HAVING(對VT5中的記錄進行過濾)—VT6  
  7. SELECT(對VT6中的記錄,選取指定的列)–VT7
  8. ORDER BY(對VT7的記錄進行排序)–遊標
  9. LIMIT(對排序之後的值進行分頁)

WHERE條件執行順序(影響效能)

  • MySQL:從左往右去執行where條件
  • Oracle:從右往左執行where條件

所以寫where條件的時候,優先順序高的部分要去編寫過濾力度最大的條件語句。

MySQL效能優化細節

  1. 合理的建立及使用索引(考慮資料的增刪情況)。
  2. 合理的冗餘欄位(儘量建一些大表,考慮資料庫的三正規化和業務設計的取捨)。
  3. 使用SQL要注意一些細節:select語句中儘量不要使用*、count(*),WHERE語句中儘量不要使用1=1、in語句(建議使用exists)、注意組合索引的建立順序按照順序組著查詢條件、儘量查詢粒度大的SQL放到最左邊、儘量建立組合索引。
  4. 合理利用慢查詢日誌、explain執行計劃查詢、show profile檢視SQL執行時的資源使用情況。
  5. 表關聯查詢時務必遵循小表驅動大表原則。
  6. 使用查詢語where條件時,不允許出現函式,否則索引會失效;
  7. 使用單表查詢時,相同欄位儘量不要用OR,因為可能導致索引失效,比如:SELECT * FROM table WHERE name = '手機' OR name = '電腦',可以使用UNION替代;
  8. LIKE語句不允許使用%開頭,否則索引會失效;
  9. 組合索引一定要遵循從左到右原則,否則索引會失效;比如:SELECT * FROM table WHERE name = '張三' AND age = 18,那麼該組合索引必須是name,age形式;
  10. 索引不宜過多,根據實際情況決定,儘量不要超過 10 個;
  11. 每張表都必須有主鍵,達到加快查詢效率的目的;
  12. 分表,可根據業務欄位尾數中的個位或十位或百位(以此類推)做表名達到分表的目的;
  13. 分庫,可根據業務欄位尾數中的個位或十位或百位(以此類推)做庫名達到分庫的目的;
  14. 表分割槽,類似於硬碟分割槽,可以將某個時間段的資料放在分割槽裡,加快查詢速度,可以配合分表 + 表分割槽結合使用;

索引使用

索引設計原則

  • 最適合索引的列是在where子句中的列,或連線子句中的列,而不是出現在select關鍵字後的列
  • 使用唯一索引。考慮某列中值的分佈。索引列的基數越大,效果越好(一列中相同的資料越少,索引越好)
  • 使用短索引。如果對字串列進行索引,應該指定一個字首長度。這樣可以節省索引空間和磁碟IO。(alter tableName add key indexName (columnName(7)) --給表tableName的columnName欄位的前7位建立字首做引,索引名字為indexName)
  • 利用最左字首。比如建立了一個多列索引 index_c1_c2_c3 (c1,c2,c3),相當於建立了(c1)單列索引,(c1,c2)的組合做引以及(c1,c2,c3)的組合索引。根據這個原則,在建立多列索引時,要根據業務需求 ,where子句中使用最頻繁的一列要放在索引的最左邊。
  • 不要過度索引。索引過多,會導致磁碟佔用較高,insert和update操作耗時增加,查詢優化效率會變低。

最左字首匹配原則

在MySQL中建立聯合索引會遵循最左字首的匹配原則,在檢索資料時從聯合索引的最左邊開始匹配,先建立一個聯合索引

CREATE INDEX index_name ON STUDENT(name,age,sex)

聯合索引實際上建立了name、name,age、name,age,sex三個索引

為什麼要使用聯合索引

  • 減少開銷。建一個聯合索引(col1,col2,col3),實際相當於建了(col1),(col1,col2),(col1,col2,col3)三個索引。每多一個索引,都會增加寫操作的開銷和磁碟空間的開銷。對於大量資料的表,使用聯合索引會大大的減少開銷!
  • 覆蓋索引。對聯合索引(col1,col2,col3),如果有如下的sql: select col1,col2,col3 from test where col1=1 and col2=2。那麼MySQL可以直接通過遍歷索引取得資料,而無需回表,這減少了很多的隨機io操作。減少io操作,特別的隨機io其實是dba主要的優化策略。所以,在真正的實際應用中,覆蓋索引是主要的提升效能的優化手段之一。
  • 效率高。索引列越多,通過索引篩選出的資料越少。有1000W條資料的表,有如下sql:select from table where col1=1 and col2=2 and col3=3,假設假設每個條件可以篩選出10%的資料,如果只有單值索引,那麼通過該索引能篩選出1000W10%=100w條資料,然後再回表從100w條資料中找到符合col2=2 and col3= 3的資料,然後再排序,再分頁;如果是聯合索引,通過索引篩選出1000w10% 10% *10%=1w,效率提升可想而知!

資料庫引擎對比

  • MyISMA是MySQL的預設儲存引擎。MyISMA不支援事務,不支援外來鍵,優勢是訪問速度快,對事務完整性沒有要求或者以SELECT、INSERT為主的應用基本上都可以使用MyISMA引擎。比較適合Web、資料倉儲等場景。
  • InnoDB儲存引擎提供具有提交、回滾和崩潰恢復的事務安全,支援外來鍵。對資料一致性要求比較高或更新比較頻繁的的應用可以選擇InnoDB。比較適合類似計費和財務系統等準確度要求比較高的系統。
  • MEMORY儲存引擎-記憶體資料庫,服務重啟資料會丟失。適用於那些內容變化不頻繁的程式碼表(常量表),或者作為統計結果的中間結果表。修改的資料不會寫入磁碟。
  • MERGE儲存引擎是一組MyISMA表的組合,這些MyISMA表的結構必須完全相同,MERGE表本身沒有資料,對MERGE表的操作實際上是對內部的MyISMA表進行的。較適合資料倉儲。

資料庫架構演變

  剛開始我們只用單機資料庫就夠了,隨後面對越來越多的請求,我們將資料庫的寫操作和讀操作進行分離, 使用多個從庫副本(Slaver Replication)負責讀,使用主庫(Master)負責寫, 從庫從主庫同步更新資料,保持資料一致。架構上就是資料庫主從同步。 從庫可以水平擴充套件,所以更多的讀請求不成問題。但是當用戶量級上來後,寫請求越來越多,該怎麼辦?加一個Master是不能解決問題的, 因為資料要儲存一致性,寫操作需要2個master之間同步,相當於是重複了,而且更加複雜。這時就需要用到分庫分表(sharding),對寫操作進行切分。

  一般就是垂直切分和水平切分,這是一種結果集描述的切分方式,是物理空間上的切分。 我們從面臨的問題,開始解決,闡述: 首先是使用者請求量太大,我們就堆機器搞定(這不是本文重點)。然後是單個庫太大,這時我們要看是因為表多而導致資料多,還是因為單張表裡面的資料多。 如果是因為表多而資料多,使用垂直切分,根據業務切分成不同的庫。

  如果是因為單張表的資料量太大,這時要用水平切分,即把表的資料按某種規則切分成多張表,甚至多個庫上的多張表。分庫分表的順序應該是先垂直分,後水平分。因為垂直分更簡單,更符合我們處理現實世界問題的方式。

表結構優化

垂直拆分

把主鍵和一些常用的欄位放到一個表中,把主鍵和其他的欄位放到另一個表中。
優點:垂直拆分可以使一個數據頁放更多的資料,可以較少IO次數。
缺點:查詢所需的資料可能需要通過JOIN來查詢。
適用場景:表過寬,包含text或blob欄位,可以將不常用的列或text/blob列放到另外的表中儲存。比如文章表可以將文章內容拆分到另外的表中。

水平拆分

根據某一列的值把資料放到多個獨立的表中,比如歷史資料放到另一張表裡。
優點:減少大多數查詢讀取的資料量,降低索引層數,提高查詢速度。
缺點:增加查詢複雜度,查詢多個表需要使用UNION,或者通過MERGE表。
適用場景:表中資料量過大,歷史資料查詢次數很少,比如訂單資訊、操作記錄等。

逆規範化

增加冗餘列:在多個表中具有相同的列,避免聯合查詢
增加派生列:增加的列來自其他表的計算結果,可避免使用函式
重新組表:將經常聯合查詢的表組成一個表,減少聯合查詢

庫結構優化

垂直分庫

  垂直分庫針對的是一個系統中的不同業務進行拆分,比如使用者User一個庫,商品Producet一個庫,訂單Order一個庫。 切分後,要放在多個伺服器上,而不是一個伺服器上。為什麼? 我們想象一下,一個購物網站對外提供服務,會有使用者,商品,訂單等的CRUD。沒拆分之前, 全部都是落到單一的庫上的,這會讓資料庫的單庫處理能力成為瓶頸。按垂直分庫後,如果還是放在一個數據庫伺服器上, 隨著使用者量增大,這會讓單個數據庫的處理能力成為瓶頸,還有單個伺服器的磁碟空間,記憶體,tps等非常吃緊。 所以我們要拆分到多個伺服器上,這樣上面的問題都解決了,以後也不會面對單機資源問題。

  資料庫業務層面的拆分,和服務的“治理”,“降級”機制類似,也能對不同業務的資料分別的進行管理,維護,監控,擴充套件等。 資料庫往往最容易成為應用系統的瓶頸,而資料庫本身屬於“有狀態”的,相對於Web和應用伺服器來講,是比較難實現“橫向擴充套件”的。 資料庫的連線資源比較寶貴且單機處理能力也有限,在高併發場景下,垂直分庫一定程度上能夠突破IO、連線數及單機硬體資源的瓶頸。

水平分庫分表

  將單張表的資料切分到多個伺服器上去,每個伺服器具有相應的庫與表,只是表中資料集合不同。 水平分庫分表能夠有效的緩解單機和單庫的效能瓶頸和壓力,突破IO、連線數、硬體資源等的瓶頸。

水平分庫分表切分規則

  • RANGE:從0到10000一個表,10001到20000一個表;
  • HASH取模:一個商場系統,一般都是將使用者,訂單作為主表,然後將和它們相關的作為附表,這樣不會造成跨庫事務之類的問題。 取使用者id,然後hash取模,分配到不同的資料庫上。
  • 地理區域:比如按照華東,華南,華北這樣來區分業務,七牛雲應該就是如此。
  • 時間:按照時間切分,就是將6個月前,甚至一年前的資料切出去放到另外的一張表,因為隨著時間流逝,這些表的資料 被查詢的概率變小,所以沒必要和“熱資料”放在一起,這個也是“冷熱資料分離”。

分庫分表原則

  • 能不分就不分,1000 萬以內的表,不建議分片,通過合適的索引,讀寫分離等方式,可以很好的解決效能問題。
  • 分片數量儘量少,分片儘量均勻分佈在多個 DataHost 上,因為一個查詢 SQL 跨分片越多,則總體效能越差,雖然要好於所有資料在一個分片的結果,只在必要的時候進行擴容,增加分片數量。
  • 分片規則需要慎重選擇,分片規則的選擇,需要考慮資料的增長模式,資料的訪問模式,分片關聯性問題,以及分片擴容問題,最近的分片策略為範圍分片,列舉分片,一致性 Hash 分片,這幾種分片都有利於擴容。
  • 儘量不要在一個事務中的 SQL 跨越多個分片,分散式事務一直是個不好處理的問題。
  • 儘量不要在一個事務中的 SQL 跨越多個分片,分散式事務一直是個不好處理的問題。