精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

MySQL如何正確的使用索引

數(shù)據(jù)庫 MySQL
學習索引,主要是寫出更快的sql,當我們寫sql的時候,需要明確的知道sql為什么會走索引?為什么有些sql不走索引?sql會走那些索引,為什么會這么走?我們需要了解其原理,本篇我們就是搞懂這些問題。

學習索引,主要是寫出更快的sql,當我們寫sql的時候,需要明確的知道sql為什么會走索引?為什么有些sql不走索引?sql會走那些索引,為什么會這么走?我們需要了解其原理,了解內(nèi)部具體過程,這樣使用起來才能更順手,才可以寫出更高效的sql。本篇我們就是搞懂這些問題。

讀本篇文章之前,需要先了解一些知識:

  1.  什么是索引?
  2.  mysql索引原理詳解
  3.  mysql索引管理詳解

上面3篇文章沒有讀過的最好去讀一下,不然后面的內(nèi)容會難以理解。

先來回顧一些知識

本篇文章我們以innodb存儲引擎為例來做說明。

mysql采用b+樹的方式存儲索引信息。

b+樹結(jié)構(gòu)如下: 

說一下b+樹的幾個特點:

葉子節(jié)點(最下面的一層)存儲關(guān)鍵字(索引字段的值)信息及對應(yīng)的data,葉子節(jié)點存儲了所有記錄的關(guān)鍵字信息

其他非葉子節(jié)點只存儲關(guān)鍵字的信息及子節(jié)點的指針

每個葉子節(jié)點相當于mysql中的一頁,同層級的葉子節(jié)點以雙向鏈表的形式相連

每個節(jié)點(頁)中存儲了多條記錄,記錄之間用單鏈表的形式連接組成了一條有序的鏈表,順序是按照索引字段排序的

b+樹中檢索數(shù)據(jù)時:每次檢索都是從根節(jié)點開始,一直需要搜索到葉子節(jié)點

InnoDB 的數(shù)據(jù)是按數(shù)據(jù)頁為單位來讀寫的。也就是說,當需要讀取一條記錄的時候,并不是將這個記錄本身從磁盤讀取出來,而是以頁為單位,將整個也加載到內(nèi)存中,一個頁中可能有很多記錄,然后在內(nèi)存中對頁進行檢索。在innodb中,每個頁的大小默認是16kb。

Mysql中索引分為

聚集索引(主鍵索引)

每個表一定會有一個聚集索引,整個表的數(shù)據(jù)存儲以b+樹的方式存在文件中,b+樹葉子節(jié)點中的key為主鍵值,data為完整記錄的信息;非葉子節(jié)點存儲主鍵的值。

通過聚集索引檢索數(shù)據(jù)只需要按照b+樹的搜索過程,即可以檢索到對應(yīng)的記錄。

非聚集索引

每個表可以有多個非聚集索引,b+樹結(jié)構(gòu),葉子節(jié)點的key為索引字段字段的值,data為主鍵的值;非葉子節(jié)點只存儲索引字段的值。

通過非聚集索引檢索記錄的時候,需要2次操作,先在非聚集索引中檢索出主鍵,然后再到聚集索引中檢索出主鍵對應(yīng)的記錄,該過程比聚集索引多了一次操作。

索引怎么走,為什么有些查詢不走索引?為什么使用函數(shù)了數(shù)據(jù)就不走索引了?

這些問題可以先放一下,我們先看一下b+樹檢索數(shù)據(jù)的過程,這個屬于原理的部分,理解了b+樹各種數(shù)據(jù)檢索過程,上面的問題就都可以理解了。

通常說的這個查詢走索引了是什么意思?

當我們對某個字段的值進行某種檢索的時候,如果這個檢索過程中,我們能夠快速定位到目標數(shù)據(jù)所在的頁,有效的降低頁的io操作,而不需要去掃描所有的數(shù)據(jù)頁的時候,我們認為這種情況能夠有效的利用索引,也稱這個檢索可以走索引,如果這個過程中不能夠確定數(shù)據(jù)在那些頁中,我們認為這種情況下索引對這個查詢是無效的,此查詢不走索引。

b+樹中數(shù)據(jù)檢索過程

唯一記錄檢索

如上圖,所有的數(shù)據(jù)都是唯一的,查詢105的記錄,過程如下:

  1.  將P1頁加載到內(nèi)存
  2.  在內(nèi)存中采用二分法查找,可以確定105位于[100,150)中間,所以我們需要去加載100關(guān)聯(lián)P4頁
  3.  將P4加載到內(nèi)存中,采用二分法找到105的記錄后退出

查詢某個值的所有記錄

如上圖,查詢105的所有記錄,過程如下:

  1.  將P1頁加載到內(nèi)存
  2.  在內(nèi)存中采用二分法查找,可以確定105位于[100,150)中間,100關(guān)聯(lián)P4頁
  3.  將P4加載到內(nèi)存中,采用二分法找到最有一個小于105的記錄,即100,然后通過鏈表從100開始向后訪問,找到所有的105記錄,直到遇到第一個大于100的值為止

范圍查找

數(shù)據(jù)如上圖,查詢[55,150]所有記錄,由于頁和頁之間是雙向鏈表升序結(jié)構(gòu),頁內(nèi)部的數(shù)據(jù)是單項升序鏈表結(jié)構(gòu),所以只用找到范圍的起始值所在的位置,然后通過依靠鏈表訪問兩個位置之間所有的數(shù)據(jù)即可,過程如下:

  1.  將P1頁加載到內(nèi)存
  2.  內(nèi)存中采用二分法找到55位于50關(guān)聯(lián)的P3頁中,150位于P5頁中
  3.  將P3加載到內(nèi)存中,采用二分法找到第一個55的記錄,然后通過鏈表結(jié)構(gòu)繼續(xù)向后訪問P3中的60、67,當P3訪問完畢之后,通過P3的nextpage指針訪問下一頁P4中所有記錄,繼續(xù)遍歷P4中的所有記錄,直到訪問到P5中所有的150為止。

