索引和唯一索引的区别

索引是我们经常使用的一种数据库搜索优化手段。适当的业务操作场景使用适当的索引方案可以显著的提升系统整体性能和用户体验。在Oracle中,索引有包括很多类型。不同类型的索引适应不同的系统环境和访问场景。其中,唯一性索引Unique Index是我们经常使用到的一种。

唯一性索引unique index和一般索引normal index最大的差异就是在索引列上增加了一层唯一约束。添加唯一性索引的数据列可以为空,但是只要存在数据值,就必须是唯一的。

那么,在使用唯一性索引时,同一般索引有什么差异呢?下面通过一系列的演示实验来说明。

1、实验环境准备

为了体现出一致性和可能的差异性,选择相同的数据值列加入不同类型的索引结构。

SQL> select * from v$version where rownum<2;

BANNER

--------------------------------------------------------------------------------

Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production

SQL> create table t as select * from dba_objects;

Table created

//保证data_object_id和object_id取值相同;

SQL> update t set data_object_id=object_id;

72581 rows updated

SQL> commit;

Commit complete

//普通索引

SQL> create index idx_t_normalid on t(object_id);

Index created

//唯一性索引

SQL> create unique index idx_t_uniid on t(data_object_id);

Index created

SQL> exec dbms_stats.gather_table_stats(user,‘T‘,cascade => true);

PL/SQL procedure successfully completed

2、体积容量比较

在环境准备中,我们将索引列取值设置为完全相同,尽量避免由于外在原因引起的差异。下面我们检查数据字典中的容量比较信息。

首先是查看索引段index segment信息。

SQL> select segment_name, segment_type, bytes, blocks, extents from dba_segments where segment_name in (‘IDX_T_NORMALID‘,‘IDX_T_UNIID‘);

SEGMENT_NAME         SEGMENT_TYPE         BYTES     BLOCKS  EXTENTS

-------------------- ------------------ ---------- ---------- ----------

IDX_T_NORMALID       INDEX                  983040        120         15

IDX_T_UNIID            INDEX                  917504        112         14

一般索引normal index较唯一性索引空间消耗略大。索引idx_t_normalid占据15个分区,120个block。略大于idx_t_uniid的14个分区块。

这个地方需要注意一下,在数据字典中一个segment的分区占据,是Oracle系统分配给的空间,并不意味着全部使用殆尽。可能两个索引结构差异很小,但是额外多分配出一个extent。

索引叶子结构上,检查数据字典内容。

SQL> select index_name, index_type, UNIQUENESS, BLEVEL, LEAF_BLOCKS, DISTINCT_KEYS from dba_indexes where index_name in (‘IDX_T_NORMALID‘,‘IDX_T_UNIID‘);

INDEX_NAME           INDEX_TYPE      UNIQUENESS     BLEVEL LEAF_BLOCKS DISTINCT_KEYS

-------------------- --------------- ---------- ---------- ----------- -------------

IDX_T_UNIID          NORMAL          UNIQUE              1         106         51330

IDX_T_NORMALID       NORMAL          NONUNIQUE           1         113         51330

两者的差异不大,normal index空间消耗要略大于unique index。

结论:从数据字典反映出的情况可以知道,同一般索引相比,唯一性索引的空间消耗略小一些。由于我们采用的实验数据都是相同的,这一点点的差距可能就意味着两种索引类型在存储结构上存在略微的差异。

3、违反约束实验

作为唯一性索引,在添加创建和进行dml操作的时候,都会严格发挥出约束的作用。

SQL> insert into t select * from t where rownum<2;

insert into t select * from t where rownum<2

ORA-00001: 违反唯一约束条件 (SYS.IDX_T_UNIID)

4、等号检索实验

当我们进行等号查询的时候,Oracle对两种索引生成的执行计划有何种差异?注意:下面的select检索之前,都使用flush语句对shared_pool和buffer_cache进行清理。

--精确查询

SQL> select * from t where object_id=1000;

执行计划

----------------------------------------------------------

Plan hash value: 776407697

----------------------------------------------------------------------------------------------

| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |

----------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT            |                |     1 |   101 |     2   (0)| 00:00:01 |

|   1 |  TABLE ACCESS BY INDEX ROWID| T              |     1 |   101 |     2   (0)| 00:00:01 |

|*  2 |   INDEX RANGE SCAN          | IDX_T_NORMALID |     1 |       |     1   (0)| 00:00:01 |

----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

2 - access("OBJECT_ID"=1000)

统计信息

----------------------------------------------------------

528  recursive calls

0  db block gets

87  consistent gets

11  physical reads

0  redo size

1200  bytes sent via SQL*Net to client

376  bytes received via SQL*Net from client

2  SQL*Net roundtrips to/from client

4  sorts (memory)

0  sorts (disk)

1  rows processed

SQL> select * from t where data_object_id=1000;

执行计划

----------------------------------------------------------

Plan hash value: 335537167

-------------------------------------------------------------------------------------------

| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |

-------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT            |             |     1 |   101 |     2   (0)| 00:00:01 |

|   1 |  TABLE ACCESS BY INDEX ROWID| T           |     1 |   101 |     2   (0)| 00:00:01 |

|*  2 |   INDEX UNIQUE SCAN         | IDX_T_UNIID |     1 |       |     1   (0)| 00:00:01 |

-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

2 - access("DATA_OBJECT_ID"=1000)

统计信息

----------------------------------------------------------

528  recursive calls

0  db block gets

86  consistent gets

10  physical reads

0  redo size

1200  bytes sent via SQL*Net to client

376  bytes received via SQL*Net from client

2  SQL*Net roundtrips to/from client

4  sorts (memory)

0  sorts (disk)

1  rows processed

这里,我们看到了Unique Index的一个特性,就是等号操作时执行计划的差异。对Unique Index而言,进行相等查询的结果只有一行值或者没有,所以没必要进行传统的Index Range Scan操作。此处,执行计划中使用的是Index Unique Scan操作,直接精确定位到指定的记录项目,返回rowid记录。

而一般索引在进行等号检索的时候,通常使用的就是Index Range Scan操作。沿着索引树叶子节点进行水平扫描操作,直到获取索引符合条件索引列值的rowid列表。

从成本上看,两者虽然执行计划操作方式有一定差别,但是成本实际差异不大。CPU成本和执行时间上相同。各种块读操作(逻辑读和物理读)存在一些差异,笔者认为源于两个索引结构的微量区别,这样读取的块数一定有些差异。

5、范围搜索实验

当我们进行索引列的范围搜索时,执行计划和成本有何种差异呢?

--范围匹配

SQL> select * from t where object_id>=1000 and object_id<=1500;

已选择490行。

执行计划

----------------------------------------------------------

Plan hash value: 776407697

----------------------------------------------------------------------------------------------

| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |

----------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT            |                |   485 | 48985 |    14   (0)| 00:00:01 |

|   1 |  TABLE ACCESS BY INDEX ROWID| T              |   485 | 48985 |    14   (0)| 00:00:01 |

|*  2 |   INDEX RANGE SCAN          | IDX_T_NORMALID |   485 |       |     3   (0)| 00:00:01 |

----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

2 - access("OBJECT_ID">=1000 AND "OBJECT_ID"<=1500)

统计信息

----------------------------------------------------------

528  recursive calls

0  db block gets

158  consistent gets

17  physical reads

0  redo size

23775  bytes sent via SQL*Net to client

728  bytes received via SQL*Net from client

34  SQL*Net roundtrips to/from client

4  sorts (memory)

0  sorts (disk)

490  rows processed

SQL> select * from t where data_object_id>=1000 and data_object_id<=1500;

已选择490行。

执行计划

----------------------------------------------------------

Plan hash value: 2700411221

-------------------------------------------------------------------------------------------

| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |

-------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT            |             |   485 | 48985 |    14   (0)| 00:00:01 |

|   1 |  TABLE ACCESS BY INDEX ROWID| T           |   485 | 48985 |    14   (0)| 00:00:01 |

