程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> Oracle數據庫 >> Oracle數據庫基礎 >> Oracle分區鍵與分區本地索引

Oracle分區鍵與分區本地索引

編輯:Oracle數據庫基礎
關於全局索引和本地索引的優缺點,分別應該在什麼情況下使用,這方面的資料很多,本文不作討論。本文討論一種特殊情況,即建立在分區鍵之上的本地索引。9i也算是很老的Oracle版本了,只是很多系統包括很多大型的核心的系統都在用,因此本文介紹建立在分區鍵列上的本地索引存在的問題。下面是一些測試: 
1   1   SQL> create table t1 ( a int, b varchar2(300)) partition by range(a)  
2   2  (  
3   3   partition p01 values less than (1000),  
4   4   partition p02 values less than (2000),  
5   5   partition p03 values less than (3000),  
6   6   partition p04 values less than (4000),  
7   7   partition p05 values less than (5000),  
8   8   partition p06 values less than (6000),  
9   9   partition p07 values less than (7000),  
10 10   partition p08 values less than (8000),  
11 11   partition p09 values less than (9000),  
12 12   partition p10 values less than (10000),  
13 13   partition p11 values less than (11000),  
14 14   partition p12 values less than (12000),  
15 15   partition p13 values less than (13000),  
16 16   partition p14 values less than (14000),  
17 17   partition p15 values less than (15000),  
18 18   partition p16 values less than (16000),  
19 19   partition p17 values less than (17000),  
20 20   partition p18 values less than (18000),  
21 21   partition p19 values less than (19000),  
22 22   partition p20 values less than (20000)  
23 23  )  
24 24  /  
25   
26 表已創建。  
27   
28 SQL> insert into t1 select rownum,lpad('x',200,'x') from dual connect by rownum<20000;  
29   
30 已創建19999行。  
31   
32 SQL> commit;  
33   
34 提交完成。  
35   
36 SQL> insert /*+ append */ into t1 select * from t1;  
37   
38 已創建19999行。  
39   
40 SQL> commit;  
41   
42 提交完成。  
43   
44 SQL> insert /*+ append */ into t1 select * from t1;  
45   
46 已創建39998行。  
47   
48 SQL> commit;  
49   
50 提交完成。  
51   
52 SQL> insert /*+ append */ into t1 select * from t1;  
53   
54 已創建79996行。  
55   
56 SQL> commit;  
57   
58 提交完成。  
59   
60 SQL> insert /*+ append */ into t1 select * from t1;  
61   
62 已創建159992行。  
63   
64 SQL> commit;  
65   
66 提交完成。  
67   
68 SQL> insert /*+ append */ into t1 select * from t1;  
69   
70 已創建319984行。  
71   
72 SQL> commit;  
73   
74 提交完成。

  首先建立一個測試范圍分區表,分區鍵列是”a”,共20個分區,在這個測試表中生成約64萬行數據。下面在列a上建本地索引並收集統計信息: 