模糊匹配

數(shù)據(jù)如上圖。

查詢以`f`開頭的所有記錄

過程如下:

  1.     將P1數(shù)據(jù)加載到內(nèi)存中
  2.     在P1頁的記錄中采用二分法找到最后一個小于等于f的值,這個值是f,以及第一個大于f的,這個值是z,f指向葉節(jié)點P3,z指向葉節(jié)點P6,此時可以斷定以f開頭的記錄可能存在于[P3,P6)這個范圍的頁內(nèi),即P3、P4、P5這三個頁中

      3.     加載P3這個頁,在內(nèi)部以二分法找到第一條f開頭的記錄,然后以鏈表方式繼續(xù)向后訪問P4、P5中的記錄,即可以找到所有已f開頭的數(shù)據(jù)

查詢包含`f`的記錄

包含的查詢在sql中的寫法是%f%,通過索引我們還可以快速定位所在的頁么?

可以看一下上面的數(shù)據(jù),f在每個頁中都存在,我們通過P1頁中的記錄是無法判斷包含f的記錄在那些頁的,只能通過io的方式加載所有葉子節(jié)點,并且遍歷所有記錄進行過濾,才可以找到包含f的記錄。

所以如果使用了%值%這種方式,索引對查詢是無效的。

最左匹配原則

當b+樹的數(shù)據(jù)項是復(fù)合的數(shù)據(jù)結(jié)構(gòu),比如(name,age,sex)的時候,b+樹是按照從左到右的順序來建立搜索樹的,比如當(張三,20,F)這樣的數(shù)據(jù)來檢索的時候,b+樹會優(yōu)先比較name來確定下一步的所搜方向,如果name相同再依次比較age和sex,最后得到檢索的數(shù)據(jù);但當(20,F)這樣的沒有name的數(shù)據(jù)來的時候,b+樹就不知道下一步該查哪個節(jié)點,因為建立搜索樹的時候name就是第一個比較因子,必須要先根據(jù)name來搜索才能知道下一步去哪里查詢。比如當(張三,F)這樣的數(shù)據(jù)來檢索時,b+樹可以用name來指定搜索方向,但下一個字段age的缺失,所以只能把名字等于張三的數(shù)據(jù)都找到,然后再匹配性別是F的數(shù)據(jù)了, 這個是非常重要的性質(zhì),即索引的最左匹配特性。

來一些示例我們體驗一下。

下圖中是3個字段(a,b,c)的聯(lián)合索引,索引中數(shù)據(jù)的順序是以a asc,b asc,c asc這種排序方式存儲在節(jié)點中的,索引先以a字段升序,如果a相同的時候,以b字段升序,b相同的時候,以c字段升序,節(jié)點中每個數(shù)據(jù)認真看一下。

查詢a=1的記錄

由于頁中的記錄是以a asc,b asc,c asc這種排序方式存儲的,所以a字段是有序的,可以通過二分法快速檢索到,過程如下:

  1.  將P1加載到內(nèi)存中
  2.  在內(nèi)存中對P1中的記錄采用二分法找,可以確定a=1的記錄位于{1,1,1}和{1,5,1}關(guān)聯(lián)的范圍內(nèi),這兩個值子節(jié)點分別是P2、P4
  3.  加載葉子節(jié)點P2,在P2中采用二分法快速找到第一條a=1的記錄,然后通過鏈表向下一條及下一頁開始檢索,直到在P4中找到第一個不滿足a=1的記錄為止

查詢a=1 and b=5的記錄

方法和上面的一樣,可以確定a=1 and b=5的記錄位于{1,1,1}和{1,5,1}關(guān)聯(lián)的范圍內(nèi),查找過程和a=1查找步驟類似。

查詢b=1的記錄

這種情況通過P1頁中的記錄,是無法判斷b=1的記錄在那些頁中的,只能加鎖索引樹所有葉子節(jié)點,對所有記錄進行遍歷,然后進行過濾,此時索引是無效的。

按照c的值查詢

這種情況和查詢b=1也一樣,也只能掃描所有葉子節(jié)點,此時索引也無效了。

按照b和c一起查

這種也是無法利用索引的,也只能對所有數(shù)據(jù)進行掃描,一條條判斷了,此時索引無效。

按照[a,c]兩個字段查詢

這種只能利用到索引中的a字段了,通過a確定索引范圍,然后加載a關(guān)聯(lián)的所有記錄,再對c的值進行過濾。

查詢a=1 and b>=0 and c=1的記錄

這種情況只能先確定a=1 and b>=0所在頁的范圍,然后對這個范圍的所有頁進行遍歷,c字段在這個查詢的過程中,是無法確定c的數(shù)據(jù)在哪些頁的,此時我們稱c是不走索引的,只有a、b能夠有效的確定索引頁的范圍。

類似這種的還有>、<、between and,多字段索引的情況下,mysql會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配。

上面說的各種情況,大家都多看一下圖中數(shù)據(jù),認真分析一下查詢的過程,基本上都可以理解了。

上面這種查詢叫做最左匹配原則。

索引區(qū)分度

我們看2個有序數(shù)組

[1,2,3,4,5,6,7,8,8,9,10]

[1,1,1,1,1,8,8,8,8,8]

上面2個數(shù)組是有序的,都是10條記錄,如果我需要檢索值為8的所有記錄,那個更快一些?

咱們使用二分法查找包含8的所有記錄過程如下:先使用二分法找到最后一個小于8的記錄,然后沿著這條記錄向后獲取下一個記錄,和8對比,知道遇到第一個大于8的數(shù)字結(jié)束,或者到達數(shù)組末尾結(jié)束。

采用上面這種方法找到8的記錄,第一個數(shù)組中更快的一些。因為第二個數(shù)組中含有8的比例更多的,需要訪問以及匹配的次數(shù)更多一些。

