索引原理

索引

索引在MySQL中也叫作「鍵」,是存儲引擎用於快速找到記錄的一種數據結構。 若索引太多,應用程序的性能可能會受到影響。而索引太少,對查詢性能又會產生影響,要找到一個平衡點,這對應用程序的性能相當重要。python

1、索引的原理

  • 索引的目的在於提升查詢效率,與咱們查閱圖書所用的目錄是一個道理:先定位到章,而後定位到該章下的一個小節,而後找到頁數。
  • 經過不斷地縮小想要獲取數據的範圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件。

2、索引的數據結構

每次查找數據時把磁盤IO次數控制在一個很小的數量級,最好是常數數量級。B+樹應運而生(B+樹是經過二叉查找樹,再由平衡二叉樹,B樹演化而來)。mysql

一、B+樹性質

  • 索引字段要儘可能的小:若是數據項佔的空間越小,數據項的數量越多,樹的高度越低。好比int佔4字節,要比bigint8字節少一半。這也是爲何b+樹要求把真實的數據放到葉子節點而不是內層節點,一旦放到內層節點,磁盤塊的數據項會大幅度降低,致使樹增高。當數據項等於1時將會退化成線性表。
  • 索引的最左匹配特性:當b+樹的數據項是複合的數據結構,好比(name,age,sex)的時候,b+數是按照從左到右的順序來創建搜索樹的,好比當(張三,20,F)這樣的數據來檢索的時候,b+樹會優先比較name來肯定下一步的所搜方向,若是name相同再依次比較age和sex,最後獲得檢索的數據;但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道下一步該查哪一個節點,由於創建搜索樹的時候name就是第一個比較因子,必需要先根據name來搜索才能知道下一步去哪裏查詢。

3、 MySQL索引管理

一、功能

  • 索引的功能就是加速查找

二、MySQL經常使用的索引

  • 惟一索引:
    1. 加速查找
  • 普通索引INDEX:
    1. 主鍵索引PRIMARY KEY:加速查找+約束(不爲空、不能重複)
    2. 惟一索引UNIQUE:加速查找+約束(不能重複)
  • 聯合索引:
    1. PRIMARY KEY(id,name):聯合主鍵索引
    2. UNIQUE(id,name):聯合惟一索引
    3. INDEX(id,name):聯合普通索引
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
舉個例子來講,好比你在爲某商場作一個會員卡的系統。
 
這個系統有一個會員表
有下列字段:
會員編號 INT
會員姓名 VARCHAR (10)
會員身份證號碼 VARCHAR (18)
會員電話 VARCHAR (10)
會員住址 VARCHAR (50)
會員備註信息 TEXT
 
那麼這個 會員編號,做爲主鍵,使用 PRIMARY
會員姓名 若是要建索引的話,那麼就是普通的 INDEX
會員身份證號碼 若是要建索引的話,那麼能夠選擇 UNIQUE (惟一的,不容許重複)
 
#除此以外還有全文索引,即FULLTEXT
會員備註信息 , 若是須要建索引的話,能夠選擇全文搜索。
用於搜索很長一篇文章的時候,效果最好。
用在比較短的文本,若是就一兩行字的,普通的 INDEX 也能夠。
但其實對於全文搜索,咱們並不會使用MySQL自帶的該索引,而是會選擇第三方軟件如Sphinx,專門來作全文搜索。
 
#其餘的如空間索引SPATIAL,瞭解便可,幾乎不用

三、索引的兩大類型hash與btree

?
1
2
3
4
5
6
7
8
9
10
#咱們能夠在建立上述索引的時候,爲其指定索引類型,分兩類
hash類型的索引:查詢單條快,範圍查詢慢
btree類型的索引:b+樹,層數越多,數據量指數級增加(咱們就用它,由於innodb默認支持它)
 
#不一樣的存儲引擎支持的索引類型也不同
InnoDB 支持事務,支持行級別鎖定,支持 B-tree、 Full -text 等索引,不支持 Hash 索引;
MyISAM 不支持事務,支持表級別鎖定,支持 B-tree、 Full -text 等索引,不支持 Hash 索引;
Memory 不支持事務,支持表級別鎖定,支持 B-tree、Hash 等索引,不支持 Full -text 索引;
NDB 支持事務,支持行級別鎖定,支持 Hash 索引,不支持 B-tree、 Full -text 等索引;
Archive 不支持事務,支持表級別鎖定,不支持 B-tree、Hash、 Full -text 等索引;

