oracle online 和非online创建index

最近线上系统有个需求,就是一张大表需要创建索引,而且这张表有频繁的DML,开发人员认为,就是一条create index语句,没什么可担心的,直接执行就行,其实不然,首先任何线上的操作,哪怕你认为很简单的操作,都要心存敬畏,不能想当然,要有理论支撑,其次,对于create index,尤其是增删改频繁的大表,是有讲究的,否则很可能无法执行成功,甚至对性能造成影响。

 

之前写了一篇《创建索引的两种方式比对》,介绍了创建索引的两种方法,即online和非online,当时给出的结论,如下所示,

(1) online和非online方式创建索引,效果相同。 
(2) online方式创建索引,由于使用了一张临时表,以ROW SHARE锁表,不会阻塞原表DML的语句,非online方式创建索引,则会以SHARE NOWAIT锁表,阻塞原表DML语句。 
(3) 由于online方式创建索引,Oracle执行工作复杂,因此比非online方式创建索引用时要久。 
(4) 一句话“不能什么便宜均占着”,要么选择可以快速创建索引的非online方式但创建期间会锁表阻塞DML语句,要么选择不会阻塞原表DML语句的online方式创建索引但用时较久。从实际来看,我理解,若小表选择任何一种均可,大表,尤其是生产系统,找不着非高峰时间,选择online更合理一些,若不关注是否影响DML操作,则两种方式均可以了。

 

我们再做下实验,创建测试表,

SQL> create table tbl(id number, name varchar2(1));
Table created.

 

场景一:非online创建索引

session 1:插入一条数据,但不提交,

SQL> select sid from v$mystat where rownum = 1;
 SID
----------
  1

SQL> insert into tbl values(2, 'b');        
1 row created.


此时会出现一个表级锁,和一个行级锁,
SQL> select sid, type, id1, lmode, request, block from v$lock where sid in (1, 40)
   and type in ('TX', 'TM'); 
SID    TYPE    ID1    LMODE    REQUEST   BLOCK
----- -------- ---------- ---------- ---------- ----------
  1    TM    14338     3        0      0
  1    TX    131079    6        0      0

SQL> select object_name from dba_objects 
   where object_id = 14338;
OBJECT_NAME
--------------------------------------------------------------------------------
TBL

 

 

关于锁的模式,

0:none 
1:null 空 
2:Row-S 行共享(RS):共享表锁 
3:Row-X 行专用(RX):用于行的修改 
4:Share 共享锁(S):阻止其他DML操作 
5:S/Row-X 共享行专用(SRX):阻止其他事务操作 
6:exclusive 专用(X):独立访问使用 

 

此时session 2创建索引,注意未加online关键字,提示ORA-00054错误,表示资源繁忙,

SQL> select sid from v$mystat where rownum = 1;     
 SID
--------
  40

SQL> create index idx_tbl on tbl(id);
create index idx_tbl on tbl(id)
                       *
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

 

从现象上看,说明使用非online创建索引,如果表上存在未提交事务,则无法执行,提示错误ORA-00054,直到所有事务已经提交。

 

如果上述操作反向来,即先create index创建索引,创建过程中,执行DML操作,此时DML操作会被hang,相当于创建索引阻碍了DML。

 

从锁的情况看,此时create index的session会请求mode=4的TM锁,而insert未提交的session未释放锁,block=1,LMODE为2、3、4不影响DML(insert,delete,update,select)操作, 但DDL(alter、drop等)操作会提示ORA-00054错误,这和现象一致,

 SID   TY     ID1     LMODE   REQUEST   BLOCK
------ ------- ---------- --------- --------- ---------
 40    TM    21504     0      4      0
  1    TM    21504     3      0      1
  1    TX    524310    6      0      0

 

另外,我们从10046的trace(BISAL_ora_52965.trc,大小117K),发现非online方式创建索引,一共SELECT数据字典共计23次,UPDATE了2次,INSERT了4次,总共对数据字典,执行31次操作,才完成了创建索引的工作,

grep 'select ' BISAL_ora_52965.trc | wc -l
23

grep 'update ' BISAL_ora_52965.trc | wc -l
2

grep 'delete ' BISAL_ora_52965.trc | wc -l
0

grep 'insert ' BISAL_ora_52965.trc | wc -l
4

 