這里就涉及到數(shù)據(jù)的區(qū)分度問題:

索引區(qū)分度 = count(distint 記錄) / count(記錄)。

當索引區(qū)分度高的時候,檢索數(shù)據(jù)更快一些,索引區(qū)分度太低,說明重復(fù)的數(shù)據(jù)比較多,檢索的時候需要訪問更多的記錄才能夠找到所有目標數(shù)據(jù)。

當索引區(qū)分度非常小的時候,基本上接近于全索引數(shù)據(jù)的掃描了,此時查詢速度是比較慢的。

第一個數(shù)組索引區(qū)分度為1,第二個區(qū)分度為0.2,所以第一個檢索更快的一些。

所以我們創(chuàng)建索引的時候,盡量選擇區(qū)分度高的列作為索引。

正確使用索引

準備400萬測試數(shù)據(jù) 

  1. /*建庫javacode2018*/DROP DATABASE IF EXISTS javacode2018;CREATE DATABASE javacode2018;USE javacode2018;/*建表test1*/DROP TABLE IF EXISTS test1;CREATE TABLE test1 ( id INT NOT NULL COMMENT '編號', name VARCHAR(20) NOT NULL COMMENT '姓名', sex TINYINT NOT NULL COMMENT '性別,1:男,2:女', email VARCHAR(50));/*準備數(shù)據(jù)*/DROP PROCEDURE IF EXISTS proc1;DELIMITER $CREATE PROCEDURE proc1() BEGIN DECLARE i INT DEFAULT 1; START TRANSACTION; WHILE i <= 4000000 DO 
  2.   INSERT INTO test1 (id, name, sex, email) VALUES (i,concat('javacode',i),if(mod(i,2),1,2),concat('javacode',i,'@163.com'));  
  3.  SET ii = i + 1;  
  4.  if i%10000=0 THEN  
  5.  COMMIT;  
  6.  START TRANSACTION;  
  7.  END IF;  
  8.  END WHILE;  
  9.  COMMIT;  
  10.  END $  
  11. DELIMITER ;  
  12. CALL proc1(); 

上面插入的400萬數(shù)據(jù),除了sex列,其他列的值都是沒有重復(fù)的。

無索引檢索效果

400萬數(shù)據(jù),我們隨便查詢幾個記錄看一下效果。

按照id查詢記錄 

  1. mysql> select * from test1 where id = 1 
  2. +----+-----------+-----+-------------------+  
  3. | id | name | sex | email |  
  4. +----+-----------+-----+-------------------+  
  5. | 1 | javacode1 | 1 | javacode1@163.com |  
  6. +----+-----------+-----+-------------------+  
  7. 1 row in set (1.91 sec) 

id=1的數(shù)據(jù),表中只有一行,耗時近2秒,由于id列無索引,只能對400萬數(shù)據(jù)進行全表掃描。

主鍵檢索

test1表中沒有明確的指定主鍵,我們將id設(shè)置為主鍵: 

  1. mysql> alter table test1 modify id int not null primary key;  
  2. Query OK, 0 rows affected (10.93 sec)  
  3. Records: 0 Duplicates: 0 Warnings: 0  
  4. mysql> show index from test1;  
  5. +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+  
  6. | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |  
  7. +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+  
  8. | test1 | 0 | PRIMARY | 1 | id | A | 3980477 | NULL | NULL | | BTREE | | |  
  9. +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+  
  10. 1 row in set (0.00 sec) 

id被置為主鍵之后,會在id上建立聚集索引,隨便檢索一條我們看一下效果: 

  1. mysql> select * from test1 where id = 1000000 
  2. +---------+-----------------+-----+-------------------------+  
  3. | id | name | sex | email |  
  4. +---------+-----------------+-----+-------------------------+  
  5. | 1000000 | javacode1000000 | 2 | javacode1000000@163.com |  
  6. +---------+-----------------+-----+-------------------------+  
  7. 1 row in set (0.00 sec) 

這個速度很快,這個走的是上面介紹的`唯一記錄檢索`。

between and范圍檢索 

  1. mysql> select count(*) from test1 where id between 100 and 110;  
  2. +----------+  
  3. | count(*) |  
  4. +----------+  
  5. | 11 |  
  6. +----------+  
  7. 1 row in set (0.00 sec) 

速度也很快,id上有主鍵索引,這個采用的上面介紹的范圍查找可以快速定位目標數(shù)據(jù)。

但是如果范圍太大,跨度的page也太多,速度也會比較慢,如下: 

  1. mysql> select count(*) from test1 where id between 1 and 2000000;  
  2. +----------+  
  3. | count(*) |  
  4. +----------+  
  5. | 2000000 |  
  6. +----------+  
  7. 1 row in set (1.17 sec) 

上面id的值跨度太大,1所在的頁和200萬所在頁中間有很多頁需要讀取,所以比較慢。

所以使用between and的時候,區(qū)間跨度不要太大。

in的檢索

in方式檢索數(shù)據(jù),我們還是經(jīng)常用的。

平時我們做項目的時候,建議少用表連接,比如電商中需要查詢訂單的信息和訂單中商品的名稱,可以先查詢查詢訂單表,然后訂單表中取出商品的id列表,采用in的方式到商品表檢索商品信息,由于商品id是商品表的主鍵,所以檢索速度還是比較快的。

