1)两者重建索引时的扫描方式不同,rebuild用的是“INDEX FAST FULL SCAN”,rebuild online用的是“TABLE ACCESS FULL”;

即alter index rebuild online实质上是扫描表而不是扫描现有的索引块来实现索引的重建,alter index rebuild 只扫描现有的索引块来实现索引的重建。

SQL> explain plan for
  2  alter index ind_test_id rebuild;
 
Explained.
 
SQL> select * from table(dbms_xplan.display);
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------
Plan hash value: 187312216
 
--------------------------------------------------------------------------------------
| Id  | Operation              | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | ALTER INDEX STATEMENT  |             |   115K|   565K|    78   (2)| 00:00:10 |
|   1 |  INDEX BUILD NON UNIQUE| IND_TEST_ID |       |       |            |          |
|   2 |   SORT CREATE INDEX    |             |   115K|   565K|            |          |
|   3 |    INDEX FAST FULL SCAN| IND_TEST_ID |       |       |            |          |
--------------------------------------------------------------------------------------
 
10 rows selected.
 
SQL> explain plan for
  2  alter index ind_test_id rebuild online;
 
Explained.
 
SQL> select * from table(dbms_xplan.display);
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------
Plan hash value: 3365522411
 
--------------------------------------------------------------------------------------
| Id  | Operation              | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | ALTER INDEX STATEMENT  |             |   115K|   565K|    78   (2)| 00:00:10 |
|   1 |  INDEX BUILD NON UNIQUE| IND_TEST_ID |       |       |            |          |
|   2 |   SORT CREATE INDEX    |             |   115K|   565K|            |          |
|   3 |    TABLE ACCESS FULL   | TEST        |   115K|   565K|    78   (2)| 00:00:10 |
--------------------------------------------------------------------------------------
 
10 rows selected.

2)如果index文件坏了,并且是 primary key ,rebuild online 非常有用

SQL> alter index PK_PDB_PDBOUTINFO  rebuild tablespace ts_indx;
alter index PK_PDB_PDBOUTINFO  rebuild tablespace ts_indx
*
第 1 行出现错误:
ORA-01115: IO error reading block from file 19 (block # 41925)
ORA-01110: data file 19: '/u301/data/ts_indx/ts_indx01.dbf'
ORA-27091: unable to queue I/O
ORA-27072: File I/O error
Additional information: 3
Additional information: 41909
Additional information: 16384
  *********************************************
alter index PK_PDB_PDBOUTINFO rebuild online  tablespace ts_indx ;
it will ok ,but it is slow !

3)
create index和rebuild index时都会对原表加4号锁,对obj$加3号锁,阻止对原表的DML操作
create index online 和 rebuild index online 采用新增一张临时表来处理创建或者重建索引时对原表索引列的修改,这样就可以不用锁表,保证原表的dml操作在过程中可以正常执行。
rebuild的过程用户仍然可以走旧的索引来查询,索引重建索引需要原来索引两倍的空间

测试1:create index加锁

[session 1]
SQL> select distinct sid from v$mystat;

       SID
----------
       147

SQL> create index idx_name on t(object_name);

Index created.

[session 2]

SQL> select object_name,lmode from v$lock l,dba_objects o where o.object_id=l.id1 and l.type='TM' and sid=147;

OBJECT_NAME                         LMODE
------------------------------ ----------
OBJ$                                    3
T                                       4

测试1:rebulid index online加锁

[session 1]

SQL> alter index idx_name rebuild online;

[session 2]
SQL> select object_name,lmode from v$lock l,dba_objects o where o.object_id=l.id1 and l.type='TM' and sid=147;

OBJECT_NAME                         LMODE
------------------------------ ----------
T                                       2
SYS_JOURNAL_52675                       4


测试3:在online重建过程中,数据库是如何记录的

[session 1]

SQL> alter index idx_name rebuild online;

[session 2]

SQL> update t set object_name = '123'  where rownum<2;

