日期:2014-05-16  浏览次数:20507 次

如何对线上数据表创建唯一键或者主键约束
由于oracle中主键和唯一键的执行是通过唯一索引来保证的,在增加主键或者唯一键约束的时候需要建立相关的索引。因此,在线创建拥有大批量数据的表的约束的时候,会不会出现阻塞呢?来实验一下。
-- sid=147,先看看表T有没有已经可以使用的索引
SQL> select index_name,UNIQUENESS,table_name from user_indexes where table_name='T';
no rows selected

-- sid=143
SQL> select count(*) from t;
  COUNT(*)
----------
   1061469

SQL> alter table t
  2  add constraint uk_t_owner unique (owner,object_name,subobject_name,object_id,data_object_id,object_type)
  3  enable novalidate;
add constraint uk_t_owner unique (owner,object_name,subobject_name,object_id,data_object_id,object_type)
               *
ERROR at line 2:
ORA-02299: cannot validate (SCOTT.UK_T_OWNER) - duplicate keys found

-- sid=159,几乎是在同一个时间里面来执行下面这个句子
SQL> select sid,type,lmode,request from v$lock where sid=143;
       SID TY      LMODE    REQUEST
---------- -- ---------- ----------
       143 DL          3          0
       143 DL          3          0
       143 TM          4          0
       143 TM          3          0
       143 TX          6          0

-- sid=147,143session返回错误以后,执行下面这个句子,看看有没有索引建立
SQL> select index_name,UNIQUENESS,table_name from user_indexes where table_name='T';
no rows selected
从上面的这个实验,可以看出:
1)尽管我使用了ENABLE NOVALIDATE来创建唯一约束,但是由于表本来没有一个索引来给这个约束使用,是创建不成功的。原因就是,oracle由于没有发现现在表中没有可以使用的索引,因此它要自动来创建一个唯一索引给唯一约束使用,但是由于表中存在重复数据,因此就创建唯一索引不成功,于是返回错误。
2)在创建唯一约束的时候,oracle会锁住表,对表所加的锁包括DL锁,这个锁的作用是Direct loader parallel index create,因此可以知道它是在并行创建索引,这也证实了这个说法:当你要建立唯一约束和主键约束时,如果没有索引可以使用,oracle会自动创建相关索引。oracle还会对表加S TM锁和RX TM锁,这两个锁联合出现,导致在其执行期间,其他人除了能对表加RS TM锁以外,不能加任何其他的TM锁。最主要的是,请看例子中的红色字体,oracle对表T的数据加了一个X锁,很显然,如果是一个具有大量数据的在线的表,这会导致其他会话阻塞。
       上面是在没有索引的情况下去建立唯一约束的,会导致以上诸多问题。换一种思路,如果已经存在索引,再去建立唯一约束,会不会可能导致其他会话阻塞现象呢?继续做实验,很显然,我的表中有重复数据,因此需要建立一个非唯一约束(其实这也是一种方法,虽然oracle的主键和唯一键约束底层要有唯一索引,但是我们可以只给它一个非唯一索引,只要这个表中的数据不重复就可以了,因为oracle在判断的时候,还是要通过查询索引有没有重复值来达到是不是满足唯一的要求)。
-- sid=143
SQL> create index uk_t_owner
  2  on t(owner,object_name,subobject_name,object_id,data_object_id,object_type) ;
Index created.

-- sid=159
SQL> select sid,type,lmode,request from v$lock where sid=143;
       SID TY      LMODE    REQUEST
---------- -- ---------- ----------
       143 DL          3          0
       143 DL          3          0
       143 TM          4          0
       143 TM          3          0
       143 TX   &nbs