如何構建高BLEVEL的INDEX?
[i=s] 本帖最後由 wei-xh 於 2012-5-9 13:00 編輯
思考一個問題:有一個查詢select * from table where id=:1 and name=:2;
單從這一個SQL來看,該如何建立索引。ID區分度足夠好,NAME區分度稍微差點。
是ID,NAME好呢?還是NAME,ID好?為什麼?
實驗一把,看看。
Table created.
test@CRMD>insert into test select rownum,lpad('test',4000) from dba_source;
103602 rows created.
test@CRMD>commit;
Commit complete.上面建立了一張擁有103602條記錄的表。表的第一個欄位唯一,第二個欄位全部都是一樣的、4K個位元組。
Index created.
test@CRMD>create index t_2 on test(name,id);
Index created.建立了兩個索引。兩個索引的建立方式,唯一的區別就是鍵值的順序做了調換。
檢視統計資訊。
Please enter Name of Table Owner: test
Please enter Table Name : test
Index Leaf Distinct Number AV Av Cluster Date
Name BLV Blks Keys of Rows LEA Data Factor MM-DD-YYYY
------------------------------ --- ------------ -------------- -------------- ------- ------- ------------ ----------
T_1 2 103,602 103,602 103,602 1 1 103,602 05-08-2012
T_2 11 103,602 103,602 103,602 1 1 103,602 05-08-2012
Index Column Col Column
Name Name Pos Details
------------------------------ ------------------------------ ---- ------------------------
T_1 ID 1 NUMBER(22)
NAME 2 VARCHAR2(4000)
T_2 NAME 1 VARCHAR2(4000)
ID 2 NUMBER(22)發現兩個索引的統計資訊除了BLEVEL(上面的BLV)外,其他的資訊完全一樣。而且BLEVEL出現了“天壤之別”。
T_1的BLV為2。
T_2的BLV為12。
記得我剛開始做DBA的時候,老師告訴我一條準則,就是兩個謂詞如果都做等值查詢,那麼區分度好的要放在前面,也就是distinct值大的放前面。當時我並不知道為什麼。
後面看LEWIS的COST成本演算法。
按照走索引的COST的演算法:
COST=索引成本+回表成本
索引成本=BLEVEL+BLOCKS*選擇率
回表成本=聚簇因子*表的有效選擇率
經過排除發現,貌似老師告訴我的準則是錯誤的,因為不管你建立的索引的鍵值順序是怎麼樣的,索引成本的選擇率和回表成本的表有效選擇率都是維持不變的。索引葉子的BLOCKS是不變的。聚簇因子雖然有可能不一樣,但是它完全依賴與索引相對與表的有序程度,與鍵值的區分度好壞完全沒關係。
那麼就差BLEVEL了。經過實驗發現BLEVEL確實是不一樣的。
為什麼呢?
test@CRMD>select object_name,object_id from dba_objects where object_name in ('T_1','T_2');
OBJECT_NAME OBJECT_ID
-------------------- ----------
T_1 10834
T_2 10835
test@CRMD>alter session set events 'immediate trace name treedump level 10834';
Session altered.
test@CRMD>alter session set events 'immediate trace name treedump level 10835';
Session altered.上面的步驟可以把索引的內部結果DUMP出來。方便我們找到分支節點的物理位置。我們對葉子節點的物理結構不關心,因為兩種情況下建立的索引,葉子節點數目是一樣的,儲存的內容也是一樣的,只是鍵值順序不一樣。但是之所以第二種方式建立的索引,BLEVEL高,就是因為分支節點的數目大大增多的緣故,因此我們透過比對分支節點儲存的內容,就能找到原因了。
檢視DUMP檔案:
branch: 0x1019b03 16882435 (-1: nrow: 740, level: 1)-------------------------第一個分支節點的地址
leaf: 0x101980d 16881677 (-1: nrow: 1 rrow: 1)
leaf: 0x101980e 16881678 (0: nrow: 1 rrow: 1)上面的DUMP內容是第一個索引的,我只擷取了一小部分。第二個索引的DUMP內容我就不貼出來了。根據DUMP檔案,我們DUMP出具體的分支塊來看看裡面具體的儲存。
第一種方式建立的索引的分支塊DUMP.
col 0; len 3; (3): c2 08 2b
col 1; TERM
row#1[8038] dba: 16882437=0x1019b05
col 0; len 3; (3): c2 08 2c
col 1; TERM
row#2[8029] dba: 16882438=0x1019b06
col 0; len 3; (3): c2 08 2d
col 1; TERM可以看到ORACLE只記錄了第一列的資料在分支塊裡,第二列的鍵值根本沒記錄!!!!
第二種方式建立的索引分支塊的DUMP.
col 0; len 4000; (4000):
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
省略非常長的內容
。
。
。
col 1; len 2; (2): c1 03
col 2; TERM第二種方式建立的索引兩個列的內容都做了記錄。
到這裡基本就清楚了,ORACLE的分支塊裡,只記錄必需的索引鍵值內容,也就是具有足夠的區分度可以導航找到葉子塊就可以了。
對於我們這個例子,對於第一種建立索引的方式,由於第一列的值已經是唯一的了,具有足夠的區分度了,第二列就可以不在索引分支塊裡出現了。
而對於第二種建立索引的方式,由於第一列的值沒有區分度,只有把第二列也加進去才具有區分度,因此就導致分支塊裡要把兩個列的鍵值都加進去。
而我們的例子又比較的極端,第二列非常的大,有4K個位元組,就導致分支塊的數目非常的多,最終導致索引的BLEVEL非常的大,在我們的例子裡
在一個只有103602行記錄表的索引裡,索引的層數就達到了12層。
看來當年老師告訴我的準則是有效的:當兩個謂詞只做等值查詢,把區分度好的作為前導列建立索引比較好。但是可能這個準則並不是多麼的重要,因為畢竟我們這裡的例子太特殊了,第二個欄位非常的長,而且不具有任何區分度,可能實際我們的環境中,效果差異並不那麼大。
還有這個準則不是肯定有效的,因為聚簇因子的關係,如果欄位A的順序跟表的儲存順序極其一致,而欄位B的極其不一致,但是欄位A的區別度糟糕,欄位B的區別度非常好。這個時候,到底是A,B好還是B,A好,要經過測試。
如果你充分理解了上面所說的內容,可以看下,如果我對錶的資料做了如下的處理,那麼兩種方式建立的索引,還會有區別嗎?
Table truncated.
test@CRMD>insert /*+ append */into test select rownum,rownum||lpad('test',3993) from dba_source;
103614 rows created.
test@CRMD>commit;
Commit complete.
test@CRMD>create index t_1 on test(id,name);
test@CRMD>create index t_2 on test(name,id);第二個欄位,還是4K個位元組,但是它的前導欄位的區分度是用ROWNUM填充的,也就是說,第二個欄位的前N個字元的區分度是唯一的。
在這種情況下,兩種方式建立的索引,就是一樣的,連分支塊裡存的內容也是一樣的,ORACLE只擷取了第二個欄位的前N個(ROWNUM佔的位元組數)就可以了
Index Leaf Distinct Number AV Av Cluster Date
Name BLV Blks Keys of Rows LEA Data Factor MM-DD-YYYY
------------------------------ --- ------------ -------------- -------------- ------- ------- ------------ ----------
T_1 2 103,614 103,614 103,614 1 1 103,614 05-09-2012
T_2 2 103,614 103,614 103,614 1 1 103,614 05-09-2012
Index Column Col Column
Name Name Pos Details
------------------------------ ------------------------------ ---- ------------------------
T_1 ID 1 NUMBER(22)
NAME 2 VARCHAR2(4000)
T_2 NAME 1 VARCHAR2(4000)
ID 2 NUMBER(22)
思考一個問題:有一個查詢select * from table where id=:1 and name=:2;
單從這一個SQL來看,該如何建立索引。ID區分度足夠好,NAME區分度稍微差點。
是ID,NAME好呢?還是NAME,ID好?為什麼?
實驗一把,看看。
CODE:
test@CRMD>create table test (id number ,name varchar2(4000));Table created.
test@CRMD>insert into test select rownum,lpad('test',4000) from dba_source;
103602 rows created.
test@CRMD>commit;
Commit complete.上面建立了一張擁有103602條記錄的表。表的第一個欄位唯一,第二個欄位全部都是一樣的、4K個位元組。
CODE:
test@CRMD>create index t_1 on test(id,name);Index created.
test@CRMD>create index t_2 on test(name,id);
Index created.建立了兩個索引。兩個索引的建立方式,唯一的區別就是鍵值的順序做了調換。
檢視統計資訊。
CODE:
test@CRMD>tabstatPlease enter Name of Table Owner: test
Please enter Table Name : test
Index Leaf Distinct Number AV Av Cluster Date
Name BLV Blks Keys of Rows LEA Data Factor MM-DD-YYYY
------------------------------ --- ------------ -------------- -------------- ------- ------- ------------ ----------
T_1 2 103,602 103,602 103,602 1 1 103,602 05-08-2012
T_2 11 103,602 103,602 103,602 1 1 103,602 05-08-2012
Index Column Col Column
Name Name Pos Details
------------------------------ ------------------------------ ---- ------------------------
T_1 ID 1 NUMBER(22)
NAME 2 VARCHAR2(4000)
T_2 NAME 1 VARCHAR2(4000)
ID 2 NUMBER(22)發現兩個索引的統計資訊除了BLEVEL(上面的BLV)外,其他的資訊完全一樣。而且BLEVEL出現了“天壤之別”。
T_1的BLV為2。
T_2的BLV為12。
記得我剛開始做DBA的時候,老師告訴我一條準則,就是兩個謂詞如果都做等值查詢,那麼區分度好的要放在前面,也就是distinct值大的放前面。當時我並不知道為什麼。
後面看LEWIS的COST成本演算法。
按照走索引的COST的演算法:
COST=索引成本+回表成本
索引成本=BLEVEL+BLOCKS*選擇率
回表成本=聚簇因子*表的有效選擇率
經過排除發現,貌似老師告訴我的準則是錯誤的,因為不管你建立的索引的鍵值順序是怎麼樣的,索引成本的選擇率和回表成本的表有效選擇率都是維持不變的。索引葉子的BLOCKS是不變的。聚簇因子雖然有可能不一樣,但是它完全依賴與索引相對與表的有序程度,與鍵值的區分度好壞完全沒關係。
那麼就差BLEVEL了。經過實驗發現BLEVEL確實是不一樣的。
為什麼呢?
CODE:
test@CRMD>COL OBJECT_NAME FOR A20test@CRMD>select object_name,object_id from dba_objects where object_name in ('T_1','T_2');
OBJECT_NAME OBJECT_ID
-------------------- ----------
T_1 10834
T_2 10835
test@CRMD>alter session set events 'immediate trace name treedump level 10834';
Session altered.
test@CRMD>alter session set events 'immediate trace name treedump level 10835';
Session altered.上面的步驟可以把索引的內部結果DUMP出來。方便我們找到分支節點的物理位置。我們對葉子節點的物理結構不關心,因為兩種情況下建立的索引,葉子節點數目是一樣的,儲存的內容也是一樣的,只是鍵值順序不一樣。但是之所以第二種方式建立的索引,BLEVEL高,就是因為分支節點的數目大大增多的緣故,因此我們透過比對分支節點儲存的內容,就能找到原因了。
檢視DUMP檔案:
CODE:
branch: 0x101980c 16881676 (0: nrow: 154, level: 2)--------------------------根節點地址branch: 0x1019b03 16882435 (-1: nrow: 740, level: 1)-------------------------第一個分支節點的地址
leaf: 0x101980d 16881677 (-1: nrow: 1 rrow: 1)
leaf: 0x101980e 16881678 (0: nrow: 1 rrow: 1)上面的DUMP內容是第一個索引的,我只擷取了一小部分。第二個索引的DUMP內容我就不貼出來了。根據DUMP檔案,我們DUMP出具體的分支塊來看看裡面具體的儲存。
第一種方式建立的索引的分支塊DUMP.
CODE:
row#0[8047] dba: 16882436=0x1019b04col 0; len 3; (3): c2 08 2b
col 1; TERM
row#1[8038] dba: 16882437=0x1019b05
col 0; len 3; (3): c2 08 2c
col 1; TERM
row#2[8029] dba: 16882438=0x1019b06
col 0; len 3; (3): c2 08 2d
col 1; TERM可以看到ORACLE只記錄了第一列的資料在分支塊裡,第二列的鍵值根本沒記錄!!!!
第二種方式建立的索引分支塊的DUMP.
CODE:
row#0[4046] dba: 16986126=0x103300ecol 0; len 4000; (4000):
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
省略非常長的內容
。
。
。
col 1; len 2; (2): c1 03
col 2; TERM第二種方式建立的索引兩個列的內容都做了記錄。
到這裡基本就清楚了,ORACLE的分支塊裡,只記錄必需的索引鍵值內容,也就是具有足夠的區分度可以導航找到葉子塊就可以了。
對於我們這個例子,對於第一種建立索引的方式,由於第一列的值已經是唯一的了,具有足夠的區分度了,第二列就可以不在索引分支塊裡出現了。
而對於第二種建立索引的方式,由於第一列的值沒有區分度,只有把第二列也加進去才具有區分度,因此就導致分支塊裡要把兩個列的鍵值都加進去。
而我們的例子又比較的極端,第二列非常的大,有4K個位元組,就導致分支塊的數目非常的多,最終導致索引的BLEVEL非常的大,在我們的例子裡
在一個只有103602行記錄表的索引裡,索引的層數就達到了12層。
看來當年老師告訴我的準則是有效的:當兩個謂詞只做等值查詢,把區分度好的作為前導列建立索引比較好。但是可能這個準則並不是多麼的重要,因為畢竟我們這裡的例子太特殊了,第二個欄位非常的長,而且不具有任何區分度,可能實際我們的環境中,效果差異並不那麼大。
還有這個準則不是肯定有效的,因為聚簇因子的關係,如果欄位A的順序跟表的儲存順序極其一致,而欄位B的極其不一致,但是欄位A的區別度糟糕,欄位B的區別度非常好。這個時候,到底是A,B好還是B,A好,要經過測試。
如果你充分理解了上面所說的內容,可以看下,如果我對錶的資料做了如下的處理,那麼兩種方式建立的索引,還會有區別嗎?
CODE:
test@CRMD>truncate table test;Table truncated.
test@CRMD>insert /*+ append */into test select rownum,rownum||lpad('test',3993) from dba_source;
103614 rows created.
test@CRMD>commit;
Commit complete.
test@CRMD>create index t_1 on test(id,name);
test@CRMD>create index t_2 on test(name,id);第二個欄位,還是4K個位元組,但是它的前導欄位的區分度是用ROWNUM填充的,也就是說,第二個欄位的前N個字元的區分度是唯一的。
在這種情況下,兩種方式建立的索引,就是一樣的,連分支塊裡存的內容也是一樣的,ORACLE只擷取了第二個欄位的前N個(ROWNUM佔的位元組數)就可以了
CODE:
sys@CRMD>@tabstatIndex Leaf Distinct Number AV Av Cluster Date
Name BLV Blks Keys of Rows LEA Data Factor MM-DD-YYYY
------------------------------ --- ------------ -------------- -------------- ------- ------- ------------ ----------
T_1 2 103,614 103,614 103,614 1 1 103,614 05-09-2012
T_2 2 103,614 103,614 103,614 1 1 103,614 05-09-2012
Index Column Col Column
Name Name Pos Details
------------------------------ ------------------------------ ---- ------------------------
T_1 ID 1 NUMBER(22)
NAME 2 VARCHAR2(4000)
T_2 NAME 1 VARCHAR2(4000)
ID 2 NUMBER(22)
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22034023/viewspace-723127/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 如何加快建 index 索引 的時間Index索引
- 如何構建高擴充套件性網站?套件網站
- dedecms建的網站如何去掉/index.html網站IndexHTML
- 如何構建高可用、高併發、高效能的雲原生容器網路?
- 索引學習二—blevel索引
- 如何使用 FutureBuilder and StreamBuilder 優雅的構建高質量專案Rebuild
- 如何構建測試團隊的軟實力 - 高學文
- 如何在阿里雲上構建高可用應用阿里
- 構建高併發高可用的電商平臺架構實踐架構
- OpenStack的高可用系統構建(1)
- OpenStack的高可用系統構建(2)
- 構建高併發&高可用&安全的IT系統-高併發部分
- 構建ORACLE高可用環境Oracle
- 用 Hystrix 構建高可用服務架構架構
- 萬丈高樓平地起:如何構建全流程機器學習平臺機器學習
- 開發者談如何以實際行動構建高質量遊戲遊戲
- 如何構建你的聊天介面
- 快速構建高併發微服務微服務
- 如何構建微服務架構微服務架構
- 一個社交App是如何構建高伸縮性的互動式系統APP
- 如何構建大型的前端專案前端
- Google 是如何構建 Web 框架的GoWeb框架
- Activemq構建高併發、高可用的大規模訊息系統MQ
- 如何構建零信任的雲資料架構架構
- 構建MHA實現MySQL高可用叢集架構MySql架構
- FutureBuilder and StreamBuilder 優雅的構建高質量專案Rebuild
- 構建持續高可用系統的破局之道
- 華為雲FunctionGraph構建高可用系統的實踐Function
- oracle 構建索引index_logging_nologgingOracle索引Index
- 【轉】如何建設高可用系統
- 如何使用webpack構建UeditorWeb
- 如何用Go構建GoGo
- dump index 的層次結構Index
- 使用Redis構建高併發高可靠的秒殺拍賣系統 - LuisRedisUI
- 如何構建自己的知識體系
- 如何構建自己的筆記系統?筆記
- Mysql+Corosync+Pacemaker+DRBD構建高可用MMySqlROS
- [譯] 使用 Recompose 來構建高階元件元件