MySQL添加索引特點及優化問題
一、索引的特點
當MySQL單表記錄數過大時,增刪改查性能都會急劇下降。MySQL索引的建立對於MySQL的高效運行是很重要的,索引可以大大提高MySQL的檢索速度。除非單表數據未來會一直不斷上漲,否則不要一開始就考慮拆分,拆分會帶來邏輯、部署、運維的各種復雜度。一般以整型值
為主的表在千萬級以下,字符串
為主的表在五百萬以下是沒有太大問題的,而事實上很多時候MySQL單表的性能依然有不少優化空間,甚至能正常支撐千萬級以上的數據量。
索引優勢和劣勢:
- 優勢: 大大減少瞭服務器需要掃描的數據量,可以幫助服務器避免排序和臨時表,實現快速檢索,將隨機I/O變成順序I/O,減少I/O次數,加快檢索速度;根據索引分組和排序,可以加快分組和排序;
- 劣勢: 索引本身也是表,因此會占用存儲空間,一般來說,索引表占用的空間的數據表的1.5倍;索引表的維護和創建需要時間成本,這個成本隨著數據量增大而增大;構建索引會降低數據表的修改操作(刪除,添加,修改)的效率,因為在修改數據表的同時還需要修改索引表;創建索引時需要對表加鎖,因此實際操作中需要在業務空閑期間進行。
二、索引類型
Mysql目前主要有以下幾種索引類型:FULLTEXT,HASH,BTREE,RTREE。
1.FULLTEXT
即為全文索引,目前隻有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不過目前隻有 CHAR、VARCHAR ,TEXT 列上可以創建全文索引。
全文索引並不是和MyISAM一起誕生的,它的出現是為瞭解決WHERE name LIKE “%word%"這類針對文本的模糊查詢效率較低的問題。
FULLTEXT(全文)索引,僅可用於MyISAM和InnoDB
- 對於較大的數據集,把數據添加到一個沒有FULLTEXT索引的表,然後添加FULLTEXT索引的速度比把數據添加到一個已經有FULLTEXT索引的表快。
- 5.6版本前的MySQL自帶的全文索引隻能用於MyISAM存儲引擎,如果是其它數據引擎,那麼全文索引不會生效。5.6版本之後InnoDB存儲引擎開始支持全文索引。
- 在MySQL中,全文索引支隊英文有用,目前對中文還不支持。5.7版本之後通過使用ngram插件開始支持中文。
- 在MySQL中,如果檢索的字符串太短則無法檢索得到預期的結果,檢索的字符串長度至少為4字節,此外,如果檢索的字符包括停止詞,那麼停止詞會被忽略。
2.HASH
哈希索引用索引列的值計算該值的hashCode,然後在hashCode相應的位置存執該值所在行數據的物理位置,因為使用散列算法,因此訪問速度非常快,但是一個值隻能對應一個hashCode,而且是散列的分佈方式。由於HASH的唯一(幾乎100%的唯一)及類似鍵值對的形式,很適合作為索引。
HASH索引可以一次定位,不需要像樹形索引那樣逐層查找,因此具有極高的效率。但是,這種高效是有條件的,即隻在“=”和“in”條件下高效,對於范圍查詢、排序及組合索引仍然效率不高。
3.BTREE
BTREE(B+TREE)索引就是一種將索引值按一定的算法,存入一個樹形的數據結構中(二叉樹),每次查詢都是從樹的入口root開始,依次遍歷node,獲取leaf。由於BTREE非葉子節點不存儲數據(data),因此所有的數據都要查詢至葉子節點,而葉子節點的高度都是相同的,因此所有數據的查詢速度都是一樣的。這是MySQL裡默認和最常用的索引類型。
4.RTREE
RTREE在MySQL很少使用,僅支持geometry數據類型,支持該類型的存儲引擎隻有MyISAM、BDb、InnoDb、NDb、Archive幾種。
相對於BTREE,RTREE的優勢在於范圍查找。
三、索引種類
- 普通索引:僅加速查詢。
- 唯一索引:加速查詢 + 列值唯一(可以有null)。
- 主鍵索引:加速查詢 + 列值唯一(不可以有null)+ 表中隻有一個。
- 組合索引:多列值組成一個索引,專門用於組合搜索,其效率大於索引合並,遵循“最左前綴”原則,把最常用作為檢索或排序的列放在最左,依次遞減,組合索引相當於建立瞭col1,col1col2,col1col2col3三個索引,而col2或者col3是不能使用索引的。
- 全文索引:對文本的內容進行分詞,進行搜索。
四、索引的使用策略
1.什麼時候要使用索引?
主鍵自動建立唯一索引;經常作為查詢條件在WHERE或者ORDER BY;語句中出現的列要建立索引;作為排序的列要建立索引;查詢中與其他表關聯的字段,外鍵關系建立索引高並發條件下傾向組合索引;用於聚合函數的列可以建立索引,例如使用瞭max(column_1)或者count(column_1)時的column_1就需要建立索引。
2.什麼時候不要使用索引?
經常增刪改的列不要建立索引;有大量重復的列不建立索引;表記錄太少不要建立索引。隻有當數據庫裡已經有瞭足夠多的測試數據時,它的性能測試結果才有實際參考價值。如果在測試數據庫裡隻有幾百條數據記錄,它們往往在執行完第一條查詢命令之後就被全部加載到內存裡,這將使後續的查詢命令都執行得非常快–不管有沒有使用索引。隻有當數據庫裡的記錄超過瞭1000條、數據總量也超過瞭MySQL服務器上的內存總量時,數據庫的性能測試結果才有意義。
3.索引失效的情況?
在組合索引中不能有列的值為NULL,如果有,那麼這一列對組合索引就是無效的;在一個SELECT語句中,索引隻能使用一次,如果在WHERE中使用瞭,那麼在ORDER BY中就不要用瞭;LIKE操作中,’%aaa%'不會使用索引,也就是索引會失效,但是’aaa%'可以使用索引;在索引的列上使用表達式或者函數會使索引失效,例如:
select * from table where ceate_time > unix_timestamp(curdate());
將在每個行上進行運算,這將導致索引失效而進行全表掃描,因此我們可以改成當前時間由程序作為參數傳入:
select * from table where ceate_time > 1524561911;
- 其它通配符同樣,也就是說,在查詢條件中使用正則表達式時,隻有在搜索模板的第一個字符不是通配符的情況下才能使用索引;
- 在查詢條件中使用不等於,包括<符號、>符號和!=會導致索引失效。特別的是:如果對主鍵索引使用!=則不會使索引失效,如果對主鍵索引或者整數類型的索引使用<符號或者>符號也不會使索引失效。(不等於,包括<符號、>符號和!,如果占總記錄的比例很小的話,也不會失效);
- 在查詢條件中使用IS NULL或者IS NOT NULL會導致索引失效;
- 字符串不加單引號會導致索引失效。更準確的說是類型不一致會導致失效,比如字段mobile是字符串類型的,使用WHERE mobile=99999 則會導致失敗,應該改為WHERE mobile=‘99999’;
- 在查詢條件中使用OR連接多個條件會導致索引失效,除非OR鏈接的每個條件都加上索引,這時應該改為兩次查詢,然後用UNION ALL連接起來;
- 如果排序的字段使用瞭索引,那麼select的字段也要是索引字段,否則索引失效。特別的是:如果排序的是主鍵索引則select * 也不會導致索引失效;
- 盡量不要包括多列排序,如果一定要,最好為這隊列構建組合索引。
4.mysql查詢優化?
字段:
- 盡量使用TINYINT、SMALLINT、MEDIUM_INT作為整數類型而非INT,如果非負則加上UNSIGNED;
- VARCHAR的長度隻分配真正需要的空間;
- 使用枚舉或整數代替字符串類型;
- 盡量使用TIMESTAMP而非DATETIME;
- 單表不要有太多字段,建議在20以內;
- 避免使用NULL字段,很難查詢優化且占用額外索引空間;
- 用整型來存IP。
索引:
- 索引並不是越多越好,要根據查詢有針對性的創建,考慮在WHERE和ORDER BY命令上涉及的列建立索引,可根據EXPLAIN來查看是否用瞭索引還是全表掃描;
- 應盡量避免在WHERE子句中對字段進行NULL值判斷,否則將導致引擎放棄使用索引而進行全表掃描;
- 值分佈很稀少的字段不適合建索引,例如“性別”這種隻有兩三個值的字段;
- 字符字段隻建前綴索引;
- 字符字段最好不要做主鍵;
- 不用外鍵,由程序保證約束;盡量不用UNIQUE,由程序保證約束;
- 使用多列索引時主意順序和查詢條件保持一致,同時刪除不必要的單列索引。
查詢sql:
- 可通過開啟慢查詢日志來找出較慢的SQL;
- 不做列運算:SELECT id WHERE age + 1 = 10,任何對列的操作都將導致表掃描,它包括數據庫教程函數、計算表達式等等,查詢時要盡可能將操作移至等號右邊;
- sql語句盡可能簡單:一條sql隻能在一個cpu運算;大語句拆小語句,減少鎖時間;一條大sql可以堵死整個庫;
- 不用SELECT *;
- OR改寫成IN:OR的效率是n級別,IN的效率是log(n)級別,IN的個數建議控制在200以內;
- 不用函數和觸發器,在應用程序實現;
- 避免%xxx式查詢,’%xxx%'不會使用索引,可以使用全文索引,然後:
- SELECT * FROM tablename MATCH(index_colum) ANGAINST(‘word’);
- 少用JOIN;
- 使用同類型進行比較,比如用’123’和’123’比,123和123比;
- 組合索引要遵循最做前綴原則,排序分組頻率最高的列放在最左邊,以此類推;
- 盡量避免在WHERE子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描;
- 對於連續數值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5;
- 列表數據不要拿全表,要使用LIMIT來分頁,每頁數量也不要太大;
- 使用短索引,如果可能應該指定一個前綴長度。例如,如果有一個CHAR(255)的 列,如果在前10 個或20 個字符內,多數值是惟一的,那麼就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作。
5.索引的常見問題
1.索引是幹什麼的?
索引用於快速找出在某個列中有一特定值的行。不使用索引,mysql必須從第一條記錄開始讀完整個表直到找出相關的行。表越大,花費的時間越多。如果表中查詢的列有一個索引,mysql能快速到達一個位置搜尋到數據文件的中間,沒有必要查看所有數據。
大多數mysql的索引(primary key、index、unique、fulltext)在B樹中存儲,隻是空間列類型的索引使用R樹,並且memory表還支持hash索引。
2.索引好復雜,我該怎麼理解索引,有沒有一個更形象的例子?
索引就像是一本書的目錄。
3.索引越多越好?
大多數情況下,索引能大幅提高查詢效率。但是:數據變更(增刪改)都需要維護索引,因此更多索引意味著更多維護成本;也意味著需要更多控件空間(一本書100頁,卻有50頁目錄?);過小的表,建索引可能會更慢。
4.索引的字段類型問題
text類型,也可建索引(需要指定長度);MyISAM存儲引擎長度綜合不能超過1000字節;用來篩選的值盡量保持和索引列同樣的數據類型。
5.like能用到索引?
盡量減少like查詢,但是也不是絕對不可用,'xxx%'是可以用到索引的。除瞭like,以下操作符也可以用到索引:
<,<=,=,>,>=,between,in
這些用不到索引:
<>,not in,!=
6.什麼樣的字段不適合建索引?
列的值唯一性太小(比如性別,類型),不適合建索引。(什麼叫大小?一般來說,同值的數據超過表的15%,那就沒有必要建索引瞭)更新非常頻繁的數據不適合建索引。
7.一次查詢能用多個索引?
不能
8.多列查詢該如何建索引?
一次查詢隻能用到一個索引, a列建索引還是b列建索引?誰的區分度(同值的少)更高,建誰!當然,聯合索引也是個不錯的方案。
9.聯合索引的問題
-- 命中col1、col2聯合索引 select col1,col2 from test where col1 = 'xxx'; -- 不能命中col1、col2聯合索引 select col1,col2 from test where col2 = 'xxx';
所以大多數情況下,有col1、col2索引瞭,就不用再去建col1索引瞭
10.哪些常見的情況不能用到索引?
like '%xxx' not in !=
對列進行函數運算,如:
where md5(password) = "xxx"
存瞭數值的字符串類型字段(如手機號),查詢是記得不要丟掉值的引號,否則無法命中索引:
select * from test where mobile = 13800002222;
如果mobile字段是char或者varchar類型,則上面查詢無法命中索引,應為:
select * from test where mobile = '13800002222';
11.NULL的問題
Null會導致索引形同虛設,所以在設計表結構應避免NULL的存在。
可用其他方式來表達,比如-1。
到此這篇關於MySQL添加索引特點及優化問題的文章就介紹到這瞭,更多相關MySQL索引優化內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!