1 SQL> create index t1_idx on t1(a) local;  
2   
3 索引已創建。  
4   
5 SQL> exec dbms_stats.gather_table_stats(user,'T1',  
6     method_opt=>'for all columns size 1',cascade=>true);  
7   
8 PL/SQL 過程已成功完成。  
9 SQL> @sosi  
10   
11 Please enter Name of Table Owner (Null = TEST):  
12 Please enter Table Name to show Statistics for: t1  
13   
14 ***********  
15 Table Level  
16 ***********  
17   
18 Table                   Number                 Empty  
19 Name                   of Rows   Blocks       Blocks  
20 --------------- -------------- -------- ------------  
21 T1                     639,968   18,880            0  
22   
23 Column                    Column                       Distinct            Number       Number  
24 Name                      Details                        Values   Density Buckets        Nulls  
25 ------------------------- ------------------------ ------------ --------- ------- ------------  
26 A                         NUMBER(22)                     19,999   .000050       1            0  
27 B                         VARCHAR2(300)                       1  1.000000       1            0  
28   
29                               B  
30 Index                      Tree     Leaf       Distinct         Number      Cluster  
31 Name            Unique    Level     Blks           Keys        of Rows       Factor  
32 --------------- --------- ----- -------- -------------- -------------- ------------  
33 T1_IDX          NONUNIQUE     1    1,390         19,999        639,968      639,968  
34   
35 Index           Column                     Col Column  
36 Name            Name                       Pos Details  
37 --------------- ------------------------- ---- ------------------------  
38 T1_IDX          A                            1 NUMBER(22)  
39   
40 ***************  
41 Partition Level  
42 ***************  
43   
44   Part Partition               Number                 Empty  
45    Pos Name                   of Rows   Blocks       Blocks  
46 ------ --------------- -------------- -------- ------------  
47      1 P01                     31,968      944            0  
48      2 P02                     32,000      944            0  
49      3 P03                     32,000      944            0  
50      4 P04                     32,000      944            0  
51      5 P05                     32,000      944            0  
52      6 P06                     32,000      944            0  
53      7 P07                     32,000      944            0  
54      8 P08                     32,000      944            0  
55      9 P09                     32,000      944            0  
56     10 P10                     32,000      944            0  
57     11 P11                     32,000      944            0  
58     12 P12                     32,000      944            0  
59     13 P13                     32,000      944            0  
60     14 P14                     32,000      944            0  
61     15 P15                     32,000      944            0  
62     16 P16                     32,000      944            0  
63     17 P17                     32,000      944            0  
64     18 P18                     32,000      944            0  
65     19 P19                     32,000      944            0  
66     20 P20                     32,000      944            0  
67                                     B  
68 Index           Partition        Tree     Leaf       Distinct         Number  
69 Name            Name            Level     Blks           Keys        of Rows  
70 --------------- --------------- ----- -------- -------------- --------------  
71 T1_IDX          P01                 1       67            999         31,968  
72 T1_IDX          P02                 1       67          1,000         32,000  
73 T1_IDX          P03                 1       67          1,000         32,000  
74 T1_IDX          P04                 1       67          1,000         32,000  
75 T1_IDX          P05                 1       67          1,000         32,000  
76 T1_IDX          P06                 1       67          1,000         32,000  
77 T1_IDX          P07                 1       67          1,000         32,000  
78 T1_IDX          P08                 1       67          1,000         32,000  
79 T1_IDX          P09                 1       67          1,000         32,000  
80 T1_IDX          P10                 1       67          1,000         32,000  
81 T1_IDX          P11                 1       72          1,000         32,000  
82 T1_IDX          P12                 1       72          1,000         32,000  
83 T1_IDX          P13                 1       72          1,000         32,000  
84 T1_IDX          P14                 1       72          1,000         32,000  
85 T1_IDX          P15                 1       72          1,000         32,000  
86 T1_IDX          P16                 1       72          1,000         32,000  
87 T1_IDX          P17                 1       72          1,000         32,000  
88 T1_IDX          P18                 1       72          1,000         32,000  
89 T1_IDX          P19                 1       72          1,000         32,000  
90 T1_IDX          P20                 1       72          1,000         32,000  

  下面執行查詢:


