1. 索引
當MySQL單表記錄數過大時,增刪改查性能都會急劇下降。MySQL索引的建立對於MySQL的高效運行是很重要的,索引可以大大提高MySQL的檢索速度。除非單表數據未來會一直不斷上漲,否則不要一開始就考慮拆分,拆分會帶來邏輯、部署、運維的各種復雜度。一般以整型值為主的表在千萬級以下,字符串為主的表在五百萬以下是沒有太大問題的,而事實上很多時候MySQL單表的性能依然有不少優化空間,甚至能正常支撐千萬級以上的數據量。
索引優勢和劣勢:
優勢: 大大減少了服務器需要掃描的數據量,可以幫助服務器避免排序和臨時表,實現快速檢索,將隨機I/O變成順序I/O,減少I/O次數,加快檢索速度;根據索引分組和排序,可以加快分組和排序;
劣勢: 索引本身也是表,因此會占用存儲空間,一般來說,索引表占用的空間的數據表的1.5倍;索引表的維護和創建需要時間成本,這個成本隨着數據量增大而增大;構建索引會降低數據表的修改操作(刪除,添加,修改)的效率,因為在修改數據表的同時還需要修改索引表;創建索引時需要對表加鎖,因此實際操作中需要在業務空閑期間進行。
2. 索引類型
Mysql目前主要有以下幾種索引類型:FULLTEXT,HASH,BTREE,RTREE。
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字節,此外,如果檢索的字符包括停止詞,那么停止詞會被忽略。
HASH
哈希索引用索引列的值計算該值的hashCode,然后在hashCode相應的位置存執該值所在行數據的物理位置,因為使用散列算法,因此訪問速度非常快,但是一個值只能對應一個hashCode,而且是散列的分布方式。由於HASH的唯一(幾乎100%的唯一)及類似鍵值對的形式,很適合作為索引。
HASH索引可以一次定位,不需要像樹形索引那樣逐層查找,因此具有極高的效率。但是,這種高效是有條件的,即只在“=”和“in”條件下高效,對於范圍查詢、排序及組合索引仍然效率不高。
BTREE
BTREE(B+TREE)索引就是一種將索引值按一定的算法,存入一個樹形的數據結構中(二叉樹),每次查詢都是從樹的入口root開始,依次遍歷node,獲取leaf。由於BTREE非葉子節點不存儲數據(data),因此所有的數據都要查詢至葉子節點,而葉子節點的高度都是相同的,因此所有數據的查詢速度都是一樣的。這是MySQL里默認和最常用的索引類型。
RTREE
RTREE在MySQL很少使用,僅支持geometry數據類型,支持該類型的存儲引擎只有MyISAM、BDb、InnoDb、NDb、Archive幾種。
相對於BTREE,RTREE的優勢在於范圍查找。
3. 索引種類
普通索引:僅加速查詢。
唯一索引:加速查詢 + 列值唯一(可以有null)。
主鍵索引:加速查詢 + 列值唯一(不可以有null)+ 表中只有一個。
組合索引:多列值組成一個索引,專門用於組合搜索,其效率大於索引合並,遵循“最左前綴”原則,把最常用作為檢索或排序的列放在最左,依次遞減,組合索引相當於建立了col1,col1col2,col1col2col3三個索引,而col2或者col3是不能使用索引的。
全文索引:對文本的內容進行分詞,進行搜索。
4. 索引的使用策略
4.1 什么時候要使用索引?
- 主鍵自動建立唯一索引;
- 經常作為查詢條件在WHERE或者ORDER BY;
語句中出現的列要建立索引; - 作為排序的列要建立索引;
- 查詢中與其他表關聯的字段,外鍵關系建立索引
- 高並發條件下傾向組合索引;
- 用於聚合函數的列可以建立索引,例如使用了max(column_1)或者count(column_1)時的column_1就需要建立索引。
4.2 什么時候不要使用索引?
- 經常增刪改的列不要建立索引;
- 有大量重復的列不建立索引;
- 表記錄太少不要建立索引。只有當數據庫里已經有了足夠多的測試數據時,它的性能測試結果才有實際參考價值。如果在測試數據庫里只有幾百條數據記錄,它們往往在執行完第一條查詢命令之后就被全部加載到內存里,這將使后續的查詢命令都執行得非常快--不管有沒有使用索引。只有當數據庫里的記錄超過了1000條、數據總量也超過了MySQL服務器上的內存總量時,數據庫的性能測試結果才有意義。
4.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.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操作。
4.5 索引的常見問題
1、索引是干什么的?
索引用於快速找出在某個列中有一特定值的行。不使用索引,mysql必須從第一條記錄開始讀完整個表直到找出相關的行。表越大,花費的時間越多。如果表中查詢的列有一個索引,mysql能快速到達一個位置搜尋到數據文件的中間,沒有必要查看所有數據。
大多數mysql的索引(primary key、index、unique、fulltext)在B樹中存儲,只是空間列類型的索引使用R樹,並且memory表還支持hash索引。
2、索引好復雜,我該怎么理解索引,有沒有一個更形象的例子?
想象一下,你面前有本詞典,數據就是書的正文內容,你就是那個cpu,而索引是書的目錄。
3、索引越多越好?
大多數情況下,索引能大幅提高查詢效率。但是:
數據變更(增刪改)都需要維護索引,因此更多索引意味着更多維護成本;
也意味着需要更多控件空間(一本書100頁,卻有50頁目錄?);
過小的表,建索引可能會更慢(讀2頁的宣傳手冊,你還需要先去找目錄?)
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。
參考:
https://blog.csdn.net/liutong123987/article/details/79384395
https://blog.csdn.net/github_26672553/article/details/82887009
https://blog.csdn.net/tongdanping/article/details/79878302