微观世界的层级之美:深度解析Oracle数据块、区(Extent)与段(Segment)的层级关系
- 一、开篇显微镜:Oracle是如何存下一张表的?
- 二、全景层级图:从表空间到数据块的四层架构
- 三、核心概念逐层拆解:从底层到顶层
- 1.数据块(Data Block):Oracle的“原子单位”
- 2.区(Extent):空间分配的“最小合同单位”
- 3.段(Segment):数据对象的“物理化身”
- 四、三层结构的完整关系链:一张EMP表的诞生
- 场景:创建EMP表并插入数据
- 五、块、区、段的五大核心区别
- 六、自动段空间管理(ASSM)与传统管理的区别
- 1.传统空间管理(Manual Segment Space Management)
- 2.自动段空间管理(ASSM - Automatic Segment Space Management)
- 七、常见问题与最佳实践
- 1.问题一:表空间空间足够,但创建表失败?
- 2.问题二:表有大量空闲空间,但查询很慢?
- 3.问题三:如何预估一个表需要多少空间?
- 八、总结:记住这个“书”的类比就够了
🌺The Begin🌺点点关注,收藏不迷路🌺 ⬇ ⬇ 底部 ⬇ ⬇ |
一、开篇显微镜:Oracle是如何存下一张表的?
当你执行CREATE TABLE emp (...)时,Oracle并不是简单地把数据丢进一个文件里,而是按照一套极其精密的“三层存储结构”,将数据层层封装,最终落盘到数据文件中。
这套三层结构就是:
段(Segment)→ 区(Extent)→ 块(Block)
如果说表空间是“书架”,数据文件是“书架的隔层”,那么段就是“一本书”,区是“一章”,块是“一页纸”。今天这篇文章,带你从“一本书”的视角,彻底拆解Oracle存储结构的微观世界。
二、全景层级图:从表空间到数据块的四层架构
先通过一张架构图,看清表空间、段、区、块之间的完整层级关系:
关键解读:
- 蓝色(数据库):顶层容器,一切的开端。
- 橘色(表空间):逻辑分区,段的家园。
- 红色(段):数据对象的空间占用实体。
- 绿色(区):段分配空间的最小单位。
- 紫色(块):Oracle I/O的最小单位。
三、核心概念逐层拆解:从底层到顶层
1.数据块(Data Block):Oracle的“原子单位”
精确描述:
数据块是Oracle数据库中最小的存储单位和I/O单位。无论你查询一行数据还是百万行数据,Oracle都是从磁盘读取整个块到内存,不会只读一行。
数据块的核心参数:
| 参数 | 说明 | 典型值 |
|---|---|---|
| 块大小(DB_BLOCK_SIZE) | 标准块大小,数据库创建时指定 | 8KB(OLTP)/ 32KB(OLAP) |
| 块头(Block Header) | 存储块地址、块类型、事务信息 | 约100字节 |
| 表目录(Table Directory) | 存储该块中有数据的表的信息 | 动态变化 |
| 行目录(Row Directory) | 存储该块中每行的偏移地址 | 动态变化 |
| 空闲空间(Free Space) | 可用于插入新行或更新行 | 动态变化 |
| 行数据(Row Data) | 实际存储的表行数据 | 数据的主体 |
数据块的空间管理参数:
-- 查看数据库块大小SHOWPARAMETER db_block_size;-- 查看表的块空间使用情况SELECTtable_name,blocks,empty_blocks,avg_spaceFROMdba_tablesWHEREtable_name='EMP';PCTFREE与PCTUSED:控制块空间使用的两个关键参数
- PCTFREE:预留的空间百分比,用于行更新时扩展。默认10%。
- PCTUSED:块使用率低于此值时,重新允许插入。默认40%。
块是Oracle一切操作的基础,锁的最小粒度是行,但读的最小粒度是块。
2.区(Extent):空间分配的“最小合同单位”
精确描述:
区是Oracle为段分配空间的最小单位,由连续的数据块组成。当一个段需要更多空间时,Oracle不会逐个块地分配,而是按区分配,一次给一组连续的块。
区的核心特征:
- 连续性:区内的所有块在数据文件中是物理连续的。
- 大小可变:区的初始大小和后续大小可以不同。
- 按需增长:段空间用完时,自动分配新区。
区的分配策略:
-- 创建表时指定区的分配策略CREATETABLEemp(emp_id NUMBER,name VARCHAR2(100))STORAGE(INITIAL64K-- 初始区大小NEXT64K-- 下一个区大小MINEXTENTS1-- 最少区数MAXEXTENTS100-- 最多区数PCTINCREASE0-- 区大小增长率);查看对象的区分配情况:
-- 查看EMP表的区分配SELECTextent_id,file_id,block_id,blocks,bytes/1024ASsize_kbFROMdba_extentsWHEREsegment_name='EMP'ANDowner='SCOTT'ORDERBYextent_id;区的分配示例:
| 区编号 | 起始块 | 块数 | 大小 | 何时分配 |
|---|---|---|---|---|
| Extent 0 | Block 100 | 8 | 64KB | 创建表时 |
| Extent 1 | Block 256 | 8 | 64KB | 前8个块用完后 |
| Extent 2 | Block 512 | 128 | 1MB | 数据量增大后 |
区是Oracle管理空间分配的“批处理”机制,减少了逐个块分配的开销。
3.段(Segment):数据对象的“物理化身”
精确描述:
段是Oracle中占用存储空间的数据库对象。每一个有实际数据的表、索引、物化视图、簇等,都对应一个段。段是区的集合,区是块的集合。
段的核心特征:
- 对象级实体:一个表 = 一个段,一个索引 = 一个段。
- 空间持有者:段是真正占用数据文件空间的实体。
- 不可跨表空间:一个段的所有区必须在同一个表空间内。
常见段类型:
| 序号 | 段类型 | 对应对象 | 说明 |
|---|---|---|---|
| 1 | TABLE | 普通表、分区表 | 存放表的数据行 |
| 2 | INDEX | B树索引、位图索引 | 存放索引的键值和ROWID |
| 3 | UNDO | 回滚段 | 存放修改前的旧值,用于回滚和读一致性 |
| 4 | TEMPORARY | 临时段 | 排序、哈希连接产生的临时空间 |
| 5 | LOB | 大对象列 | 存放CLOB、BLOB的大数据 |
查看段信息:
-- 查看SCOTT用户下所有段SELECTsegment_name,segment_type,tablespace_name,blocks,bytes/1024/1024ASsize_mbFROMdba_segmentsWHEREowner='SCOTT'ORDERBYbytesDESC;四、三层结构的完整关系链:一张EMP表的诞生
让我们通过一个完整的实例,追踪一张表从创建到数据增长,是如何使用三层存储结构的:
场景:创建EMP表并插入数据
步骤一:创建表(生成段)
CREATETABLEemp(emp_id NUMBERPRIMARYKEY,name VARCHAR2(100),salary NUMBER)TABLESPACEusers;发生了什么:
- 在USERS表空间中创建一个名为EMP的段(SEGMENT)。
- 段类型为TABLE。
- 分配第一个区(Extent 0),包含8个连续块(每个8KB)。
- 此时段总大小 = 64KB。
步骤二:插入少量数据(使用第一个区)
INSERTINTOempVALUES(1,'张三',5000);INSERTINTOempVALUES(2,'李四',6000);发生了什么:
- 数据写入Extent 0的第一个块(Block 1)。
- 每个块8KB,一行数据约100字节,一个块可存约80行。
- 此时段仍只占用1个区,64KB。
步骤三:大量插入数据(触发新区分配)
-- 插入10万行数据BEGINFORiIN1..100000LOOPINSERTINTOempVALUES(i,'员工'||i,5000+i);ENDLOOP;COMMIT;END;发生了什么:
- Extent 0的8个块很快被填满。
- Oracle自动分配Extent 1(又8个块,64KB)。
- 数据继续增长,分配Extent 2(可能升级到128个块,1MB)。
- 随着数据量增大,后续区的块数越来越多。
- EMP段现在由多个区组成,每个区由连续块组成。
步骤四:创建索引(生成另一个段)
CREATEINDEXidx_emp_nameONemp(name)TABLESPACEusers;发生了什么:
- 在USERS表空间中创建另一个名为IDX_EMP_NAME的段(SEGMENT)。
- 段类型为INDEX。
- 这个段也有自己的区链和块链。
- EMP表和IDX_EMP_NAME索引是两个独立的段,各自管理空间。
五、块、区、段的五大核心区别
| 序号 | 对比维度 | 数据块(Block) | 区(Extent) | 段(Segment) |
|---|---|---|---|---|
| 1 | 层级定位 | 最底层,原子单位 | 中间层,空间分配单位 | 最顶层,数据对象实体 |
| 2 | 大小 | 固定(通常8KB) | 可变(64KB~几百MB) | 可变(几KB~几TB) |
| 3 | 连续性 | 单个块不涉及连续 | 区内所有块必须连续 | 段内各区可以不连续 |
| 4 | 对应关系 | 1个区 = N个连续块 | 1个段 = N个不连续的区 | 1个对象 = 1个段(通常) |
| 5 | 分配时机 | 写入数据时使用 | 段空间不足时按需分配 | 创建对象时立即分配第一个区 |
六、自动段空间管理(ASSM)与传统管理的区别
1.传统空间管理(Manual Segment Space Management)
特征:
- 使用PCTFREE、PCTUSED、FREELISTS参数手动管理。
- 通过空闲列表(Freelist)跟踪可用的块。
- 高并发插入时,Freelist争用成为性能瓶颈。
2.自动段空间管理(ASSM - Automatic Segment Space Management)
特征:
- 使用位图(Bitmap)跟踪块的使用状态。
- 无需PCTUSED、FREELISTS参数。
- 并发插入性能大幅提升,是Oracle 10g后的默认方式。
创建ASSM表空间:
CREATETABLESPACEassm_tbs DATAFILE'/u01/oradata/ORCL/assm_tbs01.dbf'SIZE100M EXTENT MANAGEMENTLOCALAUTOALLOCATE SEGMENT SPACE MANAGEMENT AUTO;-- 关键:AUTO表示使用ASSM查看表空间是否使用ASSM:
SELECTtablespace_name,segment_space_managementFROMdba_tablespaces;七、常见问题与最佳实践
1.问题一:表空间空间足够,但创建表失败?
原因:段的第一个区无法分配足够的连续块。
解决:整理表空间碎片,合并空闲空间。
-- 合并空闲空间ALTERTABLESPACEusersCOALESCE;-- 查看表空间的碎片情况SELECTtablespace_name,COUNT(*)ASfragments,SUM(bytes)/1024/1024ASfree_mbFROMdba_free_spaceGROUPBYtablespace_name;2.问题二:表有大量空闲空间,但查询很慢?
原因:大量DELETE操作导致块内碎片,高水位线未下降。
解决:重组表,降低高水位线。
-- 方式一:移动表ALTERTABLEemp MOVE;-- 方式二:收缩表(Oracle 10g+)ALTERTABLEempENABLEROWMOVEMENT;ALTERTABLEemp SHRINK SPACECASCADE;-- 重建索引(MOVE后索引失效)ALTERINDEXidx_emp_name REBUILD;3.问题三:如何预估一个表需要多少空间?
估算公式:
表占用空间 = 行数 × 平均行长度 × (1 + PCTFREE/100) 段占用空间 = 表占用空间 + 索引占用空间 所需块数 = CEIL(段占用空间 / (块大小 × 可用空间比例)) 所需区数 = 根据区的分配策略动态分配-- 查看表的平均行长SELECTtable_name,avg_row_len,num_rows,blocksFROMdba_tablesWHEREtable_name='EMP';八、总结:记住这个“书”的类比就够了
【段是一本书,区是一章,块是一页】
- 段(Segment)- 一本书:一个表就是一本完整的书,包含所有内容。
- 区(Extent)- 一章:书的内容分章节,每一章由连续的页码组成。
- 块(Block)- 一页:每一页有固定大小,是阅读和翻页的最小单位。
当书的内容增多(插入数据),作者会申请新的纸张(分配新区)。每张纸可以写固定字数的内容(每个块可存固定数量的行)。如果书的内容被大量删除(DELETE),纸张会出现空白(碎片),需要通过收缩(SHRINK)来释放多余的纸张。
理解了段、区、块的三层结构,你就掌握了Oracle存储管理的微观世界。下次当有人问你“表空间里到底存了什么”,你可以自信地回答:表空间存的是段,段由区组成,区由块组成,块里面才是真正的一行行数据。
你在日常运维中,是否遇到过因为段碎片导致的空间浪费?或者因为区分配策略不当导致的性能问题?欢迎在评论区分享你的实战经验。
🌺The End🌺点点关注,收藏不迷路🌺 ⬆ ⬆ 顶部 ⬆ ⬆ |