1 SQL> set arraysize 1000  
2 SQL> set autot traceonly  
3 SQL> select * from t1 where a=1000;  
4   
5 已選擇32行。  
6   
7 Execution Plan  
8 ----------------------------------------------------------  
9    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=33 Card=32 Bytes=652  
10           8)  
11   
12    1    0   TABLE Access (BY LOCAL INDEX ROWID) OF 'T1' (Cost=33 Card=  
13           32 Bytes=6528)  
14   
15    2    1     INDEX (RANGE SCAN) OF 'T1_IDX' (NON-UNIQUE) (Cost=1 Card  
16           =32)  
17   
18 Statistics  
19 ----------------------------------------------------------  
20           0  recursive calls  
21           0  db block gets  
22          40  consistent gets  
23           0  physical reads  
24         300  redo size  
25         828  bytes sent via SQL*Net to clIEnt  
26         503  bytes received via SQL*Net from clIEnt  
27           2  SQL*Net roundtrips to/from clIEnt  
28           0  sorts (memory)  
29           0  sorts (disk)  
30          32  rows processed  

  這裡看上去沒有什麼問題。

  下面再看看全局索引的情況: 


  1 SQL> drop index t1_idx;  
  2   
  3 索引已丟棄。  
  4   
  5 SQL> create index t1_idx on t1(a) ;  
  6   
  7 索引已創建。  
  8   
  9 SQL> exec dbms_stats.gather_table_stats(user,'T1',  
10     method_opt=>'for all columns size 1',cascade=>true);  
11   
12 PL/SQL 過程已成功完成。  
13   
14 SQL> @sosi  
15   
16 Please enter Name of Table Owner (Null = TEST):  
17 Please enter Table Name to show Statistics for: t1  
18   
19 ***********  
20 Table Level  
21 ***********  
22   
23 Table                   Number                 Empty  
24 Name                   of Rows   Blocks       Blocks  
25 --------------- -------------- -------- ------------  
26 T1                     639,968   18,880            0  
27   
28 Column                    Column                       Distinct            Number  
29 Name                      Details                        Values   Density Buckets  
30 ------------------------- ------------------------ ------------ --------- -------  
31 A                         NUMBER(22)                     19,999   .000050       1  
32 B                         VARCHAR2(300)                       1  1.000000       1  
33   
34                               B  
35 Index                      Tree     Leaf       Distinct         Number  
36 Name            Unique    Level     Blks           Keys        of Rows  
37 --------------- --------- ----- -------- -------------- --------------  
38 T1_IDX          NONUNIQUE     2    1,737         19,999        639,968  
39   
40 Index           Column                     Col Column  
41 Name            Name                       Pos Details  
42 --------------- ------------------------- ---- ------------------------  
43 T1_IDX          A                            1 NUMBER(22)  
44   
45 ***************  
46 Partition Level  
47 ***************  
48   
49   Part Partition               Number                 Empty  
50    Pos Name                   of Rows   Blocks       Blocks  
51 ------ --------------- -------------- -------- ------------  
52      1 P01                     31,968      944            0  
53      2 P02                     32,000      944            0  
54      3 P03                     32,000      944            0  
55      4 P04                     32,000      944            0  
56      5 P05                     32,000      944            0  
57      6 P06                     32,000      944            0  
58      7 P07                     32,000      944            0  
59      8 P08                     32,000      944            0  
60      9 P09                     32,000      944            0  
61     10 P10                     32,000      944            0  
62     11 P11                     32,000      944            0  
63     12 P12                     32,000      944            0  
64     13 P13                     32,000      944            0  
65     14 P14                     32,000      944            0  
66     15 P15                     32,000      944            0  
67     16 P16                     32,000      944            0  
68     17 P17                     32,000      944            0  
69     18 P18                     32,000      944            0  
70     19 P19                     32,000      944            0  
71     20 P20                     32,000      944            0  
72 Partition       Column  Distinct            Number Number  
73 Name            Name      Values   Density Buckets  Nulls  
74 --------------- ------- -------- --------- ------- ------  
75 P01             A            999   .001001       1      0  
76                       B              1  1.000000       1      0  
77 P02             A          1,000   .001000       1      0  
78                       B              1  1.000000       1      0  
79 P03             A          1,000   .001000       1      0  
80                       B              1  1.000000       1      0  
81 P04             A          1,000   .001000       1      0  
82                       B              1  1.000000       1      0  
83 P05             A          1,000   .001000       1      0  
84                       B              1  1.000000       1      0  
85 P06             A          1,000   .001000       1      0  
86                       B              1  1.000000       1      0  
87 P07             A          1,000   .001000       1      0  
88                       B              1  1.000000       1      0  
89 P08             A          1,000   .001000       1      0  
90                       B              1  1.000000       1      0  
91 P09             A          1,000   .001000       1      0  
92                       B              1  1.000000       1      0  
93 P10             A          1,000   .001000       1      0  
94                       B              1  1.000000       1      0  
95 P11             A          1,000   .001000       1      0  
96                       B              1  1.000000       1      0  
97 P12             A          1,000   .001000       1      0  
98                       B              1  1.000000       1      0  
99 P13             A          1,000   .001000       1      0  
100                     B              1  1.000000       1      0  
101 P14           A          1,000   .001000       1      0  
102                     B              1  1.000000       1      0  
103 P15             A          1,000   .001000       1      0  
104                      B              1  1.000000       1      0  
105 P16             A          1,000   .001000       1      0  
106                      B              1  1.000000       1      0  
107 P17             A          1,000   .001000       1      0  
108                      B              1  1.000000       1      0  
109 P18             A          1,000   .001000       1      0  
110                      B              1  1.000000       1      0  
111 P19             A          1,000   .001000       1      0  
112                      B              1  1.000000       1      0  
113 P20             A          1,000   .001000       1      0  
114                      B              1  1.000000       1      0  

        再次執行同樣的查詢: 