通過id在400萬數(shù)據(jù)中檢索100條數(shù)據(jù),看看效果: 

  1. mysql> select * from test1 a where a.id in (100000, 100001, 100002, 100003, 100004, 100005, 100006, 100007, 100008, 100009, 100010, 100011, 100012, 100013, 100014, 100015, 100016, 100017, 100018, 100019, 100020, 100021, 100022, 100023, 100024, 100025, 100026, 100027, 100028, 100029, 100030, 100031, 100032, 100033, 100034, 100035, 100036, 100037, 100038, 100039, 100040, 100041, 100042, 100043, 100044, 100045, 100046, 100047, 100048, 100049, 100050, 100051, 100052, 100053, 100054, 100055, 100056, 100057, 100058, 100059, 100060, 100061, 100062, 100063, 100064, 100065, 100066, 100067, 100068, 100069, 100070, 100071, 100072, 100073, 100074, 100075, 100076, 100077, 100078, 100079, 100080, 100081, 100082, 100083, 100084, 100085, 100086, 100087, 100088, 100089, 100090, 100091, 100092, 100093, 100094, 100095, 100096, 100097, 100098, 100099); 
  2. +--------+----------------+-----+------------------------+  
  3. | id | name | sex | email |  
  4. +--------+----------------+-----+------------------------+  
  5. | 100000 | javacode100000 | 2 | javacode100000@163.com |  
  6. | 100001 | javacode100001 | 1 | javacode100001@163.com |  
  7. | 100002 | javacode100002 | 2 | javacode100002@163.com |  
  8. .......  
  9. | 100099 | javacode100099 | 1 | javacode100099@163.com |  
  10. +--------+----------------+-----+------------------------+  
  11. 100 rows in set (0.00 sec) 

耗時不到1毫秒,還是相當快的。

這個相當于多個分解為多個唯一記錄檢索,然后將記錄合并。

多個索引時查詢?nèi)绾巫撸?/strong>

我們在name、sex兩個字段上分別建個索引 

  1. mysql> create index idx1 on test1(name);  
  2. Query OK, 0 rows affected (13.50 sec)  
  3. Records: 0 Duplicates: 0 Warnings: 0  
  4. mysql> create index idx2 on test1(sex);  
  5. Query OK, 0 rows affected (6.77 sec)  
  6. Records: 0 Duplicates: 0 Warnings: 0 

看一下查詢: 

  1. mysql> select * from test1 where name='javacode3500000' and sex=2 
  2. +---------+-----------------+-----+-------------------------+  
  3. | id | name | sex | email |  
  4. +---------+-----------------+-----+-------------------------+  
  5. | 3500000 | javacode3500000 | 2 | javacode3500000@163.com |  
  6. +---------+-----------------+-----+-------------------------+  
  7. 1 row in set (0.00 sec) 

上面查詢速度很快,name和sex上各有一個索引,覺得上面走哪個索引?

有人說name位于where第一個,所以走的是name字段所在的索引,過程可以解釋為這樣:

走name所在的索引找到j(luò)avacode3500000對應(yīng)的所有記錄

遍歷記錄過濾出sex=2的值

我們看一下name='javacode3500000'檢索速度,確實很快,如下: 

  1. mysql> select * from test1 where name='javacode3500000' 
  2. +---------+-----------------+-----+-------------------------+  
  3. | id | name | sex | email |  
  4. +---------+-----------------+-----+-------------------------+  
  5. | 3500000 | javacode3500000 | 2 | javacode3500000@163.com |  
  6. +---------+-----------------+-----+-------------------------+  
  7. 1 row in set (0.00 sec) 

走name索引,然后再過濾,確實可以,速度也很快,果真和where后字段順序有關(guān)么?我們把name和sex的順序?qū)φ{(diào)一下,如下: 

  1. mysql> select * from test1 where sex=2 and name='javacode3500000' 
  2. +---------+-----------------+-----+-------------------------+  
  3. | id | name | sex | email |  
  4. +---------+-----------------+-----+-------------------------+  
  5. | 3500000 | javacode3500000 | 2 | javacode3500000@163.com |  
  6. +---------+-----------------+-----+-------------------------+  
  7. 1 row in set (0.00 sec) 

速度還是很快,這次是不是先走sex索引檢索出數(shù)據(jù),然后再過濾name呢?我們先來看一下sex=2查詢速度: 

  1. mysql> select count(id) from test1 where sex=2 
  2. +-----------+  
  3. | count(id) |  
  4. +-----------+  
  5. | 2000000 |  
  6. +-----------+  
  7. 1 row in set (0.36 sec) 

看上面,查詢耗時360毫秒,200萬數(shù)據(jù),如果走sex肯定是不行的。

我們使用explain來看一下: 

  1. mysql> explain select * from test1 where sex=2 and name='javacode3500000' 
  2. +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+  
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |  
  4. +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+  
  5. | 1 | SIMPLE | test1 | NULL | ref | idx1,idx2 | idx1 | 62 | const | 1 | 50.00 | Using where |  
  6. +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+  
  7. 1 row in set, 1 warning (0.00 sec) 

possible_keys:列出了這個查詢可能會走兩個索引(idx1、idx2)

實際上走的卻是idx1(key列:實際走的索引)。

當多個條件中有索引的時候,并且關(guān)系是and的時候,會走索引區(qū)分度高的,顯然name字段重復(fù)度很低,走name查詢會更快一些。

模糊查詢

看兩個查詢 

  1. mysql> select count(*) from test1 a where a.name like 'javacode1000%';  
  2. +----------+  
  3. | count(*) |  
  4. +----------+  
  5. | 1111 |  
  6. +----------+  
  7. 1 row in set (0.00 sec)  
  8. mysql> select count(*) from test1 a where a.name like '%javacode1000%';  
  9. +----------+  
  10. | count(*) |  
  11. +----------+  
  12. | 1111 |  
  13. +----------+  
  14. 1 row in set (1.78 sec) 

上面第一個查詢可以利用到name字段上面的索引,下面的查詢是無法確定需要查找的值所在的范圍的,只能全表掃描,無法利用索引,所以速度比較慢,這個過程上面有說過。

回表

當需要查詢的數(shù)據(jù)在索引樹中不存在的時候,需要再次到聚集索引中去獲取,這個過程叫做回表,如查詢: 

  1. mysql> select * from test1 where name='javacode3500000' 
  2. +---------+-----------------+-----+-------------------------+  
  3. | id | name | sex | email |  
  4. +---------+-----------------+-----+-------------------------+  
  5. | 3500000 | javacode3500000 | 2 | javacode3500000@163.com |  
  6. +---------+-----------------+-----+-------------------------+  
  7. 1 row in set (0.00 sec) 

上面查詢是*,由于name列所在的索引中只有name、id兩個列的值,不包含sex、email,所以上面過程如下:

走name索引檢索javacode3500000對應(yīng)的記錄,取出id為3500000

在主鍵索引中檢索出id=3500000的記錄,獲取所有字段的值

索引覆蓋

查詢中采用的索引樹中包含了查詢所需要的所有字段的值,不需要再去聚集索引檢索數(shù)據(jù),這種叫索引覆蓋。

我們來看一個查詢: 

  1. select id,name from test1 where name='javacode3500000'

name對應(yīng)idx1索引,id為主鍵,所以idx1索引樹葉子節(jié)點中包含了name、id的值,這個查詢只用走idx1這一個索引就可以了,如果select后面使用*,還需要一次回表獲取sex、email的值。

所以寫sql的時候,盡量避免使用*,*可能會多一次回表操作,需要看一下是否可以使用索引覆蓋來實現(xiàn),效率更高一些。

索引下推

簡稱ICP,Index Condition Pushdown(ICP)是MySQL 5.6中新特性,是一種在存儲引擎層使用索引過濾數(shù)據(jù)的一種優(yōu)化方式,ICP可以減少存儲引擎訪問基表的次數(shù)以及MySQL服務(wù)器訪問存儲引擎的次數(shù)。

舉個例子來說一下:

我們需要查詢name以javacode35開頭的,性別為1的記錄數(shù),sql如下: 

  1. mysql> select count(id) from test1 a where name like 'javacode35%' and sex = 1 
  2. +-----------+  
  3. | count(id) |  
  4. +-----------+  
  5. | 55556 |  
  6. +-----------+  
  7. 1 row in set (0.19 sec) 

過程:

走name索引檢索出以javacode35的第一條記錄,得到記錄的id

利用id去主鍵索引中查詢出這條記錄R1

判斷R1中的sex是否為1,然后重復(fù)上面的操作,直到找到所有記錄為止。

上面的過程中需要走name索引以及需要回表操作。

如果采用ICP的方式,我們可以這么做,創(chuàng)建一個(name,sex)的組合索引,查詢過程如下:

走(name,sex)索引檢索出以javacode35的第一條記錄,可以得到(name,sex,id),記做R1

判斷R1.sex是否為1,然后重復(fù)上面的操作,知道找到所有記錄為止

這個過程中不需要回表操作了,通過索引的數(shù)據(jù)就可以完成整個條件的過濾,速度比上面的更快一些。

數(shù)字使字符串類索引失效 

  1. mysql> insert into test1 (id,name,sex,email) values (4000001,'1',1,'javacode2018@163.com');  
  2. Query OK, 1 row affected (0.00 sec)  
  3. mysql> select * from test1 where name = '1' 
  4. +---------+------+-----+----------------------+  
  5. | id | name | sex | email |  
  6. +---------+------+-----+----------------------+  
  7. | 4000001 | 1 | 1 | javacode2018@163.com |  
  8. +---------+------+-----+----------------------+  
  9. 1 row in set (0.00 sec)  
  10. mysql> select * from test1 where name = 1 
  11. +---------+------+-----+----------------------+  
  12. | id | name | sex | email |  
  13. +---------+------+-----+----------------------+  
  14. | 4000001 | 1 | 1 | javacode2018@163.com |  
  15. +---------+------+-----+----------------------+  
  16. 1 row in set, 65535 warnings (3.30 sec) 

上面3條sql,我們插入了一條記錄。

第二條查詢很快,第三條用name和1比較,name上有索引,name是字符串類型,字符串和數(shù)字比較的時候,會將字符串強制轉(zhuǎn)換為數(shù)字,然后進行比較,所以第二個查詢變成了全表掃描,只能取出每條數(shù)據(jù),將name轉(zhuǎn)換為數(shù)字和1進行比較。

數(shù)字字段和字符串比較什么效果呢?如下: 

  1. mysql> select * from test1 where id = '4000000' 
  2. +---------+-----------------+-----+-------------------------+  
  3. | id | name | sex | email |  
  4. +---------+-----------------+-----+-------------------------+  
  5. | 4000000 | javacode4000000 | 2 | javacode4000000@163.com |  
  6. +---------+-----------------+-----+-------------------------+  
  7. 1 row in set (0.00 sec)  
  8. mysql> select * from test1 where id = 4000000 
  9. +---------+-----------------+-----+-------------------------+  
  10. | id | name | sex | email |  
  11. +---------+-----------------+-----+-------------------------+  
  12. | 4000000 | javacode4000000 | 2 | javacode4000000@163.com |  
  13. +---------+-----------------+-----+-------------------------+  
  14. 1 row in set (0.00 sec) 

id上面有主鍵索引,id是int類型的,可以看到,上面兩個查詢都非常快,都可以正常利用索引快速檢索,所以如果字段是數(shù)組類型的,查詢的值是字符串還是數(shù)組都會走索引。

函數(shù)使索引無效 

  1. mysql> select a.name+1 from test1 a where a.name = 'javacode1' 
  2. +----------+  
  3. | a.name+1 |  
  4. +----------+  
  5. | 1 |  
  6. +----------+  
  7. 1 row in set, 1 warning (0.00 sec)  
  8. mysql> select * from test1 a where concat(a.name,'1') = 'javacode11';  
  9. +----+-----------+-----+-------------------+  
  10. | id | name | sex | email |  
  11. +----+-----------+-----+-------------------+  
  12. | 1 | javacode1 | 1 | javacode1@163.com |  
  13. +----+-----------+-----+-------------------+  
  14. 1 row in set (2.88 sec) 

name上有索引,上面查詢,第一個走索引,第二個不走索引,第二個使用了函數(shù)之后,name所在的索引樹是無法快速定位需要查找的數(shù)據(jù)所在的頁的,只能將所有頁的記錄加載到內(nèi)存中,然后對每條數(shù)據(jù)使用函數(shù)進行計算之后再進行條件判斷,此時索引無效了,變成了全表數(shù)據(jù)掃描。

結(jié)論:索引字段使用函數(shù)查詢使索引無效。

運算符使索引無效 

  1. mysql> select * from test1 a where id = 2 - 1;  
  2. +----+-----------+-----+-------------------+  
  3. | id | name | sex | email |  
  4. +----+-----------+-----+-------------------+  
  5. | 1 | javacode1 | 1 | javacode1@163.com |  
  6. +----+-----------+-----+-------------------+  
  7. 1 row in set (0.00 sec)  
  8. mysql> select * from test1 a where id+1 = 2 
  9. +----+-----------+-----+-------------------+  
  10. | id | name | sex | email |  
  11. +----+-----------+-----+-------------------+  
  12. | 1 | javacode1 | 1 | javacode1@163.com |  
  13. +----+-----------+-----+-------------------+  
  14. 1 row in set (2.41 sec) 

id上有主鍵索引,上面查詢,第一個走索引,第二個不走索引,第二個使用運算符,id所在的索引樹是無法快速定位需要查找的數(shù)據(jù)所在的頁的,只能將所有頁的記錄加載到內(nèi)存中,然后對每條數(shù)據(jù)的id進行計算之后再判斷是否等于1,此時索引無效了,變成了全表數(shù)據(jù)掃描。

結(jié)論:索引字段使用了函數(shù)將使索引無效。

使用索引優(yōu)化排序

我們有個訂單表t_order(id,user_id,addtime,price),經(jīng)常會查詢某個用戶的訂單,并且按照addtime升序排序,應(yīng)該怎么創(chuàng)建索引呢?我們來分析一下。

在user_id上創(chuàng)建索引,我們分析一下這種情況,數(shù)據(jù)檢索的過程:

  1.  走user_id索引,找到記錄的的id
  2.  通過id在主鍵索引中回表檢索出整條數(shù)據(jù)
  3.  重復(fù)上面的操作,獲取所有目標記錄
  4.  在內(nèi)存中對目標記錄按照addtime進行排序

我們要知道當數(shù)據(jù)量非常大的時候,排序還是比較慢的,可能會用到磁盤中的文件,有沒有一種方式,查詢出來的數(shù)據(jù)剛好是排好序的。

我們再回顧一下mysql中b+樹數(shù)據(jù)的結(jié)構(gòu),記錄是按照索引的值排序組成的鏈表,如果將user_id和addtime放在一起組成聯(lián)合索引(user_id,addtime),這樣通過user_id檢索出來的數(shù)據(jù)自然就是按照addtime排好序的,這樣直接少了一步排序操作,效率更好,如果需addtime降序,只需要將結(jié)果翻轉(zhuǎn)一下就可以了。

總結(jié)一下使用索引的一些建議

  1. 在區(qū)分度高的字段上面建立索引可以有效的使用索引,區(qū)分度太低,無法有效的利用索引,可能需要掃描所有數(shù)據(jù)頁,此時和不使用索引差不多
  2. 聯(lián)合索引注意最左匹配原則:必須按照從左到右的順序匹配,mysql會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調(diào)整

      3. 查詢記錄的時候,少使用*,盡量去利用索引覆蓋,可以減少回表操作,提升效率

      4. 有些查詢可以采用聯(lián)合索引,進而使用到索引下推(IPC),也可以減少回表操作,提升效率

      5. 禁止對索引字段使用函數(shù)、運算符操作,會使索引失效

      6. 字符串字段和數(shù)字比較的時候會使索引無效

      7. 模糊查詢'%值%'會使索引無效,變?yōu)槿頀呙瑁?值%'這種可以有效利用索引

      8. 排序中盡量使用到索引字段,這樣可以減少排序,提升查詢效率 

 

