天天看點

Oracle性能分析9:重建索引索引不可用索引碎片為什麼重建索引重建索引

當索引出現問題時,會導緻嚴重的性能問題,索引問題包括索引不可用、索引碎片導緻性能下降,我們需要一些手段在檢測索引的問題,并解決這些問題。這一篇将為你講述怎麼定位索引問題,并提供了解決的辦法。

索引不可用

索引不可用的原因有很多,包括:

 1)索引空間耗盡,導緻SQL*Loader更新索引失敗;

 2)建立索引的過程中執行個體失敗;

 3)唯一鍵有重複值;

 4)某個索引的順序與sorted indexes子句中指定的順序不同;

 5)移動表或表分區(alter table move和alter table move partition);

 6)對表執行線上重定義;

 7)截斷表分區(alter table truncate partition);

 8)導入分區;

 9)删除表分區;

 10)拆分表的分區或子分區(alter table split partition);

 11)分區索引的維護操作(alter index split partition)。

除了上述的這些原因之外,你還可以手動的将索引标注為不可用,這樣可以使批量加載速度更快,下面是把索引的狀态改變為不可用的方法:

alter index IDX_HISTORYALARM_HOUR$01 unusable
           

如果你的索引為分區索引,這個操作将導緻所有分區的索引都不可用,你也可以指定某個分區的索引不可用:

alter index IDX_HISTORYALARM$02 modify partition HISTORYALARM20140731 unusable
           

通過下面的方法可以檢視索引的狀态:

select ind.INDEX_NAME,ind.status,ind.PARTITIONED from user_indexes ind where index_name like '%HISTORYALARM%'

INDEX_NAME			STATUS		PARTITIONED
---------------------------------------------------------------------
IDX_HISTORYALARM_HOUR$01		UNUSABLE		NO
IDX_HISTORYALARM$02			N/A		YES
           

可以看到,全局索引的狀态已經變為UNUSABLE,但本地索引的狀态辨別為N/A,通過下面的方法可以檢視本地索引在每一個分區中的索引狀态:

INDEX_NAME			PARTITION_NAME			STATUS
------------------------------------------------------------------------------------------
IDX_HISTORYALARM$02			HISTORYALARM20140731		UNUSABLE
IDX_HISTORYALARM$02			HISTORYALARM20140801		USABLE
IDX_HISTORYALARM$02			HISTORYALARM20140802		USABLE
IDX_HISTORYALARM$02			HISTORYALARM20140803		USABLE
IDX_HISTORYALARM$02			HISTORYALARM20140804		USABLE
IDX_HISTORYALARM$02			HISTORYALARM20140805		USABLE
......
           

可以看到分區HISTORYALARM20140731的索引已經标注為UNUSABLE。

當索引被标注為不可用後,優化器就會忽略這些索引,Oracle在DML更改表時也不再維護這些索引,如果希望優化器再次使用它,就必須先重建(rebuild)索引。

索引碎片

随着時間的推移,由于大量的删除操作,索引可能會産生碎片。Oracle文檔(Performance Tuning Manual for Oracle DataBase 11.2)建議運作"analyze...validate"語句來識别需要重建的索引,這個操作會将索引的統計資料放到INDEX_STATS視圖中,下面是該視圖中的關鍵列:

 1)高度(HEIGHT):索引的高度,從1開始,1代表隻有根的索引;

 2)塊數(BLOCKS):配置設定給索引的塊數;

 3)葉行數(LF_ROWS):葉行數(包括已删除的行);

 4)已删除的葉行數(DEL_LF_ROWS):已删除尚未清理的葉行條目數;

 5)已用空間(USED_SPACE):索引内使用的總空間(包括已删除的條目);

 6)已用百分比(PCT_USED):索引内使用空間的百分比(包括已删除的條目)。。計算公式:(USED_SPACE / BTREE_SPACE) * 100;

 7)B樹空間(BTREE_SPACE):索引的總大小(包括已删除的條目)。

下面通過一個例子來學習該視圖的使用。

先建立一個測試表格,并在上面建立索引:

create table test as select rownum id,'Test' text from dual connect by level <= 100000;
create index idx_test on test(id);
           

然後執行索引分析語句:

analyze index idx_test validate structure;
           

注意在執行分析語句之前INDEX_STATS視圖是空的,現在查詢該視圖來檢查被删除的葉行數:

select lf_rows,lf_blks,del_lf_rows from index_stats;

LF_ROWS		LF_BLKS		DEL_LF_ROWS
--------------------------------------------------------------------------------
100000		222		0
           

這裡可以看出删除的葉行數為0,接下來我們删除表中大量的行,再次運作分析語句:

delete test where id <= 99999;
commit;
analyze index idx_test validate structure;
           

然後查詢被删除的葉行數:

select lf_rows,lf_blks,del_lf_rows from index_stats;

LF_ROWS		LF_BLKS		DEL_LF_ROWS
-----------------------------------------------------------
100000		222		99999
           