其中所有的DML操作如下所示,使用SHARE MODE NOWAIT方式,LOCK表,对obj$、seg$、icol、ind$等数据字典,进行增删改操作,

LOCK TABLE 'TBL' IN SHARE MODE  NOWAIT
insert into obj$
insert into seg$
update seg$
insert into icol$
insert into ind$
update obj$

 

场景二:online创建索引

同样,session 1插入一条记录,但不提交,session 2执行create index ... online操作,不会像场景一抛出错误,而是等待状态,直到session 1提交,

SQL> create index idx_tbl on tbl(id) online;

 

从现象上看,说明使用online来创建索引,如果表上存在未提交事务,则无法执行,不会报错,但会等待,直到所有事务已经提交。

 

如果上述操作反向来,先执行了create index ... online,创建过程中,执行DML操作,此时这些DML能执行,不会影响。

 

等待状态期间,检索锁的信息,除了ID1=14338对应TBL表存在TM锁的持有和等待,还对ID1=14341对应SYS_JOURNAL_14340表有TM锁,同时这两个会话,对TBL表的行,存在TX锁的持有和等待,另外一个TX就是对SYS_JOURNAL_14340的行持锁,

SQL> select sid, type, id1, lmode, request, block from v$lock where sid in (1, 40)
   and type in ('TX', 'TM');
 SID   TYPE   ID1    LMODE    REQUEST   BLOCK
------ ------- -------- ---------- ---------- ----------
 40    TX   131072    0        4      0
 40    TM   14338     2        0      0
 40    TM   14341     4        0      0
  1    TM   14338     3        0      0
 40    TX   327682    6        0      0
  1    TX   131072    6        0      1

SQL> select object_name from dba_objects where object_id = 14341;
OBJECT_NAME
--------------------------------------------------------------------------------
SYS_JOURNAL_14340

SQL> select object_name from dba_objects where object_id = 14338;
OBJECT_NAME
--------------------------------------------------------------------------------
TBL

 

注:SYS_JOURNAL_14340相当于中间表,之所以online不影响正常DML,就是因为他用了中间表,类似物化视图的机制,对中间表进行的操作,

 

从10046的trace(BISAL_ora_53117.trc,大小371K)来看,发现使用online方式创建索引,一共SELECT数据字典共计54次,INSERT了12次,UPDATE了5次,DELETE了46次,总共对数据字典,执行117次操作,才完成了创建索引的工作,相比非online,onilne方式创建索引,做了更多的操作,

grep 'select ' BISAL_ora_53117.trc | wc -l
54

grep 'insert ' BISAL_ora_53117.trc | wc -l
12

grep 'update ' BISAL_ora_53117.trc | wc -l
5

grep 'delete ' BISAL_ora_53117.trc | wc -l
46

 

其中所有DML操作的调用栈,如下所示,第一步操作是LOCK TABLE,模式ROW SHARE,接下来创建了中间表,并创建唯一索引,以及各种DML数据字典的操作,

LOCK TABLE 'TBL' IN ROW SHARE MODE
create table 'BISAL'.'SYS
update obj$
update con$
CREATE UNIQUE INDEX 'BISAL'.'SYS_IOT_TOP_14344' on 'BISAL'.'SYS_JOURNAL_14343'('C0','RID') INDEX ONLY TOPLEVEL TABLESPACE 'TB' STORAGE( BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) NOPARALLEL
insert into seg$
insert into icol$
insert into ind$
insert into tab$
insert into col$
insert into ccol$
insert into cdef$
insert into icol$
insert into ind_online$
update seg$
delete from ind_stats$ where obj#=:1
update ind$
UPDATE ind_online$ SET flags=:2 WHERE obj#=:1
insert into sys.wri$_optstat_ind_history
delete from ind_stats$ where obj#=:1
delete from object_usage
delete from sys.cache_stats_1$
delete com$
delete from hist_head$
delete from compression$
DELETE FROM sys.sumdelta$
DELETE FROM sys.sumpartlog$
DELETE FROM sys.snap_loadertime$
BEGIN aw_drop_proc(ora_dict_obj_type, ora_dict_obj_name, ora_dict_obj_owner);
delete from ind_online$
delete from ind_stats$
delete from idl_ub1$
delete from idl_ub2$
delete from idl_sb4$
delete from ncomp_dll$
delete from idl_char$
delete from idl_ub2$
delete from idl_sb4$
delete from ncomp_dll$
delete from idl_ub1$
delete from idl_char$
delete from idl_ub2$
delete from idl_sb4$
delete from ncomp_dll$
delete from objauth$
delete from col$
delete from icol$
delete from icoldep$
delete from jijoin$
delete from jirefreshsql$
delete from ccol$
delete from ind$
delete from cdef$
delete ecol$  -- 语句缺少from关键字
delete from tab$
delete coltype$  -- 语句缺少from关键字
delete from subcoltype$
delete ntab$
delete lob$  -- 语句缺少from关键字
delete from opqtype$
delete from cdef$
delete from objauth$
delete from obj$
update seg$
delete from con$
delete from seg$

 