責任編輯:龐桂玉 來源: 今日頭條
相關(guān)推薦

2017-08-30 17:47:35

MySql索引

2010-07-07 10:25:00

SQL Server索

2010-06-13 14:31:18

MySQL 5.0

2010-05-18 15:58:39

MySQL觸發(fā)器

2010-02-03 15:40:37

Python函數(shù)

2010-10-13 15:59:21

MySQL索引

2010-07-19 14:48:27

SQL Server索

2020-12-29 05:34:48

Scrapy網(wǎng)頁源代碼

2015-03-31 14:15:12

JavaJava事件通知

2018-12-05 09:00:00

RedisRedis Strea數(shù)據(jù)庫

2022-09-07 08:58:58

Node.js框架

2021-03-15 12:23:24

Pythonyield代碼

2010-01-18 17:23:55

函數(shù)

2010-01-18 17:23:55

函數(shù)

2023-12-26 11:56:14

Go通道編程

2022-11-23 08:00:00

開發(fā)Regulator調(diào)試

2011-08-08 15:43:01

MySQL索引

2011-04-27 16:38:31

投影機

2010-06-13 15:00:23

MySQL統(tǒng)計函數(shù)

2010-10-12 14:16:56

MySQL索引
點贊
收藏

51CTO技術(shù)棧公眾號