四、建立/刪除索引的語法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 方法一:建立表時
  CREATE
TABLE
表名(
     字段名 1
數據類型[完整性約束條件…],
字段名 2
數據類型[完整性約束條件…],
[UNIQUE | FULLTEXT | SPATIAL]
INDEX | KEY
[索引名](字段名[(長度)][ASC | DESC])
);
 
 
# 方法二:CREATE在已存在的表上建立索引
CREATE[UNIQUE | FULLTEXT | SPATIAL]
INDEX
索引名
ON
表名(字段名[(長度)][ASC | DESC]);
 
# 方法三:ALTER TABLE在已存在的表上建立索引
ALTER
TABLE
表名
ADD[UNIQUE | FULLTEXT | SPATIAL]
INDEX
索引名(字段名[(長度)][ASC | DESC]);
 
# 刪除索引:DROP INDEX 索引名 ON 表名字;
 
 
 
#方式一
create table t1(
     id int ,
     name char,
     age int ,
     sex enum( 'male' , 'female' ),
     unique key uni_id( id ),
     index ix_name(name) #index沒有key
);
 
 
#方式二
create index ix_age on t1(age);
 
#方式三
alter table t1 add index ix_sex(sex);
 
#查看
mysql> show create table t1;
| t1    | CREATE TABLE `t1` (
   ` id ` int ( 11 ) DEFAULT NULL,
   `name` char( 1 ) DEFAULT NULL,
   `age` int ( 11 ) DEFAULT NULL,
   `sex` enum( 'male' , 'female' ) DEFAULT NULL,
   UNIQUE KEY `uni_id` (` id `),
   KEY `ix_name` (`name`),
   KEY `ix_age` (`age`),
   KEY `ix_sex` (`sex`)
) ENGINE = InnoDB DEFAULT CHARSET = latin1

五、總結

  • 一、必定是爲搜索條件的字段建立索引,好比select * from s1 where id = 333;就須要爲id加上索引
  • 二、在表中已經有大量數據的狀況下,建索引會很慢,且佔用硬盤空間,建完後查詢速度加快
  • 三、須要注意的是:innodb表的索引會存放於s1.ibd文件中,而myisam表的索引則會有單獨的索引文件table1.MYI

MySAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在innodb中,表數據文件自己就是按照B+Tree(BTree即Balance True)組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,所以innodb表數據文件自己就是主索引。 由於inndob的數據文件要按照主鍵彙集,因此innodb要求表必需要有主鍵(Myisam能夠沒有),若是沒有顯式定義,則mysql系統會自動選擇一個能夠惟一標識數據記錄的列做爲主鍵,若是不存在這種列,則mysql會自動爲innodb表生成一個隱含字段做爲主鍵,這字段的長度爲6個字節,類型爲長整型.sql

4、正確使用索引

並非說咱們建立了索引就必定會加快查詢速度,若想利用索引達到預想的提升查詢速度的效果,咱們在添加索引時,必須注意如下問題數據結構

一、索引未命中

  • 1)、範圍問題,或者說條件不明確,條件中出現這些符號或關鍵字:>、>=、<、<=、!= 、between...and...、like、
  • 2)、儘可能選擇區分度高的列做爲索引,區分度的公式是count(distinct col)/count(*),表示字段不重複的比例,比例越大咱們掃描的記錄數越少,惟一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0,那可能有人會問,這個比例有什麼經驗值嗎?使用場景不一樣,這個值也很難肯定,通常須要join的字段咱們都要求是0.1以上,即平均1條掃描10條記錄
  • 3)、=和in能夠亂序,好比a = 1 and b = 2 and c = 3 創建(a,b,c)索引能夠任意順序,mysql的查詢優化器會幫你優化成索引能夠識別的形式
  • 4)、索引列不能參與計算,保持列「乾淨」。好比爲id加了索引,可是查的時候where id*3+10 ,索引字段參與了計算,沒法拿到一個明確的值去索引樹中查找,每次都得臨時計算一下。
  • 5)、and/or 在左邊條件成立可是索引字段的區分度低的狀況下(name與gender均屬於這種狀況),會依次往右找到一個區分度高的索引字段,加速查詢
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#一、 and or 的邏輯
     條件1 and 條件2:全部條件都成立纔算成立,但凡要有一個條件不成立則最終結果不成立
     條件1 or 條件2:只要有一個條件成立則最終結果就成立
 