总结来讲,

1. 非online方式创建索引,不能存在正在执行的DML,否则create index就会抛出ORA-00054。如果非online创建索引过程中,有正在执行的DML,则这些操作会hang,直到create index执行完成。换句话说,对于频繁DML的表,因为并发操作,有可能一直无法创建索引,也有可能创建索引的过程中,影响其他DML操作。

2. online方式创建的索引,不能存在正在执行的DML,否则create index ... online不会抛出错误,但会一直等待,直到所有DML完成。如果使用online创建索引过程中,有正在执行的DML,则不会影响。换句话说,对于频繁DML的表,因为并发操作,有可能一直无法创建索引,但是在创建索引的过程中,不会影响其他DML操作。

3. 无论online还是非online,如果创建的时候,有其他的DML操作,则都不会成功,不同的是,online是等待,非online是抛出错误。

4. 对于非online,创建索引过程中,会阻碍其他的DML,对于online,创建过程中,不会阻碍其他DML。

5. 相对来说,online比非online对现有DML的影响较小,但是二者,都存在无法创建的可能。

6. 针对无法创建的可能,一种方法,就是暂停业务,暂停所有DML操作,这样可以顺利地创建完成,此时选择非online,相比使用online,可以更快创建

转:http://www.360doc.com/content/18/0920/08/13123233_788134288.shtml

  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Oracle中,可以使用以下步骤来创建索引: 1. 使用"create index"语句创建索引,指定索引名和表名,并在括号内列出要在索引中包含的列名。例如: "create index 索引名 on 表名(列名);" 2. 可以选择指定表空间来存储索引。可以使用"online tablespace"或"tablespace"子句来声明表空间。例如: "create index 索引名 on 表名(列名) online tablespace 空间名;"或"create index 索引名 on 表名(列名) tablespace 表空间名;" 3. 若要删除索引,可以使用"drop index"语句,后面跟上索引名。例如: "drop index 索引名;" 4. 如果希望创建组合索引,可以在"create index"语句中列出多个列名。例如: "create index 索引名 on 表名(列名1, 列名2);" 5. 若要创建唯一索引,可以在"create index"语句中加上"unique"关键字。例如: "create unique index 索引名 on 表名(列名);" 6. 若要创建反向键索引,可以在"create index"语句中加上"reverse"关键字。例如: "create index 索引名 on 表名(列名) reverse;" 7. 若要创建位图索引,可以使用"create bitmap index"语句。例如: "create bitmap index 索引名 on 表名(列名);" 8. 若要创建基于函数的索引,可以在"create index"语句中使用函数名作为列名。例如: "create index 索引名 on 表名(函数名) tablespace 表空间名;" 另外,可以使用以下语句查询索引相关信息: - 根据索引名查询表索引字段: "select * from user_ind_columns where index_name='索引名';" - 根据表名查询一张表的索引: "select * from user_indexes where table_name='表名';" 需要注意的是,在MySQL中,创建索引的语法略有不同。可以使用"ALTER TABLE"语句来创建和删除索引。例如: - 创建索引: "ALTER TABLE 表名 ADD INDEX 索引名(列名);" - 删除索引: "ALTER TABLE 表名 DROP INDEX 索引名;" 在决定是否创建索引时,需要权衡该字段是否经常发生增删改操作。因为索引的维护过程会导致增删改的速度变慢。所以,如果该字段经常变动,可能会对性能产生负面影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值