亚洲一区二区欧美激情| 日本少妇一区二区| 精品福利一区二区三区| 欧美午夜小视频| 亚州男人的天堂| 日韩中文字幕区一区有砖一区 | 国产suv精品一区二区| 亚洲一区二区三区蜜桃| 成人国产一区二区三区精品麻豆| 国产精品国产三级国产有无不卡| 国产精品爽爽爽| 久久久久无码国产精品不卡| 天堂成人娱乐在线视频免费播放网站| 一本到三区不卡视频| 天堂资源在线亚洲资源| xxxx18国产| 久久亚洲一区| 久久久国产一区二区| 男人网站在线观看| 国产毛片精品久久| 亚洲aⅴ怡春院| 天堂精品视频| 天堂网av2014| 久久99精品国产麻豆婷婷| 久久成年人视频| 麻豆精品免费视频| 国产91亚洲精品久久久| 亚洲午夜羞羞片| 久久伊人资源站| 国产区精品在线| 99成人免费视频| 久久精品成人欧美大片古装| 国产伦精品一区二区三区妓女 | 日韩激情一区| 日韩av在线免费看| 人妻巨大乳一二三区| 偷拍精品精品一区二区三区| 中文字幕一区二区三| 久久99九九| 中文字幕 日韩有码| 亚洲精品综合| 久久99国产精品久久久久久久久| 舐め犯し波多野结衣在线观看| 亚洲日本va中文字幕| 欧美日韩色一区| 久久人妻精品白浆国产| 国产丝袜精品丝袜| 亚洲人妖av一区二区| 欧美激情论坛| 日韩一区免费视频| 国产成人综合亚洲91猫咪| 国产免费一区视频观看免费| 毛片在线免费视频| 亚洲精品精选| 欧美激情二区三区| 午夜精品一区二区三区视频| 欧美色就是色| 日韩精品极品在线观看播放免费视频| 4438x全国最大成人| 国产精久久久| 91精品欧美一区二区三区综合在| 天天色综合天天色| 国产成人免费| 欧美性受极品xxxx喷水| 嫩草av久久伊人妇女超级a| 性欧美18~19sex高清播放| 亚洲无线码一区二区三区| 久久香蕉视频网站| 免费不卡av| 亚洲国产日韩在线一区模特| 欧美视频在线第一页| 日本天码aⅴ片在线电影网站| 综合激情成人伊人| 可以免费看的黄色网址| 黄色网址免费在线观看| 亚洲视频一二三| 浴室偷拍美女洗澡456在线| 黄av在线播放| 一区二区高清在线| 成人黄色大片网站| 天堂中文最新版在线中文| 欧美性猛交xxxx免费看漫画| 97超碰青青草| 亚洲欧洲日本韩国| 在线中文字幕一区二区| www.日本一区| 国产精品视频一区二区三区综合| 日韩一区二区视频在线观看| 精品人妻在线视频| 亚洲春色h网| 最近2019中文免费高清视频观看www99 | 成人综合国产精品| 波多野结衣大片| 久久电影网站中文字幕| 亚洲综合社区网| 国产综合在线播放| 精品一区二区在线视频| 91在线看网站| 青青草免费在线| 番号在线播放| 99久久精品国产麻豆演员表| 欧美日韩精品一区| 亚洲综合图区| 欧美性videosxxxxx| 久久久久久久久久久久国产精品| 九九综合九九| 欧美高清视频在线播放| 无码人妻丰满熟妇区bbbbxxxx| 国精产品一区一区三区mba视频| 精品国产一二| 超碰人人在线| 在线免费av一区| xfplay5566色资源网站| 久久在线视频| 国产97在线|日韩| 免费观看国产视频| 亚洲欧洲一区二区在线播放| 漂亮人妻被中出中文字幕| 91精品国产乱码久久久竹菊| 综合网中文字幕| 国产99久久久| a美女胸又www黄视频久久| 91制片厂免费观看| 日韩成人免费av| 亚洲深夜福利在线| 免费观看一区二区三区毛片| 国产精品99久久久久久宅男| 亚洲午夜精品一区二区| 三级成人黄色影院| 日韩精品在线视频| 日本中文字幕网| 国产成人综合在线播放| 亚洲精品偷拍视频| 91精品国产自产观看在线| 亚洲新声在线观看| 亚洲欧美偷拍一区| 久久婷婷国产综合精品青草 | av片在线看| 色综合久久中文综合久久牛| 风间由美一二三区av片| 伊人成人在线视频| 国产aⅴ精品一区二区三区黄| 国产一二区在线| 777奇米四色成人影色区| 少妇高潮在线观看| 久久er精品视频| 亚洲制服欧美久久| 欧美黄色网络| 日韩在线高清视频| 国产乱淫av片免费| 亚洲色图一区二区| 波多野结衣在线免费观看| 亚洲澳门在线| dy888夜精品国产专区| 久久不射影院| 日韩精品在线观看一区二区| 亚洲 欧美 成人| 国产欧美精品区一区二区三区| 成人一区二区三| 日韩欧美不卡| 91色中文字幕| ririsao久久精品一区| 亚洲二区在线播放视频| 天天爽夜夜爽夜夜爽精品| 久久综合色婷婷| 另类小说第一页| 我不卡手机影院| 国产精品jizz视频| jk漫画禁漫成人入口| 曰本色欧美视频在线| 136福利视频导航| 一区二区三区资源| 波多野结衣 在线| 久久成人麻豆午夜电影| 国产 国语对白 露脸| 欧美黑白配在线| 国产精品久久久久久五月尺| 黄色网页网址在线免费| 精品sm捆绑视频| 久久久久精彩视频| 亚洲黄色录像片| 在哪里可以看毛片| 国产一区二区伦理片| 免费国产a级片| 日韩欧美1区| 精品国产_亚洲人成在线| 久草在线资源福利站| 中文字幕一精品亚洲无线一区| 99久久精品日本一区二区免费| 午夜国产不卡在线观看视频| 国产无遮挡在线观看| 成人丝袜视频网| 欧美婷婷精品激情| 亚洲国产导航| 永久久久久久| 亚洲+小说+欧美+激情+另类 | 久久婷婷国产精品| 亚洲精品小说| 日本高清一区| 成人中文字幕视频| 成人av番号网| 偷拍精品精品一区二区三区| 欧美激情videoshd| 毛片在线看网站| 亚洲色图综合久久| 色欲av永久无码精品无码蜜桃 | 亚洲精品国偷拍自产在线观看蜜桃| 精品福利在线观看| 成年人一级黄色片| 国产农村妇女毛片精品久久麻豆| 国产麻豆剧传媒精品国产| 奇米精品一区二区三区在线观看 | 女囚岛在线观看| 国产亚洲精品日韩| 天天综合网天天综合| 91精品国产色综合久久| 亚洲欧美综合自拍| 午夜精品一区在线观看| 希岛爱理中文字幕| 国产精品每日更新在线播放网址| 荫蒂被男人添免费视频| 国产一区美女在线| 亚洲国产成人va在线观看麻豆| 国产精品久久久久9999高清| 日本大片免费看| 亚洲老妇激情| 在线视频不卡一区二区三区| 久久成人高清| 久久影视中文粉嫩av| 国产极品模特精品一二| av一本久道久久波多野结衣| 精品99re| 91九色蝌蚪国产| 欧美日韩破处视频| 国产免费一区视频观看免费| 日韩制服诱惑| 国产精品 欧美在线| 这里有精品可以观看| 97精品久久久| 久草在线中文最新视频| 91精品国产777在线观看| 国内在线免费视频| 欧美激情欧美狂野欧美精品| 91高清在线观看视频| 另类少妇人与禽zozz0性伦| 黄色片网站在线| 久久久精品一区二区| 成人高清免费在线| 精品中文字幕在线2019| 欧美寡妇性猛交xxx免费| 高清在线视频日韩欧美| 99re6在线精品视频免费播放| 久久久亚洲影院| aaa在线播放视频| 51视频国产精品一区二区| aa视频在线观看| 欧美性受xxx| 日韩电影免费观看高清完整版| 欧美做受高潮电影o| 欧美成人免费电影| 国产精品普通话| 二区三区精品| 国产伦精品一区二区三区| 日韩丝袜视频| 亚洲v国产v在线观看| 97色伦图片97综合影院| 肉大捧一出免费观看网站在线播放 | 日本在线不卡一区二区| 国产69精品久久777的优势| www.四虎精品| 久久久一区二区三区| 国产午夜精品久久久久久久久| 亚洲国产精华液网站w| 国产精品精品软件男同| 亚洲成人激情自拍| 无码人妻精品一区二区50| 欧美日韩国产另类一区| 国产成人精品无码高潮| 亚洲精品av在线| 最近高清中文在线字幕在线观看| 欧美成在线观看| 亚洲精品mv| 91最新在线免费观看| 另类在线视频| 伊甸园精品99久久久久久| 好吊一区二区三区| 天天影视综合色| 国产999精品久久久久久| 色一情一交一乱一区二区三区| 自拍av一区二区三区| 日韩在线观看第一页| 欧美高清视频不卡网| 香港三日本三级少妇66| 精品国产一区二区在线| 国产白浆在线免费观看| 国产欧美精品久久久| 全球av集中精品导航福利| 亚洲欧洲国产日韩精品| 在线成人亚洲| 国产精品嫩草影院8vv8| 99精品国产91久久久久久| av黄色免费在线观看| 午夜精品久久久久久久久久 | 日韩精品一区二区三区四区视频| 精品美女视频在线观看免费软件 | 久操视频免费在线观看| 日本福利一区二区| 日韩永久免费视频| 欧美日本高清视频| 久久精品嫩草影院| 久久久久久久久四区三区| 午夜片欧美伦| 国产精品视频黄色| 91网上在线视频| 九九九免费视频| 欧美日韩国产高清一区二区| 色视频在线看| 久久久久久久久久久网站| 涩涩涩久久久成人精品| 欧美日韩在线精品一区二区三区| 欧美精品麻豆| 免费高清视频在线观看| 中文字幕在线观看一区二区| 亚洲午夜无码久久久久| 亚洲男人av电影| 高清在线视频不卡| 国产精品推荐精品| 国产一区二区三区四区老人| 国产精品v日韩精品v在线观看| 久久综合九色欧美综合狠狠 | 一本久道久久综合狠狠爱| 成年人性生活视频| 亚洲人一二三区| 国产乱码精品一区二区| 在线亚洲男人天堂| 成人国产网站| 夜夜爽99久久国产综合精品女不卡| 久久久久久色| 短视频在线观看| 日韩欧美精品免费在线| 青青草在线免费观看| 欧美在线视频免费观看| 秋霞影视一区二区三区| 99精品在线免费视频| 91色视频在线| 日本免费在线观看视频| 国产亚洲欧美aaaa| 色猫猫成人app| 一区在线电影| 国产精品一区二区x88av| 久久久久97国产| 精品电影一区二区三区 | 午夜精品久久| 欧美日韩一区二区三区四区五区六区| 伊人一区二区三区| 日本xxxxwww| 日韩免费在线视频| 成人毛片免费看| 黄色a级三级三级三级| 亚洲资源在线观看| 色视频在线看| 91精品久久久久久久久久久久久久| 香蕉综合视频| 69xxx免费视频| 欧美性猛交xxxx黑人| 欧美三级黄网| 国产欧美在线一区二区| 噜噜噜躁狠狠躁狠狠精品视频| 男人的天堂官网 | 亚洲精品mv在线观看| 亚洲一区二三区| 美女做暖暖视频免费在线观看全部网址91 | 欧美性色黄大片手机版| 秋霞午夜在线观看| 国产日韩欧美亚洲一区| 亚洲尤物在线| 91动漫免费网站| 精品国产91九色蝌蚪| 电影在线观看一区二区| 今天免费高清在线观看国语| www.亚洲精品| 一级片视频网站| 午夜精品久久久久久久久久久久 | 亚洲资源在线| 可以在线看的av网站| 欧美国产日韩一二三区| 蜜桃av中文字幕| 国产精品老女人精品视频| 欧美激情自拍| 亚洲最大成人综合网| 日韩欧美黄色影院| 日韩另类视频| 成人性生活视频免费看| 国产精品欧美一级免费| 日本激情一区二区三区| 国产欧美va欧美va香蕉在| 国产精品普通话对白| 999精品视频在线观看播放| 日韩黄色高清视频| 精品一区二区三区亚洲|