【Oracle数据库指南】第35篇:Oracle特殊对象——簇与索引组织表(IOT)
上一篇【第34篇】Oracle索引管理与优化详解
下一篇【第36篇】Oracle用户与权限管理详解(完整版)(明日更新,敬请期待)
摘要
除了普通堆组织表(Heap-Organized Table)之外,Oracle还提供了两种特殊的表存储结构:**簇(Cluster)**将频繁一起查询的多张表的相关行存储在同一数据块中,消除连接操作的物理I/O;**索引组织表(IOT,Index-Organized Table)**将行数据直接存储在B树索引结构中,适合主键访问的应用场景。本文详细讲解这两种结构的原理、创建方式、适用场景与注意事项。
一、簇(Cluster)概述
1.1 什么是簇
簇是一种将相关表的相关行物理存储在同一数据块的存储结构。当两张表经常通过某个公共列进行连接查询时,簇能将两表中相同连接键值对应的行存放在一起,从而减少连接操作的I/O次数。
普通表的连接问题:
查询:SELECT e.ename, d.dname FROM emp e JOIN dept d ON e.deptno = d.deptno 普通表: EMP 表块:Block 1 (empno=7369,deptno=20) | Block 2 (empno=7499,deptno=30) | ... DEPT 表块:Block 10(deptno=20,RESEARCH) | Block 11(deptno=30,SALES) | ... → 查询时需要访问 EMP 的块,再访问 DEPT 的块,至少两次块读取簇的解决方案:
簇:按 deptno 将两表行存储在一起 Block 1:DEPT deptno=20 + 所有 deptno=20 的 EMP 行 Block 2:DEPT deptno=30 + 所有 deptno=30 的 EMP 行 → 连接查询只需读取一次块即可获取两表数据1.2 簇的类型
| 类型 | 说明 |
|---|---|
| 索引簇(Index Cluster) | 用B树索引管理簇键,适合等值查询 |
| 哈希簇(Hash Cluster) | 用哈希函数定位行,等值查询最快 |
二、索引簇(Index Cluster)
2.1 创建索引簇
-- 步骤1:创建簇(指定簇键列和每个键值对应的块大小)CREATECLUSTER scott.emp_dept_cluster(deptno NUMBER(2)-- 簇键列(连接键))SIZE600-- 每个簇键值预计占用的字节数TABLESPACEusers_data;-- 步骤2:为簇创建索引(必须先创建索引,才能插入数据!)CREATEINDEXscott.idx_emp_dept_clusterONCLUSTER scott.emp_dept_cluster;-- 步骤3:在簇中创建 DEPT 表CREATETABLEscott.dept_clustered(deptno NUMBER(2),dname VARCHAR2(14),loc VARCHAR2(13))CLUSTER scott.emp_dept_cluster(deptno);-- 指定簇键-- 步骤4:在簇中创建 EMP 表CREATETABLEscott.emp_clustered(empno NUMBER(4),ename VARCHAR2(10),job VARCHAR2(9),sal NUMBER(7,2),deptno NUMBER(2))CLUSTER scott.emp_dept_cluster(deptno);-- 同一个簇键-- 步骤5:先插入DEPT数据,再插入EMP数据INSERTINTOscott.dept_clusteredSELECT*FROMscott.dept;INSERTINTOscott.emp_clusteredSELECTempno,ename,job,sal,deptnoFROMscott.emp;COMMIT;2.2 使用簇查询
-- 连接查询(簇表的连接性能最优)SELECTe.empno,e.ename,e.job,d.dnameFROMscott.emp_clustered eJOINscott.dept_clustered dONe.deptno=d.deptnoWHEREe.deptno=20;-- Oracle 可能使用 CLUSTER 访问路径,比普通表连接少很多 I/O2.3 删除簇
-- 删除簇(必须先删除簇中的表)DROPTABLEscott.emp_clustered;DROPTABLEscott.dept_clustered;DROPCLUSTER scott.emp_dept_cluster;-- 或者一步删除(CASCADE CONSTRAINTS 同时删除外键约束)DROPCLUSTER scott.emp_dept_cluster INCLUDINGTABLESCASCADECONSTRAINTS;三、哈希簇(Hash Cluster)
3.1 哈希簇原理
哈希簇用哈希函数直接计算出行所在的数据块,查询时无需读索引,直接计算目标块地址,实现一次I/O读取(最理想情况)。
3.2 创建哈希簇
-- 创建哈希簇CREATECLUSTER scott.orders_hash_cluster(order_id NUMBER(10)-- 哈希键列)HASHKEYS100000-- 预计的不同键值数量(影响哈希桶数)SIZE300-- 每个哈希键值预计字节数HASHISorder_id-- 可选:直接用键值做哈希(键值必须为正整数)TABLESPACEusers_data;-- 在哈希簇中创建表(不需要额外建索引!)CREATETABLEscott.orders_hashed(order_id NUMBER(10),customer_id NUMBER(6),order_dateDATE,amount NUMBER(10,2))CLUSTER scott.orders_hash_cluster(order_id);3.3 哈希簇的适用场景
| 适合哈希簇 | 不适合哈希簇 |
|---|---|
主键等值查询(WHERE order_id = 10086) | 范围查询(WHERE order_id > 1000) |
| 键值数量稳定,不大幅增长 | 键值数量动态增长(导致冲突增加) |
| 高频率单行读取 | 全表扫描(比普通表更差) |
四、索引组织表(IOT)
4.1 什么是IOT
普通表(堆表)中,数据行存储在数据段中,索引存储在独立的索引段中,通过 ROWID 关联。
IOT 不同:将整行数据存储在B树主键索引的叶节点中,没有独立的数据段。
普通堆表(Heap Table): 数据段:(7369, 'SMITH', 800, ...) ← 无序存储 索引段:empno=7369 → ROWID:AAABcc... ← 需要两次访问 IOT(Index-Organized Table): 主键索引叶节点:empno=7369 + 'SMITH' + 800 + ... ← 按主键顺序存储,一次访问4.2 创建IOT
-- 创建IOT(必须指定主键,主键是IOT的组织键)CREATETABLEscott.zip_codes(zip_code VARCHAR2(10)PRIMARYKEY,city VARCHAR2(50)NOTNULL,province VARCHAR2(50),latitude NUMBER(8,5),longitude NUMBER(8,5))ORGANIZATIONINDEX-- 关键语法:指定为IOTTABLESPACEusers_data PCTTHRESHOLD50-- 行数据超过块大小50%时,溢出到溢出段INCLUDING longitude-- 从此列开始的列放入溢出段(可选)OVERFLOWTABLESPACEusers_data;-- 溢出段表空间4.3 IOT 的特点
优点:
- 主键查询只需一次I/O(无需回表)
- 行按主键顺序存储,范围查询高效
- 不需要独立的主键索引(减少存储空间)
缺点:
- 非主键列的查询不如普通表(无额外索引时需全表扫描)
- 行较宽时会有溢出段,增加管理复杂度
- 不能直接获取 ROWID(有 UROWID,但不同于普通 ROWID)
- DML 操作时需要维护 B 树结构,代价较高
4.4 IOT的适用场景
- 字典/代码表:数据量小、以主键查询为主(如邮编、国家代码)
- 历史数据:只按主键查询、很少更新
- 数据同步目标:以主键为唯一标识的参考数据表
4.5 在IOT上创建二级索引
-- 为IOT创建二级索引(提高非主键列的查询性能)CREATEINDEXscott.idx_zip_cityONscott.zip_codes(city)TABLESPACEusers_index;-- 注意:IOT的二级索引的行地址是逻辑ROWID,包含主键猜测值五、查看特殊对象的数据字典
-- 查看所有簇SELECTcluster_name,cluster_type,hashkeys,functionFROMdba_clustersWHEREowner='SCOTT';-- 查看簇中包含的表SELECTtable_name,cluster_nameFROMdba_tablesWHEREowner='SCOTT'ANDcluster_nameISNOTNULL;-- 查看IOTSELECTtable_name,iot_typeFROMdba_tablesWHEREowner='SCOTT'ANDiot_type='IOT';-- 查看IOT溢出段SELECTtable_name,iot_typeFROMdba_tablesWHEREowner='SCOTT'ANDiot_type='IOT_OVERFLOW';六、普通表 vs 簇 vs IOT 对比
| 特性 | 普通堆表 | 索引簇 | 哈希簇 | IOT |
|---|---|---|---|---|
| 主键查询 | 两次I/O | 两次I/O | 一次I/O(最优) | 一次I/O |
| 范围查询 | 索引+回表 | 索引扫描 | 不支持(全扫) | 直接范围扫描 |
| 多表连接 | 两次I/O | 一次I/O(最优) | — | — |
| DML性能 | 高 | 中 | 中 | 低(B树维护) |
| 全表扫描 | 高 | 中 | 差 | 中 |
| 适用场景 | 通用 | 频繁连接的关联表 | 高频等值查询 | 主键查询为主 |
七、最佳实践
- 簇适合极少变化的主从关系表:如 DEPT/EMP 这类数据量不大、连接频繁的表
- 哈希簇适合高频等值主键查询:事先预估好键值数量(HASHKEYS),过小会产生碰撞
- IOT 适合小型字典/代码表:行宽不超过一个块的30%-40%最佳,避免频繁溢出
- 大多数场景使用普通堆表:最灵活,DML 性能最好,配合合理索引已足够
- 评估收益再使用:特殊结构虽有针对性优势,但管理复杂度和限制也更多
八、总结
Oracle 特殊对象的核心要点:
- 索引簇:多表共享同一物理块,消除连接 I/O,适合稳定的关联表
- 哈希簇:等值主键查询一次 I/O,需预估键值数量
- IOT:行数据存储在主键B树叶节点,主键查询无回表
- 选型依据:查询模式是主键等值、范围查询、多表连接,还是通用CRUD
- 大多数场景:普通堆表 + 合理索引是最优选择
上一篇【第34篇】Oracle索引管理与优化详解
下一篇【第36篇】Oracle用户与权限管理详解(完整版)(明日更新,敬请期待)
参考资料
- 《Oracle 11g数据库管理员指南》— 刘宪军著
- Oracle官方文档:Database Administrator’s Guide - Managing Clusters
- Oracle官方文档:Database Concepts - Index-Organized Tables
