CREATE TABLE ... CLUSTER PRIMARY KEY
)。PK_WITH_CLUSTER=1
时,主键自动转为聚集索引。CREATE TABLE t1 (id INT, content CLOB)
STORAGE(LOB ON lob_ts); -- LOB存储在lob_ts表空间
ALTER TABLE t1 MOVE LOB ON new_lob_ts; -- 迁移LOB列到新表空间
特性 | 堆表 | B树表 | 分区表 |
---|---|---|---|
存储结构 | 扁平B树+物理ROWID | B+树+逻辑ROWID | 子表(堆表/B树表)+分区键 |
插入性能 | ⭐⭐⭐⭐(高并发) | ⭐⭐(需维护索引有序) | 依赖子表类型 |
范围查询效率 | ⭐(需全扫描) | ⭐⭐⭐⭐(有序访问) | 依赖分区剪枝+子表类型 |
LOB分离存储 | ❌ 不支持 | ❌ 不支持 | ✅ 支持 |
适用场景 | 高频插入日志类表 | 事务处理表(OLTP) | 海量数据表、历史数据管理 |
首先需要设置同一种表结构,设置id、name、create_time、grade、city不同列,不同的表对象通过STORAGE()来修改。
id INT PRIMARY KEY, name VARCHAR(50), create_time DATETIME, grade INT, city VARCHAR(20)
对于不同的表来说使用level来按照id对于每个表循环插入100万条数据。
INSERT INTO table_name (id, name, create_time, grade, city)
SELECT
LEVEL AS id,
'User_' || LEVEL AS name,
SYSDATE - MOD(LEVEL, 365) AS create_time, -- 随机日期
MOD(LEVEL, 100) AS grade, -- 成绩0-99
DECODE(MOD(LEVEL, 4), 0,'北京', 1,'上海', 2,'广州', 3,'深圳') AS city
FROM DUAL
CONNECT BY LEVEL <= 1000000; -- 生成10万行[1,6](@ref)
对B树表、堆表在插入100万行数据时的性能监控报告横向对比分析。基于SQL Monitoring Report数据,结合存储结构特性,以下是关键性能指标对比表:
指标 | B树表 | 堆表 |
---|---|---|
总耗时 | 9.55s | 6.96s |
INSERT2 | 82.11% (7.84s) | 75.39% (5.24s) |
逻辑读(页) | 3,082,811 | 2,992,861 |
物理读(页) | 1,000,000 | 1,000,000 |
数据生成耗时 | 1.44s (15.11%) | 1.45s (20.84%) |
执行次数 | 3,336次 | 3,336次 |
INSERT2操作耗时差异
存储结构 | 数据组织方式 | 写入特点 | 适用场景 |
---|---|---|---|
B树表 | 主键有序存储(索引组织表) | 需维护B+树结构,页分裂代价高 | 高频率按主键查询的场景 |
堆表 | 无序存储(堆组织表) | 插入快,但查询需额外索引支持 | 频繁插入且较少范围查询的场景 |
对于不同的表都使用同一句sql查询,查询id=500000的情况
SELECT * FROM table_name WHERE id = 500000;
性能指标 | B树表 | 堆表 |
---|---|---|
总耗时 | 0.286ms | 0.148ms |
逻辑读(页) | 3 | 4 |
物理读(页) | 0 | 0 |
执行计划关键操作 | CSEK2 (索引扫描) |
SSEK2+BLKUP2 (索引扫描+回表) |
扫描行数 | 1 | 1 |
耗时最高操作 | CSEK2 0.2101ms |
NSET2 0.0548ms |
CSEK2
(唯一索引扫描)SSEK2
(索引扫描)→ BLKUP2
(回表)CSCN2
(全表扫描)city
或create_time
,查询id=500000
无法触发分区裁剪,需扫描所有分区。id
创建全局索引,优化器退化为全表扫描PLL
操作耗时0.000044s)。结构特性 | B树表 | 堆表 |
---|---|---|
数据物理组织 | 主键有序存储 | 无序堆存储 |
主键查询路径 | 索引树直达数据 | 索引→ROWID→数据页 |
非分区键查询代价 | 低(索引高效) | 中(需回表) |
适用场景 | 高频主键查询 | 写密集型+随机写入 |
特性 | 堆表(Heap-Organized) | 索引组织表(IOT) |
---|---|---|
分区策略支持 | 全部(Range/List/Hash/复合分区) | 仅基础分区(Range/List/Hash) |
分区键与主键关系 | 无需关联 | 分区键必须是主键的子集 |
组合分区支持 | ✅ Range-Hash,Range-List等 | ✅ 支持Range-Hash,List-Range |
数据物理存储 | 无序堆 | 按主键有序的B树结构 |
典型适用场景 | 写密集型、无顺序要求 | 读密集型、强主键查询场景 |
比较指标 | 分区堆表 (partitioned_heap) | 分区B树表 (partitioned_iot) | 差异/分析 |
---|---|---|---|
表类型 | 堆表 | B树表(索引组织表) | B树表需维护索引结构,增加开销。 |
SQL ID | 553 | 562 | - |
执行持续时间 | 2.484秒 | 9.975秒 | 堆表快4.01倍:B树表索引维护导致延迟。 |
受影响行数 | 1,000,000 | 1,000,000 | 相同。 |
物理读 (页) | 1,000,000 | 1,000,000 | 相同:数据生成阶段(CONNECT BY )类似。 |
逻辑读 (页) | 253,413 | 2,994,505 | B树表高11.8倍:索引节点访问频繁(B树表需维护排序和分区索引)。 |
INSERT操作时间 | 0.7409s | 8.2543s | B树表高11.14倍:INSERT在B树表中是主要瓶颈(索引维护)。 |
投影操作时间(PRJT2) | 1.4759s | 1.455s | 两者的投影操作时间相近(因为DECODE(MOD(LEVEL,4)) )操作相同。 |
数据生成效率 | 0.0032s | 0.0039s | 在100万条的数据插入里面两者的数据生成效率相近。 |
主要瓶颈 | 投影计算(PRJT2) | INSERT操作 | B树表索引维护使其不适合高频插入;堆表更适合批量写入。 |
APPEND
提示使用了直接路径插入(Direct Path Insert),减少了空间分配开销。CONNECT BY
生成数据),但操作权重不同:以下场景是在非分区键查询场景下:
指标 | 分区IOT表 (partitioned_iot ) |
分区堆表 (partitioned_heap ) |
差异原因分析 |
---|---|---|---|
逻辑读(Logical Read) | 12页 | 10页 | IOT表需额外访问主键索引结构,增加逻辑读。 |
实际扫描行数 | 1500行 → 返回96行 | 1200行 → 返回74行 | IOT表数据存储更分散,需扫描更多行才能满足grade>90 条件。 |
执行时间(Duration) | 0.516ms | 0.395ms | IOT表额外索引访问和分散存储导致耗时略高。 |
CSCN2操作 | 0.3045ms | 0.2113ms | IOT表扫描阶段消耗更多资源。 |
过滤效率 | 1500行输入 → 96行输出 (6.4%) | 1200行输入 → 74行输出 (6.2%) | 两者过滤效率接近,但IOT需扫描更多数据。 |
CSCN2
扫描索引结构(INDEX33555506
),需访问更多逻辑页定位数据。CSCN2
直接顺序扫描物理数据页(INDEX33555496
),逻辑读更少。grade
是非分区键,高分数据分散存储(如主键1和主键10000的grade>90
记录可能位于不同索引页),导致扫描更多行。CSCN2
操作耗时0.3045ms(堆表为0.2113ms),说明IOT的索引扫描成本更高。PARALLEL
并行扫描,但IOT表并行执行效率更低(PLL
操作占比7.49% vs 堆表9.03%),因并行任务需协调分散的索引结构。DBMS_STATS.GATHER_TABLE_STATS
。文章
阅读量
获赞