|*  2 |   INDEX RANGE SCAN          | IDX_T_UNIID |   485 |       |     3   (0)| 00:00:01 |

-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

2 - access("DATA_OBJECT_ID">=1000 AND "DATA_OBJECT_ID"<=1500)

统计信息

----------------------------------------------------------

528  recursive calls

0  db block gets

157  consistent gets

16  physical reads

0  redo size

23775  bytes sent via SQL*Net to client

728  bytes received via SQL*Net from client

34  SQL*Net roundtrips to/from client

4  sorts (memory)

0  sorts (disk)

490  rows processed

从实验结果看,两者在进行范围搜索时,执行计划没有差异。两者都是进行Index Range Scan操作。各类型成本基本相同。

6、结论

本篇主要从应用角度,分析Unique Index与一般normal Index的差异。从结果看,Unique Index就是额外添加的唯一性约束。该约束严格的保证索引列的取值唯一性,这在一些数据列上的业务约束实现是很重要的功能。比如一个数据列,不能作为主键,而且允许为空,但是业务上要求唯一特性。这时候使用唯一性索引就是最好的选择。

从执行计划where条件中的表现看,Unique Index和一般normal Index没有显著性的差异。

两者数据基础值一样的情况下,生成索引的体积存在略微的差异,说明在存储结构上两者可能有不同。下面我们来分析一下两类型索引的结构信息。

时间: 2024-10-08 18:17:23

索引和唯一索引的区别的相关文章

Mysql主键索引、唯一索引、普通索引、全文索引、组合索引的区别

原文:Mysql主键索引.唯一索引.普通索引.全文索引.组合索引的区别 Mysql索引概念: 说说Mysql索引,看到一个很少比如:索引就好比一本书的目录,它会让你更快的找到内容,显然目录(索引)并不是越多越好,假如这本书1000页,有500也是目录,它当然效率低,目录是要占纸张的,而索引是要占磁盘空间的. Mysql索引主要有两种结构:B+树和hash. hash:hsah索引在mysql比较少用,他以把数据的索引以hash形式组织起来,因此当查找某一条记录的时候,速度非常快.当时因为是has

Mysql索引介绍及常见索引(主键索引、唯一索引、普通索引、全文索引、组合索引)的区别

Mysql各种索引区别:普通索引:最基本的索引,没有任何限制唯一索引:与"普通索引"类似,不同的就是:索引列的值必须唯一,但允许有空值.主键索引:它 是一种特殊的唯一索引,不允许有空值. 全文索引:仅可用于 MyISAM 表,针对较大的数据,生成全文索引很耗时好空间.组合索引:为了更多的提高mysql效率可建立组合索引,遵循”最左前缀“原则. Mysql索引概念:说说Mysql索引,看到一个很少比如:索引就好比一本书的目录,它会让你更快的找到内容,显然目录(索引)并不是越多越好,假如这

普通索引和唯一索引的区别、性能差异,以及其他索引简介

唯一索引和普通索引使用的结构都是B-tree,执行时间复杂度都是O(log n). 1.普通索引 普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度.因此,应该只为那些最经常出现在查询条件(WHEREcolumn=)或排序条件(ORDERBYcolumn)中的数据列创建索引.只要有可能,就应该选择一个数据最整齐.最紧凑的数据列(如一个整数类型的数据列)来创建索引. 2.唯一索引 普通索引允许被索引的数据列包含重复的值.比如说,因为人有可能同名,所以同一个姓名在同一个

普通索引和唯一索引的区别

1.概念 不同的业务场景下,应该选择普通索引,还是唯一索引? 假设你在维护一个市民系统,每个人都有一个唯一的身份证号,而且业务代码已经保证了不会写入两个重复的身份证号.如果市民系统需要按照身份证号查姓名,就会执行类似这样的SQL语句: select name from CUser where id_card = 'xxxxxxxyyyyyyzzzzz'; 所以,你一定会考虑在id_card字段上建索引. 由于身份证号字段比较大,我不建议你把身份证号当做主键,那么现在你有两个选择,要么给id_ca

Sql Server 索引之唯一索引和筛选索引