#二、 and 的工做原理
     條件:
         a = 10 and b = 'xxx' and c > 3 and d =4
     索引:
         製做聯合索引(d,a,b,c)
     工做原理:
         對於連續多個 and :mysql會按照聯合索引,從左到右的順序找一個區分度高的索引字段(這樣即可以快速鎖定很小的範圍),加速查詢,即按照d—>a->b->c的順序
 
#三、 or 的工做原理
     條件:
         a = 10 or b = 'xxx' or c > 3 or d =4
     索引:
         製做聯合索引(d,a,b,c)
         
     工做原理:
         對於連續多個 or :mysql會按照條件的順序,從左到右依次判斷,即a->b->c->d
  • 6)、最左前綴匹配原則(詳見第八小節),很是重要的原則,對於組合索引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的順序能夠任意調整。
  • 7)、其餘狀況
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- 使用函數
     select * from tb1 where reverse(email) = 'egon' ;
             
- 類型不一致
     若是列是字符串類型,傳入條件是必須用引號引發來,否則...
     select * from tb1 where email = 999;
     
#排序條件爲索引,則 select 字段必須也是索引字段,不然沒法命中
- order by
     select name from s1 order by email desc ;
     當根據索引排序時候, select 查詢的字段若是不是索引,則速度仍然很慢
     select email from s1 order by email desc ;
     特別的:若是對主鍵排序,則仍是速度很快:
         select * from tb1 order by nid desc ;
  
- 組合索引最左前綴
     若是組合索引爲:( name ,email)
     name and email       -- 命中索引
     name                 -- 命中索引
     email                -- 未命中索引
 
 
- count (1)或 count (列)代替 count (*)在mysql中沒有差異了
 
- create index xxxx  on tb(title(19)) #text類型,必須制定長度

二、其餘注意事項

  • - 避免使用select *
  • - count(1)或count(列) 代替 count(*)
  • - 建立表時儘可能時 char 代替 varchar
  • - 表的字段順序固定長度的字段優先
  • - 組合索引代替多個單列索引(常用多個條件查詢時)
  • - 儘可能使用短索引
  • - 使用鏈接(JOIN)來代替子查詢(Sub-Queries)
  • - 連表時注意條件類型需一致
  • - 索引散列值(重複少)不適合建索引,例:性別不適合

5、聯合索引

聯合索引時指對錶上的多個列合起來作一個索引。函數

  • 建立方法:key idx_a_b(a,b)
  • 聯合索引的好處是在第一個鍵相同的狀況下,已經對第二個鍵進行了排序處理
?
1
2
3
4
5
6
7
8
9
#對於聯合索引(a,b),下述語句能夠直接使用該索引,無需二次排序
select ... from table where a = xxx order by b;
 
#而後對於聯合索引(a,b,c)來首,下列語句一樣能夠直接經過索引獲得結果
select ... from table where a = xxx order by b;
select ... from table where a = xxx and b = xxx order by c;
 
#可是對於聯合索引(a,b,c),下列語句不能經過索引直接獲得結果,還須要本身執行一次filesort操做,由於索引(a,c)並未排序
select ... from table where a = xxx order by c;

6、慢查詢優化的基本步驟

  • - 0.先運行看看是否真的很慢,注意設置SQL_NO_CACHE
  • - 1.where條件單表查,鎖定最小返回記錄表。這句話的意思是把查詢語句的where都應用到表中返回的記錄數最小的表開始查起,單表每一個字段分別查詢,看哪一個字段的區分度最高
  • - 2.explain查看執行計劃,是否與1預期一致(從鎖定記錄較少的表開始查詢)
  • - 3.order by limit 形式的sql語句讓排序的表優先查
  • - 4.瞭解業務方使用場景
  • - 5.加索引時參照建索引的幾大原則
  • - 6.觀察結果,不符合預期繼續從0分析
相關文章
相關標籤/搜索