1 SQL> set arraysize 1000  
2 SQL> set autot traceonly  
3 SQL> select * from t1 where a=1000;  
4   
5 已選擇32行。  
6   
7 Execution Plan  
8 ----------------------------------------------------------  
9    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=59 Card=32 Bytes=652  
10           8)  
11   
12    1    0   TABLE Access (FULL) OF 'T1' (Cost=59 Card=32 Bytes=6528)  
13   
14 Statistics  
15 ----------------------------------------------------------  
16           0  recursive calls  
17           0  db block gets  
18         948  consistent gets  
19           0  physical reads  
20           0  redo size  
21         828  bytes sent via SQL*Net to clIEnt  
22         503  bytes received via SQL*Net from clIEnt  
23           2  SQL*Net roundtrips to/from clIEnt  
24           0  sorts (memory)  
25           0  sorts (disk)  
26          32  rows processed

  可以看到,這一次Oracle選擇了分區剪裁之後的全表掃描,也就是掃描了整個分區,而沒有使用索引。

  再看看使用HINT強制使用索引:


1 SQL> select /*+ index(t1) */ * from t1 where a=1000;  
2   
3 已選擇32行。  
4   
5 Execution Plan  
6 ----------------------------------------------------------  
7    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=644 Card=32 Bytes=65  
8           28)  
9   
10    1    0   TABLE Access (BY GLOBAL INDEX ROWID) OF 'T1' (Cost=644 Car  
11           d=32 Bytes=6528)  
12   
13    2    1     INDEX (RANGE SCAN) OF 'T1_IDX' (NON-UNIQUE) (Cost=4 Card  
14           =640)  
15   
16 Statistics  
17 ----------------------------------------------------------  
18           0  recursive calls  
19           0  db block gets  
20          37  consistent gets  
21           0  physical reads  
22           0  redo size  
23         828  bytes sent via SQL*Net to clIEnt  
24         503  bytes received via SQL*Net from clIEnt  
25           2  SQL*Net roundtrips to/from clIEnt  
26           0  sorts (memory)  
27           0  sorts (disk)  
28          32  rows processed

  可以看到,這裡使用HINT,讓SQL強制使用索引之後,邏輯讀只有37(為什麼比本地索引時還低,此處不深究)。顯然,Oracle此前選擇全表(分區)掃描的執行計劃是錯誤的。

  為什麼會出現這樣的情況?這得從通過索引訪問表數據的成本說起:


1 cost =  
2         blevel +  
3         ceiling(leaf_blocks * effective index selectivity) +  
4         ceiling(clustering_factor * effective table selectivity)


  簡單地說成本分為三部分:

  訪問索引分枝節點的成本。
  訪問索引葉節點的成本。
  通過索引取得ROWID,通過ROWID回表訪問表數據的成本。
  在上面的測試中,使用全局索引時,強制使用索引時,CBO評估的成本中,掃描索引的成本僅僅為4,而回表的成本高達640。能掃描分區P2評估的成本僅為59,自然CBO就選用了全表掃描,而不是索引掃描。

  那麼接下來的問題是,為什麼回表的成本那麼高?看看回表部分成本,表的有效選擇率,這裡由於索引列也是分區列,Oracle應用了分區剪裁,然後取了剪裁後的分區P2的A列的選擇率來作為公式裡的選擇率,即0.001,再看看索引的clustering factor,為639968(與表的行數一致,表明是最差情況下的聚集因子了),二者相乘再四捨五入就是640。這就是CBO估算的表訪問部分的成本。這裡的問題是,全局索引的clustering factor是針對整個表中所有數據的,這裡取的選擇率只是針對P2這個分區的。clustering factor可以簡單理解為通過索引訪問完表中的所有數據需要的IO數量,這裡就相當於訪問每一行都需要1次IO,然而實際上P2分區僅僅不過32000行數據,最差情況下,0.001選擇的數據,IO數量也才32。

  在使用本地索引時,同樣由於分區剪裁,取的是P2分區A列上的選擇率,但是索引也只取了P2分區,clustering factor大大減少(為32000)。這樣計算的成本也只有全局索引的1/20。

  使用HINT後的結果,證明索引也是很有效率的,跟本地索引幾乎一樣。只是由於Oracle成本計算的問題,導致了本文測試過中出現的情況。

  在10g中,回表成本計算的“表有效選擇率”使用了索引的distinct keys,所以這就避免了這個問題。(注:這是指組成索引的所有列上都有相等條件比較的簡單情況)

  分區鍵上的列,在建立索引時,很顯然應該考慮建為本地索引,本文從Oracle的一個BUG也表明的確應該這樣做,盡管在OLTP系統中,分區表的索引應該傾向於考慮使用全局索引。不幸的是,在一個省集中的關鍵系統中就由於將分區列建成了全局索引,導致了嚴重的性能問題。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved