進擊のpython
數據庫——索引管理
這是數據庫的最后一節,也是數據庫最難的一節
但是!也是進階最重要的一節!!
一般的應用系統,讀寫比例在10:1左右,而且插入操作和一般的更新操作很少出現性能問題
在生產環境中,我們遇到最多的,也是最容易出問題的,還是一些復雜的查詢操作
因此對查詢語句的優化顯然是重中之重。說起加速查詢,就不得不提到索引了
什么是索引
索引在MySQL中也叫做“鍵”,是存儲引擎用於快速找到記錄的一種數據結構
索引對於良好的性能非常關鍵,尤其是當表中的數據量越來越大時,
索引對於性能的影響愈發重要
索引優化應該是對查詢性能優化最有效的手段了
索引能夠輕易將查詢性能提高好幾個數量級
索引相當於字典的音序表,如果要查某個字,如果不使用音序表,則需要從幾百頁中逐頁去查
索引是應用程序設計和開發的一個重要方面!!!!!
若索引太多,應用程序的性能可能會受到影響
而索引太少,對查詢性能又會產生影響,要找到一個平衡點,這對應用程序的性能至關重要
索引原理
索引的目的在於提高查詢效率,與我們查閱圖書所用的目錄是一個道理:
先定位到章,然后定位到該章下的一個小節,然后找到頁數
相似的例子還有:查字典,查火車車次,飛機航班等
本質都是:通過不斷地縮小想要獲取數據的范圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是說,有了這種索引機制,我們可以總是用同一種查找方式來鎖定數據
數據庫的查詢機制也是這樣的,但是,要比這個復雜得多!
每次訪問數據的時候都會產生一次I/O阻塞,而這個阻塞,越少!速度越快!
索引的數據結構
基於前面的我們可以總結一下,我們需要一個數據結構:
每次查找數據時把磁盤IO次數控制在一個很小的數量級,最好是常數數量級
那么我們就想到如果一個高度可控的多路搜索樹是否能滿足需求呢?
就這樣,b+樹應運而生(B+樹是通過二叉查找樹,再由平衡二叉樹,B樹演化而來)
看不懂很正常,你看的懂,我跟你說什么啊~
淺藍色的塊我們稱之為一個磁盤塊
可以看到每個磁盤塊包含幾個數據項(深藍色所示)和指針(黃色所示)
如磁盤塊1包含數據項17和35,包含指針P1、P2、P3
P1表示小於17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大於35的磁盤塊
真實的數據存在於葉子節點即3、5、9、10、13、15、28、29、36、60、75、79、90、99
非葉子節點不存儲真實的數據,只存儲指引搜索方向的數據項,如17並不真實存在於數據表中
當我們想查29的時候,首先進行一次I/O阻塞,打開磁盤塊1
利用二分法知道29是在17和35之間的,所以走P2指針
將磁盤塊3加載進內存,此時發生第二次I/O阻塞,通過二分法確定指針為P2
將磁盤塊8加載進內存,此事發生第三次I/O阻塞,查找到了29
也就是說,只需要三次I/O就可以將數據查找到,如果不用b+樹需要七次I/O
提升的效率可不是一點點!!樹的高度決定I/O次數!
問題就變成了,如何創建才能夠使樹更低呢???
1.索引字段要盡量的小:假設當前數據表的數據為N,每個磁盤塊的數據項的數量是m,則有h=㏒(m+1)N
當數據量N一定的情況下,m越大,h越小;而m = 磁盤塊的大小 / 數據項的大小
磁盤塊的大小也就是一個數據頁的大小,是固定的,如果數據項占的空間越小,數據項的數量越多,樹的高度越低
這就是為什么每個數據項,即索引字段要盡量的小,比如int占4字節,要比bigint8字節少一半
這也是為什么b+樹要求把真實的數據放到葉子節點而不是內層節點
一旦放到內層節點,磁盤塊的數據項會大幅度下降,導致樹增高
當數據項等於1時將會退化成線性表
2.索引的最左匹配特性:當b+樹的數據項是復合的數據結構,比如(name,age,sex)的時候
b+數是按照從左到右的順序來建立搜索樹的,比如當(張三,20,F)這樣的數據來檢索的時候
b+樹會優先比較name來確定下一步的所搜方向,如果name相同再依次比較age和sex最后得到檢索的數據;
但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道下一步該查哪個節點
因為建立搜索樹的時候name就是第一個比較因子,必須要先根據name來搜索才能知道下一步去哪里查詢
比如當(張三,F)這樣的數據來檢索時,b+樹可以用name來指定搜索方向,但下一個字段age的缺失
所以只能把名字等於張三的數據都找到,然后再匹配性別是F的數據了, 這個是非常重要的性質
但是,索引不是越多越好!
拿寫書舉例子,你寫完書了,目錄也寫完了,然后有一天你想改變書的內容
是不是你改一次,就要重新寫一次目錄?
那么數據庫每次的修改,都會使得索引重新設置,就大大的降低效率,查詢變快,修改變慢
所以,不要盲目的加索引!
索引分類
前面提過,通過innodb引擎創建表的時候必須有一個主鍵,而且通常是id字段是主鍵,為什么呢?
因為innodb是索引組織數據,就是這個表剛建好,索引就建好了,那按照什么建索引呢?估計你也猜到了,主鍵!
也就是為什么要設置主鍵!前面講過,沒有主鍵就會設置一個隱藏的主鍵!而隱藏的主鍵,用不到啊!
聚集索引
聚集索引的好處之一:它對主鍵的排序查找和范圍查找速度非常快,葉子節點的數據就是用戶所要查詢的數據,如用戶需要查找一張表,查詢最后的10位用戶信息,由於B+樹索引是雙向鏈表,所以用戶可以快速找到最后一個數據頁,並取出10條記錄
聚集索引的好處之二:范圍查詢(range query),即如果要查找主鍵某一范圍內的數據,通過葉子節點的上層中間節點就可以得到頁的范圍,之后直接讀取數據頁即可
輔助索引
表中除了聚集索引外其他索引都是輔助索引(Secondary Index,也稱為非聚集索引)
與聚集索引的區別是:輔助索引的葉子節點不包含行記錄的全部數據。
葉子節點除了包含鍵值以外,每個葉子節點中的索引行中還包含一個書簽(bookmark)
該書簽用來告訴InnoDB存儲引擎去哪里可以找到與索引相對應的行數據
輔助索引的存在並不影響數據在聚集索引中的組織,因此每張表上可以有多個輔助索引,但只能有一個聚集索引
當通過輔助索引來尋找數據時,InnoDB存儲引擎會遍歷輔助索引並通過葉子級別的指針獲得只想主鍵索引的主鍵
然后再通過主鍵索引來找到一個完整的行記錄
但是有索引的前提是你用where來進行篩選,直接用*查詢全表,有沒有索引就無所謂了
而且再一個,如果是范圍查詢,那索引的作用其實也是微乎其微的
索引測試
create table s1(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);
delimiter //
create procedure auto_insert1()
BEGIN
declare i int default 1;
while(i<3000000)do
insert into s1 values(i,'egon','male',concat('egon',i,'@oldboy'));
set i=i+1;
end while;
END//
delimiter ;
call auto_insert1();
創建完之后,先進行正常的查詢:
mysql> select * from s1 where id=33333333;
Empty set (2.33 sec)
那我們為了優化效率,我們需要給id加索引!怎么加呢?
mysql> create index a on s1(id);
Query OK, 0 rows affected <5.30 sec>
很明顯,創建索引確實是很慢的
在索引建立完畢后,以該字段為查詢條件時,查詢速度提升明顯
mysql> select * from s1 where id = 33333333;
Empty set (0.00 sec)
但是你去看你的文件,你會發現他變得非常大了