唯一索引(UNIQUE  INDEX) 当主键创建时如果不设置为聚集索引,那么就一定是唯一的非聚集索引.实际上,唯一索引,故名思议就是它要求该列上的值是唯一的.唯一索引能够保证索引键中不包含重复的值,从而使表中的每一行从某种方式上具有唯一性. 创建 UNIQUE 约束和创建与约束无关的唯一索引并没有明显的区别.进行数据验证的方式相同,而且对于唯一索引是由约束创建的还是手动创建的,查询优化器并不加以区分. 但是,创建列的 UNIQUE 约束会使索引目标更清晰. Unique Constraints

MySQL的几个概念:主键,外键,索引,唯一索引

概念: 主键(primary key) 能够唯一标识表中某一行的属性或属性组.一个表只能有一个主键,但可以有多个候选索引.主键常常与外键构成参照完整性约束,防止出现数据不一致.主键可以保证记录的唯一和主键域非空,数据库管理系统对于主键自动生成唯一索引,所以主键也是一个特殊的索引. 外键(foreign key) 是用于建立和加强两个表数据之间的链接的一列或多列.外键约束主要用来维护两个表之间数据的一致性.简言之,表的外键就是另一表的主键,外键将两表联系起来.一般情况下,要删除一张表中的主键必须首

SQL有三个类型的索引,唯一索引 不能有重复,但聚集索引,非聚集索引可以有重复

重要: (1) SQL如果创建时候,不指定类型那么默认是非聚集索引 (2) 聚集索引和非聚集索引都可以有重复记录,唯一索引不能有重复记录. (3) 主键 默认是加了唯一约束的聚集索引,但是也可以在主键创建时,指定为唯一约束的非聚集索引,因此主键仅仅是默认加了唯一约束的聚集索引,不能说主键就是加了唯一约束的聚集索引 有点拗口,可以参考我的博客:主键就是聚集索引吗? 为列创建索引实际上就是为列进行排序,以方便查询.建立一个列的索引,就相当与建立一个列的排序. 主键是唯一的,所以创建了一个主键的同时,

普通索引和唯一索引,应该怎么选择?

如果业务能保证唯一性的情况下,还是选择普通索引性能更好 select id from T where k=5 首先,我们看下 查询过程 对于普通索引来说,查询到满足条件的第一个记录后,需要查找下一个记录,直到碰到第一个不满足k=5条件的记录 对于唯一索引来说,由于索引上有唯一性,查询到第一个满足条件的记录后就停止检索了 所以在这里的区别就是普通索引会多查那么一下,那么这两种的性能差别有多大呢? 答案是微乎其微,为甚呢? 因为mysql在读数据的时候,比如说上面那条语句,如果没有在内存中,会去磁盘

09 | 普通索引和唯一索引,应该怎么选择?

今天的正文开始前,我要特意感谢一下评论区几位留下高质量留言的同学. 用户名是 @某.人 的同学,对文章的知识点做了梳理,然后提了关于事务可见性的问题,就是先启动但是后提交的事务,对数据可见性的影响.@夏日雨同学也提到了这个问题,我在置顶评论中回复了,今天的文章末尾也会再展开说明.@Justin和@倪大人两位同学提了两个好问题. 对于能够引发更深一步思考的问题,我会在回复的内容中写上“好问题”三个字,方便你搜索,你也可以去看看他们的留言. 非常感谢大家很细致地看文章,并且留下了那么多和很高质量的留

SQL存储原理及聚集索引、非聚集索引、唯一索引、主键约束的关系(补)

索引类型 1.          唯一索引:唯一索引不允许两行具有相同的索引值 2.          主键索引:为表定义一个主键将自动创建主键索引,主键索引是唯一索引的特殊类型.主键索引要求主键中的每个值是唯一的,并且不能为空 3.          聚集索引(Clustered):表中各行的物理顺序与键值的逻辑(索引)顺序相同,每个表只能有一个 4.          非聚集索引(Non-clustered):非聚集索引指定表的逻辑顺序.数据存储在一个位置,索引存储在另一个位置,索引中包含指