為了讓Oracle能夠得到正确的執行計劃,我們先收集表和索引的統計資訊:

begin
  dbms_stats.gather_table_stats(ownname   => user,
                                tabname => 'TEST',
                                cascade   => TRUE);
end;
           

然後執行一個索引範圍掃描的查詢:

select * from test where id > 10;

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.27          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        1      0.01       1.53         56        224          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      0.01       1.81         56        224          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 5  

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  TABLE ACCESS BY INDEX ROWID TEST (cr=224 pr=56 pw=0 time=1538212 us)
      1   INDEX RANGE SCAN IDX_TEST (cr=223 pr=56 pw=0 time=1527442 us)(object id 58594)


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       2        0.00          0.00
  SQL*Net message from client                     2        0.79          0.83
  db file sequential read                        56        0.03          0.41
********************************************************************************
           

查詢使用了索引範圍掃描,雖然整個表隻有一條資料,但由于索引中的索引碎片,導緻索引掃描任然讀取了56個資料塊。下面重建該索引:

alter index IDX_TEST rebuild;
           

然後按上面的方法生成并檢視索引資訊:

LF_ROWS	LF_BLKS	DEL_LF_ROWS
-----------------------------------
1	1	0
           

可以看出索引碎片消失。

注意事項:

 1)執行索引分析會鎖定表,直到索引分析完畢才解除鎖定;

 2)在大多數情況下,Oracle會盡可能的重用索引中已删除條目的空間。

為什麼重建索引

實際上,重建(rebuild)索引就是重新建立索引,但它比删除原索引再重新建立索引的做法要好,因為在重建索引時存儲空間已配置設定給索引,而不需要再指定索引建立語句。

關于索引重建有很多依據,但其中有一些并不準确,下面列舉了一些:

 1)Oracle的B樹索引随着時間的推移變得不平衡

 由于B樹索引的根塊和所有的葉塊之間的高度始終是一緻的,是以這不正确。

 2)索引中被删除的空間無法重用

 實際上Oracle會重用被删除的空間。

 3)達到一定層數的索引是低效的

 索引的層數取決于索引有多少條目,重建不能解決問題。

 4)具有糟糕的聚蔟因子的索引,可以通過重建修複

 重建索引并不能改變表中資料行或索引的順序,是以聚蔟因子(見使用索引的聚蔟因子)完全不受索引重建影響。如果想改進聚蔟因子,實際上需要重建表。

那麼具體為什麼重建索引呢?

當索引不可用時,應該重建索引,但我們是否應該重建索引來消除索引碎片呢?

當你的查詢大部分都是通過索引通路讀取單個行,那麼重建索引對性能影響很小。但對于範圍查詢,由于大量的索引碎片會導緻查詢增加大量的無效IO,是以重建索引是有意義的,即使Oracle會重用索引碎片,但重建索引也可以使索引變得更加緊湊,進而提高查詢的效率。

重建索引

上面已經使用到重建索引的方法:

alter index IDX_TEST rebuild;
           

但重建索引的過程中會對表加鎖,阻止其他對表的操作,直到索引重建完成。從Oracle 10g開始,Oracle提供了線上重建索引的方法:

<pre class="sql" name="code">alter index IDX_TEST rebuild online;
           

線上重建索引不會再導緻索引鎖定。

重建分區索引則需要帶上分區資訊:

alter index IDX_TEST rebuild partition partition_name online;
           

Oracle也為創佳和重建索引提供了一些參數,用于提高建立和重建索引的效率。

并行建立或重建索引

為了建立索引,資料庫需要進行全表掃描,并行建立可以加快索引的建立速度,速度的提升由并行度和CPU數量決定:

create index IDX_TEST on test(id) parallel 4 online;
           

也可以用于重建索引:

alter index IDX_TEST rebuild parallel 4 online;
           

需要注意的是這個操作将使索引的并行度(見使用索引)永遠變為這個值,如下:

select degree from user_indexes where index_name = 'IDX_TEST';

DEGREE
--------------------
4
           

如果打算讓資料庫在處理你的索引時使用并行機制,則正好,否則,你需要在執行了并行建立和重建操作後禁用并行:

alter index IDX_TEST noparallel;
           

如果忘記禁用并行,可能會導緻嚴重的性能問題。

在索引建立或重建時避免生成重做資訊

不把建立或者重建的索引項寫入重做日志,可以大大縮短索引建立或重建的時間:

create index IDX_TEST on test(id) nologging online;
           

也可以在重建索引時使用:

alter index IDX_TEST rebuild nologging online;
           

nologging不僅可以極大地提高性能,而且不填充多個重做日志檔案,節省空間。

壓縮索引

在非唯一索引中使用壓縮,可以減少重複鍵占用的空間:

compress <數字,并小于等于索引包括的字段值>
           

一個執行個體如下:

create index IDX_TEST on test(id) compress 1 online;
           

同樣可以用于重建索引:

alter index IDX_TEST rebuild compress 1 online;