MySQL效能優化之索引優化
什麼是優化?
- 合理安排資源、調整系統引數使MySQL執行更快、更節省資源。
- 優化是多方面的,包括查詢優化、更新優化、伺服器優化等很多方面。沒有特定方式特定的方法,總是要具體場景,具體分析,但是我們要掌握基本的優化手段。
- 原則:減少系統瓶頸,減少資源佔用,增加系統的反應速度。
資料庫效能引數
- 我們可以通過SHOW STATUS語句檢視MySQL資料庫的效能引數
- SHOW STATUS LIKE ‘value’
- 常用的引數:
- Slow_queries 慢查詢次數:
- 什麼是慢查詢? mysql讀寫分離的時候的日誌,裡面記錄了執行某條sql語句超過某個時間後的記錄,方便我們做一個後期的優化,我們可以通過Slow_queries顯示慢查詢
- 檢視你的mysql資料庫是否有慢查詢:SHOW STATUS LIKE ‘Slow_queries’
- 檢視增刪改查次數: Com_insert Com_delete Com_update Com_select
- 檢視上線時間: Uptime
查詢優化
EXPLAIN
- 在MySQL中可以使用EXPLAIN檢視SQL執行計劃,用法:EXPLAIN SELECT * FROM tb_item
結果說明
- id: SELECT識別符。這是SELECT查詢序列號。這個不重要。
- select_type: 表示查詢中每個select子句的型別(簡單 OR複雜)。有以下幾種值:
- SIMPLE 查詢中不包含子查詢或者UNION
- PRIMARY 查詢中若包含任何複雜的子部分,最外層查詢則被標記為:PRIMARY。
- UNION 表示連線查詢的第2個或後面的查詢語句。
- DEPENDENT UNION UNION中的第二個或後面的SELECT語句,取決於外面的查詢。
- UNION RESULT 連線查詢的結果。
- SUBQUERY 子查詢中的第1個SELECT語句。
- DEPENDENT SUBQUERY 子查詢中的第1個SELECT語句,取決於外面的查詢。
- DERIVED SELECT(FROM 子句的子查詢)。
- table 表示查詢的表。
- type(重要) 表示表的連線型別。以下的連線型別的順序是從最佳型別到最差型別:
- system 表僅有一行,這是const型別的特列,平時不會出現,這個也可以忽略不計。
- const 資料表最多隻有一個匹配行,因為只匹配一行資料,所以很快,常用於PRIMARY KEY或者UNIQUE索引的查詢,可理解為const是最優化的。
- eq_ref mysql 手冊是這樣說的:”對於每個來自於前面的表的行組合,從該表中讀取一行。這可能是最好的聯接型別,除了const型別。它用在一個索引的所有部分被聯接使用並且索引是UNIQUE或PRIMARY KEY”。eq_ref可以用於使用=比較帶索引的列。
- ref 查詢條件索引既不是UNIQUE也不是PRIMARY KEY的情況。ref可用於=或<或>操作符的帶索引的列。
- ref_or_null 該聯接型別如同ref,但是添加了MySQL可以專門搜尋包含NULL值的行。在解決子查詢中經常使用該聯接型別的優化。
上面這五種情況都是很理想的索引使用情況 - index_merge 該聯接型別表示使用了索引合併優化方法。在這種情況下,key列包含了使用的索引的清單,key_len包含了使用的索引的最長的關鍵元素。
- unique_subquery 該型別替換了下面形式的IN子查詢的ref: value IN (SELECT primary_key FROM single_table WHERE some_expr) unique_subquery是一個索引查詢函式,可以完全替換子查詢,效率更高。
- index_subquery 該聯接型別類似於unique_subquery。可以替換IN子查詢,但只適合下列形式的子查詢中的非唯一索引: value IN (SELECT key_column FROM single_table WHERE some_expr)
- range 只檢索給定範圍的行,使用一個索引來選擇行。
- index 該聯接型別與ALL相同,除了只有索引樹(B tree 或 b+tree)被掃描。這通常比ALL快,因為索引檔案通常比資料檔案小。
- ALL 對於每個來自於先前的表的行組合,進行完整的表掃描。(效能最差)
- possible_keys 指出MySQL能使用哪個索引在該表中找到行。如果該列為NULL,說明沒有使用索引,可以對該列建立索引來提高效能。
key 顯示MySQL實際決定使用的鍵(索引)。如果沒有選擇索引,鍵是NULL。 可以強制使用索引或者忽略索引:
強制使用索引:USE INDEX(列名)
忽略使用索引:IGNORE INDEX(列名)
資料量小的時候可以強制忽略索引 可以加快速率 走索引也不一定快key_len 顯示MySQL決定使用的鍵長度。如果鍵是NULL,則長度為NULL。 注意:key_len是確定了MySQL將實際使用的索引長度。
- ref 顯示使用哪個列或常數與key一起從表中選擇行。
- rows 顯示MySQL認為它執行查詢時必須檢查的行數。
- Extra 該列包含MySQL解決查詢的詳細資訊
• Distinct:MySQL發現第1個匹配行後,停止為當前的行組合搜尋更多的行。
• Not exists:MySQL能夠對查詢進行LEFT JOIN優化,發現1個匹配LEFT JOIN標準的行後,不再為前面的的行組合在該表內檢查更多的行。
• range checked for each record (index map: #):MySQL沒有發現好的可以使用的索引,但發現如果來自前面的表的列值已知,可能部分索引可以使用。
• Using filesort:MySQL需要額外的一次傳遞,以找出如何按排序順序檢索行。
• Using index:從只使用索引樹中的資訊而不需要進一步搜尋讀取實際的行來檢索表中的列資訊。
• Using temporary:為了解決查詢,MySQL需要建立一個臨時表來容納結果。
• Using where:WHERE 子句用於限制哪一個行匹配下一個表或傳送到客戶。
• Using sort_union(…), Using union(…), Using intersect(…):這些函式說明如何為index_merge聯接型別合併索引掃描。
• Using index for group-by:類似於訪問表的Using index方式,Using index for group-by表示MySQL發現了一個索引,可以用來查 詢GROUP BY或DISTINCT查詢的所有列,而不要額外搜尋硬碟訪問實際的表。
使用索引查詢需要注意
索引可以提供查詢的速度,但並不是使用了帶有索引的欄位查詢都會生效,有些情況下是不生效的,需要注意!
資料庫索引,是資料庫管理系統中一個排序的資料結構,以協助快速查詢、更新資料庫表中資料。索引的實現通常使用B樹(B-tree(多路搜尋樹,並不是二叉的)是一種常見的資料結構)及其變種B+樹。
在資料之外,資料庫系統還維護著滿足特定查詢演算法的資料結構,這些資料結構以某種方式引用(指向)資料,這樣就可以在這些資料結構上實現高階查詢演算法。這種資料結構,就是索引。
為表設定索引要付出代價的:一是增加了資料庫的儲存空間,二是在插入和修改資料時要花費較多的時間(因為索引也要隨之變動)。為什麼使用索引就能加快查詢速度呢?
場景:面試官問你 你有沒有做過資料庫優化啊?有做過,設計的時候給一些查詢欄位加一些索引,為什麼要加索引,為什麼加了索引就快?
索引就是通過事先排好序,從而在查詢時可以應用二分查詢等高效率的演算法。
一般的順序查詢,複雜度為O(n),而二分查詢複雜度為O(log2n)。當n很大時,二者的效率相差及其懸殊。舉個例子:
表中有一百萬條資料,需要在其中尋找一條特定id的資料。如果順序查詢,平均需要查詢50萬條資料。而用二分法,至多不超過20次就能找到。二者的效率差了2.5萬倍!使用LIKE關鍵字的查詢
在使用LIKE關鍵字進行查詢的查詢語句中,如果匹配字串的第一個字元為“%”,索引不起作用。只有“%”不在第一個位置,索引才會生效。使用聯合索引的查詢
MySQL可以為多個欄位建立索引,一個索引可以包括16個欄位。對於聯合索引,只有查詢條件中使用了這些欄位中第一個欄位時,索引才會生效。使用OR關鍵字的查詢
查詢語句的查詢條件中只有OR關鍵字,且OR前後的兩個條件中的列都是索引時,索引才會生效,否則,索引不生效。子查詢優化
MySQL從4.1版本開始支援子查詢,使用子查詢進行SELECT語句巢狀查詢,可以一次完成很多邏輯上需要多個步驟才能完成的SQL操作。子查詢雖然很靈活,但是執行效率並不高。
那麼問題又來了啊? 什麼叫子查詢?為什麼它效率不高?
(把內層查詢結果當作外層查詢的比較條件的)
例子: select goods_id,goods_name from goods where goods_id = (select max(goods_id) from goods);
執行子查詢時,MYSQL需要建立臨時表,查詢完畢後再刪除這些臨時表,所以,子查詢的速度會受到一定的影響。這多了一個建立臨時表和銷燬表的過程啊。- 優化方式:可以使用連線查詢(JOIN)代替子查詢,連線查詢時不需要建立臨時表,其速度比子查詢快。
資料庫結構優化
一個好的資料庫設計方案對於資料庫的效能往往會起到事半功倍的效果。這句話是什麼意思呢,就是說我們的資料庫優化不僅僅要侷限於查詢優化,要從這塊跳出來做好最開始的設計優化,如果你這個主要設計是不合理的這些個查詢優化效果也只是杯水車薪。
需要考慮資料冗餘、查詢和更新的速度、欄位的資料型別是否合理等多方面的內容。
- 將欄位很多的表分解成多個表(分表)
對於欄位較多的表,如果有些欄位的使用頻率很低,可以將這些欄位分離出來形成新表。
因為當一個表的資料量很大時,會由於使用頻率低的欄位的存在而變慢。
專案實戰的時候會將一個完全資訊的表裡面的資料拆分出來 形成多個新表 每個新表負責那一塊的資料查詢 然後這個拆分是定時的 增加中間表
對於需要經常聯合查詢的表,可以建立中間表以提高查詢效率。這個大家能不能理解?通過建立中間表,將需要通過聯合查詢的資料插入到中間表中,然後將原來的聯合查詢改為對中間表的查詢。
舉個例子啊:比如我們需要在五張表裡面做查詢資料,left join 每次查詢要連線五張表是吧 我這裡做一箇中間表 把查詢結果都放在這個中間表裡面,只要在這個表裡面查詢就行了啊
通常都是在統計當中有使用,每次統計報表的時候都是離線統計,後臺有有一個執行緒把你這統計結果查詢好放入一箇中間表,然後你對這個中間表查詢就行了。增加冗餘欄位
設計資料表時應儘量遵循正規化理論的規約,儘可能的減少冗餘欄位,讓資料庫設計看起來精緻、優雅。但是,合理的加入冗餘欄位可以提高查詢速度。
表的規範化程度越高,表和表之間的關係越多,需要連線查詢的情況也就越多,效能也就越差。
注意:
冗餘欄位的值在一個表中修改了,就要想辦法在其他表中更新,否則就會導致資料不一致的問題。
插入資料的優化
插入資料時,影響插入速度的主要是索引、唯一性校驗、一次插入的資料條數等。
為什麼索引會影響插入速度呢?
索引越多,當你寫入資料的時候就會越慢,因為我們在插入資料的時候不只是把資料寫入檔案,而且還要把這個資料寫到索引中,索引索引越多插入越慢
那麼插入資料的優化,根據不同的儲存引擎優化手段不一樣,在MySQL中常用的儲存引擎有,MyISAM和InnoDB,兩者的區別:
MyISAM是MySQL的預設儲存引擎,基於傳統的ISAM型別,支援全文搜尋,但不是事務安全的,而且不支援外來鍵。每張MyISAM表存放在三個檔案中:frm 檔案存放表格定義;資料檔案是MYD (MYData);索引檔案是MYI (MYIndex)。
InnoDB是事務型引擎,支援回滾、崩潰恢復能力、多版本併發控制、ACID事務,支援行級鎖定(InnoDB表的行鎖不是絕對的,如果在執行一個SQL語句時MySQL不能確定要掃描的範圍,InnoDB表同樣會鎖全表,如like操作時的SQL語句),以及提供與Oracle型別一致的不加鎖讀取方式。InnoDB儲存它的表和索引在一個表空間中,表空間可以包含數個檔案。
主要區別:
MyISAM是非事務安全型的,而InnoDB是事務安全型的。
MyISAM鎖的粒度是表級,而InnoDB支援行級鎖定。
MyISAM支援全文型別索引,而InnoDB不支援全文索引。
MyISAM相對簡單,所以在效率上要優於InnoDB,小型應用可以考慮使用MyISAM。
MyISAM表是儲存成檔案的形式,在跨平臺的資料轉移中使用MyISAM儲存會省去不少的麻煩。
InnoDB表比MyISAM表更安全,可以在保證資料不會丟失的情況下,切換非事務表到事務表(alter table tablename type=innodb)。
應用場景:
MyISAM管理非事務表。它提供高速儲存和檢索,以及全文搜尋能力。如果應用中需要執行大量的SELECT查詢,那麼MyISAM是更好的選擇。
InnoDB用於事務處理應用程式,具有眾多特性,包括ACID事務支援。如果應用中需要執行大量的INSERT或UPDATE操作,則應該使用InnoDB,
這樣可以提高多使用者併發操作的效能。
MyISAM
禁用索引
對於非空表,插入記錄時,MySQL會根據表的索引對插入的記錄建立索引。如果插入大量資料,建立索引會降低插入資料速度。
為了解決這個問題,可以在批量插入資料之前禁用索引,資料插入完成後再開啟索引。禁用索引的語句:
ALTER TABLE table_name DISABLE KEYS開啟索引語句:
ALTER TABLE table_name ENABLE KEYS對於空表批量插入資料,則不需要進行操作,因為MyISAM引擎的表是在匯入資料後才建立索引。
禁用唯一性檢查
唯一性校驗會降低插入記錄的速度,可以在插入記錄之前禁用唯一性檢查,插入資料完成後再開啟。禁用唯一性檢查的語句:SET UNIQUE_CHECKS = 0;
開啟唯一性檢查的語句:SET UNIQUE_CHECKS = 1;
批量插入資料
插入資料時,可以使用一條INSERT語句插入一條資料,也可以插入多條資料。批量插入效率更高因為只需要解析一次.使用LOAD DATA INFILE
當需要批量匯入資料時,使用LOAD DATA INFILE語句比INSERT語句插入速度快很多。
InnoDB
- 禁用唯一性檢查
禁用唯一性檢查的語句:SET UNIQUE_CHECKS = 0;
開啟唯一性檢查的語句:SET UNIQUE_CHECKS = 1; - 禁用外來鍵檢查
插入資料之前執行禁止對外來鍵的檢查,資料插入完成後再恢復,可以提供插入速度。
禁用:SET foreign_key_checks = 0;
開啟:SET foreign_key_checks = 1; - 禁止自動提交
插入資料之前執行禁止事務的自動提交,資料插入完成後再恢復,可以提高插入速度。
禁用:SET autocommit = 0;
開啟:SET autocommit = 1;
- 禁用唯一性檢查
伺服器優化
優化伺服器硬體
伺服器的硬體效能直接決定著MySQL資料庫的效能,硬體的效能瓶頸,直接決定MySQL資料庫的執行速度和效率。
需要從以下幾個方面考慮:
1、配置較大的記憶體。足夠大的記憶體,是提高MySQL資料庫效能的方法之一。記憶體的IO比硬碟快的多,可以增加系統的緩衝區容量,使資料在記憶體停留的時間更長,以減少磁碟的IO。
2、配置高速磁碟,比如SSD。
3、合理分配磁碟IO,把磁碟IO分散到多個裝置上,以減少資源的競爭,提高並行操作能力。
4、配置多核處理器,MySQL是多執行緒的資料庫,多處理器可以提高同時執行多個執行緒的能力。MySQL的引數
通過優化MySQL的引數可以提高資源利用率,從而達到提高MySQL伺服器效能的目的。
MySQL的配置引數都在my.conf或者my.ini檔案的[mysqld]組中,常用的引數如下:
必須記憶至少3個