1 row updated.

SQL> commit;

Commit complete.

SQL> select * from SYS_JOURNAL_52675; 

C0                             O     PARTNO RID
------------------------------ - ---------- ------------------
123                            I          0 D/////AABAAAOtKAAA
new                            D          0 D/////AABAAAOtKAAA

当session1报Index altered.再查询就会报表不存在。

SQL> select * from SYS_JOURNAL_52675;
select * from SYS_JOURNAL_52675
              *
ERROR at line 1:
ORA-00942: table or view does not exist

可见oracle在修改到索引列的过程中会把修改的值写入一张临时创建的表SYS_JOURNAL_xxxx,xxxx表示索引的object_id,可以通过查询select index_name,object_id from user_objects得到
比如例子中update操作,在临时创建表中记录了两个动作,一个旧值D代表删除,一个新值I代表插入,同时记录了修改的rowid。修改到非索引列时SYS_JOURNAL_xxxx不会有记录


4)rebuild index online在执行期间不会阻塞DML操作,但在开始和结束阶段,需要请求模式为4的TM锁。因此,如果在rebuild index online开始前或结束时,有其它长时间的事物在运行,很有可能就造成大量的锁等待。具体可以参考:
rebuild index online的锁机制浅析
11G中有所不同:
rebuild index online的锁机制浅析(续)

这里可以通过设置10626事件,避免阻塞该表上的其它DML操作,但在rebuild index online开始、结束阶段有其它事物未完成,则会失败,报ORA-00051: timeout occurred while waiting for a resource 错误:
(以下测试在9208、10203中测试通过,在10201中等待几秒后,直接报ORA-00051,之后再想重建则报ORA-08104,处理方法见后面,因而设置该事件还需谨慎)
会话一:

SQL> select max(sid) from v$mystat;
 
  MAX(SID)
----------
        82
 
SQL> delete from test_ls where id=1;
 
1 row deleted.

会话二:

SQL> select max(sid) from v$mystat;
 
  MAX(SID)
----------
        70
 
SQL> alter session set events '10626 trace name context forever';
 
Session altered.
 
SQL> alter index IND_TEST_ID rebuild online;

此时会话二会话被阻塞。

会话三:

SQL> delete from test_ls where id=3;
 
1 row deleted.
 
SQL> commit;
 
Commit complete.
 
SQL> select * from test_ls where id=3;
 
no rows selected

会话三的DML操作并不会被阻塞。

会话一rollback后,会话二报错:

alter index IND_TEST_ID rebuild online
*
ERROR at line 1:
ORA-00051: timeout occurred while waiting for a resource

增加表的数据量,同样可以测出在rebuild online结束时,如果该表上还有其它事物未完成,则报错,如果无其它事物,索引可以重建成功,期间都不会阻塞其它DML操作。

三、rebuild onlie时,如果发生意外中断,很容易造成ORA-08104错误,之后再想rebuild、drop索引都会报错。10G之前需要等待SMON去清理,10G以后可以使用DBMS_REPAIR.ONLINE_INDEX_CLEAN进行手工清理:

SQL> alter index IND_TEST_ID rebuild online;
alter index IND_TEST_ID rebuild online
*
ERROR at line 1:
ORA-08104: this index object 93996 is being online built or rebuilt
 
SQL> DECLARE
  2    RetVal BOOLEAN;
  3    OBJECT_ID BINARY_INTEGER;
  4    WAIT_FOR_LOCK BINARY_INTEGER;
  5 
  6  BEGIN
  7    OBJECT_ID := 93996;
  8    WAIT_FOR_LOCK := NULL;
  9 
 10    RetVal := SYS.DBMS_REPAIR.ONLINE_INDEX_CLEAN ();
 11    COMMIT;
 12  END;
 13  / 
 
PL/SQL procedure successfully completed.
 
SQL> alter index ind_test_id rebuild online;
 
Index altered