聚集索引
1.聚集索引简介
表(列存储表和堆表除外)都是使用 B+树(以下简称 B 树)索引结构管理的,每一个普通表都有一个聚集索引,数据通过聚集索引键排序,根据聚集索引键可以快速查询任何记录。(即表是一个索引,这个索引名称叫聚集索引,可以理解为创建一个表后,将所有字段放在一起建立一个复合索引,只不过这个不需要我们来创建,系统自动给我们维护了一个)
当建表语句未指定聚集索引键,DM 的默认聚集索引键是ROWID,即记录默认以 ROWID在页面中排序。ROWID 是 B 树为记录生成的逻辑递增序号,表上不同记录的 ROWID 是不一样的,并且最新插入的记录 ROWID 最大。很多情况下,以 ROWID 建的默认聚集索引并不能提高查询速度,因为实际情况下很少人根据 ROWID来查找数据。
验证默认聚集索引键是ROWID
2.数据准备
create table USERS (id number(12),name varchar2(20) not null,age int not null); insert into USERS values(0001,'Tony',20); insert into USERS values(0002,'Bill',21); insert into USERS values(0003,'Jack',18); insert into USERS values(0004,'Mark',20); insert into USERS values(0005,'Lily',24); insert into USERS values(0006,'Kobe',28); insert into USERS values(0007,'James',29); insert into USERS values(0008,'Henry',30); insert into USERS values(0009,'Lisa',20); insert into USERS values(0010,'Mila',18); commit; |
查询此表上的索引
select id,owner,name,index_type,table_name,table_type from SYSOBJECTS ob join DBA_INDEXES idx on ob.name=idx.index_name where idx.owner='SYSDBA' and idx.table_name='USERS'; |
可以看到有生成了一个名为INDEX33555473的索引,索引类型是CLUSTER。
查看索引列:
select t1.owner,t1.index_name,t3.object_id,t1.index_type,t1.uniqueness,t2.table_name,t2.column_name from dba_indexes t1 inner join user_ind_columns t2 on t1.index_name=t2.index_name inner join dba_objects t3 on t1.index_name = t3.object_name where t1.owner='SYSDBA'; |
查询结果为空,表示该索引不是表中的字段列,所以可以怀疑索引列是rowid。
查看该索引的定义,此索引在客户端中无法查看到。
select dbms_metadata.get_ddl(OBJECT_TYPE => 'INDEX',NAME=>upper('INDEX33555473'),SCHNAME => 'SYSDBA'); CREATE CLUSTER INDEX "INDEX33555473" ON "SYSDBA"."USERS"( STORAGE(ON "MAIN", CLUSTERBTR) ; |
可以看到,该索引的定义里面也没有指定索引列。
查询
select * from users where id=2; |
从执行计划可以看到该查询走了聚集扫描(CLUSTER SCAN)。从这里也能看出来,INDEX33555473存储了USERS表的全部数据。
select rowid,* from users where rowid=2; |
可以看到走的是聚集索引扫描(CLUSTER INDEX SEEK)。所以就可以验证聚集索引是以rowid为索引键的。
另外默认以rowid为索引键的聚集索引无法删除。
drop index INDEX33555473; |
重建聚集索引后,该默认聚集索引则会自动删除。
create cluster index idx_users_id on users(id); |
select id,owner,name,index_type,table_name,table_type from SYSOBJECTS ob join DBA_INDEXES idx on ob.name=idx.index_name where idx.owner='SYSDBA' and idx.table_name='USERS'; |
可以看到默认的聚集索引已经不存在。
这时聚集索引是可以删除的,删除后原先的聚集索引会自动恢复。
drop index idx_users_id; select id,owner,name,index_type,table_name,table_type from SYSOBJECTS ob join DBA_INDEXES idx on ob.name=idx.index_name where idx.owner='SYSDBA' and idx.table_name='USERS';
|
但是聚集索引的名字变了,由原来的INDEX33555473变成了INDEX33555475。
DM 建表时提供三种方式供用户指定聚集索引键:
1 | CLUSTER PRIMARY KEY | 指定列为聚集索引键,并同时指定为主键,称为聚簇主键 |
2 | CLUSTER KEY | 指定列为聚集索引键,但是是非唯一的 |
3 | CLUSTER UNIQUE KEY | 指定列为聚集索引键,并且是唯一的 |
第一种方式受PK_WITH_CLUSTER参数影响,该参数为1才生效,参数功能如下:
3.PK_WITH_CLUSTER
PK_WITH_CLUSTER | 1 | 动态,会话级 | 在建表语句中指定主关键字时,是否缺省指定为 CLUSTER,0:不指定;1:指定 注:该参数对列存储表和堆表无效 |
第一种方式在PK_WITH_CLUSTER=1时CLUSTER可以不写。如果指定CLUSTER关键字,无论PK_WITH_CLUSTER是0还是1都会创建聚集索引。
drop table USERS; SP_SET_PARA_VALUE(1,'PK_WITH_CLUSTER',1); create table USERS ( id number(12) primary key, name varchar2(20) not null, age int not null); select t1.owner,t1.index_name,t3.object_id,t1.index_type,t1.uniqueness,t2.table_name,t2.column_name from dba_indexes t1 inner join user_ind_columns t2 on t1.index_name=t2.index_name inner join dba_objects t3 on t1.index_name = t3.object_name where t1.owner='SYSDBA'; |
此时可以看到USERS表会有一个主键约束
--定义如下: alter table "SYSDBA"."USERS" add constraint "CONS134218775" primary key("ID"); |
删除会报如下错误:
drop table USERS; SP_SET_PARA_VALUE(1,'PK_WITH_CLUSTER',0); create table USERS ( id number(12) primary key, name varchar2(20) not null, age int not null); select t1.owner,t1.index_name,t3.object_id,t1.index_type,t1.uniqueness,t2.table_name,t2.column_name from dba_indexes t1 inner join user_ind_columns t2 on t1.index_name=t2.index_name inner join dba_objects t3 on t1.index_name = t3.object_name where t1.owner='SYSDBA'; 可以看到索引类型是NORMAL,不是CLUSTER。 select id,owner,name,index_type,table_name,table_type from SYSOBJECTS ob join DBA_INDEXES idx on ob.name=idx.index_name where idx.owner='SYSDBA' and idx.table_name='USERS'; 可以看到有两个索引。 |
drop table USERS; SP_SET_PARA_VALUE(1,'PK_WITH_CLUSTER',1); create table USERS ( id number(12) CLUSTER KEY, name varchar2(20) not null, age int not null); select t1.owner,t1.index_name,t3.object_id,t1.index_type,t1.uniqueness,t2.table_name,t2.column_name from dba_indexes t1 inner join user_ind_columns t2 on t1.index_name=t2.index_name inner join dba_objects t3 on t1.index_name = t3.object_name where t1.owner='SYSDBA'; |
可以看到NONUNIQUE。
drop table USERS; SP_SET_PARA_VALUE(1,'PK_WITH_CLUSTER',1); create table USERS ( id number(12) CLUSTER UNIQUE KEY, name varchar2(20) not null, age int not null); |
4.创建聚集索引的约束条件
1. 每张表中只允许有一个聚集索引,如果之前已经指定过 CLUSTER INDEX 或者指定了 CLUSTER PK,则用户新建立 CLUSTER INDEX 时系统会自动删除原先的聚集索引。但如果新建聚集索引时指定的创建方式(列,顺序)和之前的聚集索引一样,则会报错;
2. 指定 CLUSTER INDEX 操作需要重建表上的所有索引,包括 PK 索引;
3. 删除聚集索引时,缺省以 ROWID 排序,自动重建所有索引;
4. 若聚集索引是默认的 ROWID 索引,不允许删除;
5. 聚集索引不能应用到函数索引中;
6. 不能在列存储表上新建/删除聚集索引;
7. 建聚集索引语句不能含有 partition_clause 子句;
8. 在临时表上增删索引会使当前会话上临时 b 树数据丢失。