当前位置: 首页 > news >正文

Oracle 数据布局探秘:段与区块的内部机制

前言

在 Oracle 数据库的庞大架构中,数据存储的效率与性能是决定整个系统健康状况的关键因素。Oracle 采用了一套精妙的逻辑存储管理体系来组织和分配数据,其中,“段(Segment)”和“区(Extent)”是两个核心的逻辑单元。

它们直接关系到数据如何被物理地写入和读取,以及存储空间的利用效率。理解并掌握 Oracle 的段与区管理方式,对于数据库管理员优化性能、规划容量、避免空间问题至关重要。

本文将深入探讨 Oracle 的段管理机制,特别是围绕段和区这两个概念,解析传统管理方式的痛点以及现代自动化管理方法的优势,旨在为您揭示 Oracle 存储管理的奥秘。

一、段空间管理方式

1、自动管理方式(ASSM(Auto Segment Space Management))

简单说就是采用位图管理方式,每个段的段头都有一组位图,位图描述每个块的满度,根据满度的不同将每个块登记到相应的位图上,位图自动跟踪每个块的使用空间,该位图不是精确的,而是按 25%的区间来分: 0-25%的空闲空间、 25%-50%的空闲空间、50%-75%的空闲空间和 75%-100%的空闲空间。比如块大小为 8k,你要插入一行是 4k 的表行,那么 oracle 就给你在满度 50%的位图上找个登记的块。ASSM 的前提是 EXTENT MANAGEMENT LOCAL,在 ORACLE9I 以后,缺省状态为自动管理方式, ASSM 废弃 pctused 属性。

2、手工管理方式(MSSM(Manual Segment Space Management))

这是传统的方法,采用 FREELIST 空闲列表管理段,现在仍然在使用,未被淘汰,保留 pctfree 和 pctused 属性,这些概念后面介绍 block 时再讨论。

二、段和表的关系

一个单纯的表就分配一个段,但往往表没那么单纯,比如表上经常会有主键约束,那么就会有索引,索引有索引段,还有分区表,每个分区会有独立的段,再有就是 oracle 的大对象, 如果你的表里引用 blob,clob,那么这个表就又被分出多个段来。

2.1、查看表与段的关系

--创建用户并授权
SYS@pdb> create user u1 identified by u1;User created.SYS@pdb> grant connect,resource to u1;Grant succeeded.SYS@pdb> grant select any dictionary to u1;Grant succeeded.2、创建表
U1@pdb>  create table t1(id number);Table created.3、查看段分配情况
U1@pdb> select segment_name,segment_type,segment_subtype,tablespace_name,bytes,blocks,extents from user_segments; no rows selected

这时候看到在查询段信息的时候, Oracle 没有分配段,原因是在19C 中采用了默认延迟段的创建方式。也就是说,在创建表的时候,暂时先不分配段,待插入第一条数据的时候再进行创建。 

我们对表插入一条数据,再次查询段的情况:

U1@pdb> insert into t1 values(1);1 row created.U1@pdb> commitU1@pdb> select segment_name,segment_type,segment_subtype,tablespace_name,bytes,blocks,extents from user_segments; SEGMENT_NAME                   SEGMENT_TYPE       SEGMENT_SU TABLESPACE_NAME                     BYTES     BLOCKS    EXTENTS
------------------------------ ------------------ ---------- ------------------------------ ---------- ---------- ----------
T1                             TABLE              ASSM       USERS                               65536          8          1

可以看到,T1表分配一个1个区。

插入更多的数据

--sys用户执行
SYS@pdb>  insert into u1.t1 select object_id from dba_objects where rownum<=10000;10000 rows created.--u1用户查看
U1@pdb> select segment_name,segment_type,segment_subtype,tablespace_name,bytes,blocks,extents from user_segments where segment_name='T1';SEGMENT_NAME                   SEGMENT_TYPE       SEGMENT_SU TABLESPACE_NAME                     BYTES     BLOCKS    EXTENTS
------------------------------ ------------------ ---------- ------------------------------ ---------- ---------- ----------
T1                             TABLE              ASSM       USERS                              196608         24          3

可以看到,随着数据插入,T1表分配一个3个区。

另外,在建表时候可以带上 SEGMENT CREATION IMMEIDATE 选项,可以不使用默认延迟段的创建方式,例如:

U1@pdb> create table t2(id number) segment creation immediate;Table created.U1@pdb> select segment_name,segment_type,segment_subtype,tablespace_name,bytes,blocks,extents from user_segments where segment_name='T2'; SEGMENT_NAME                   SEGMENT_TYPE       SEGMENT_SU TABLESPACE_NAME                     BYTES     BLOCKS    EXTENTS
------------------------------ ------------------ ---------- ------------------------------ ---------- ---------- ----------
T2                             TABLE              ASSM       USERS                               65536          8          1

延迟段的方式是由参数 deferred_segment_creation 来控制,默认值为 TRUE。也可通过更改此参数实现禁用延迟段。该参数可以在会话级别修改,也可以在 spfile 修改,下次启动生效。

SYS@pdb> show parameter deferred_segment_creationNAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
deferred_segment_creation            boolean     TRUE--session级别修改deferred_segment_creation参数
SQL> alter session set deferred_segment_creation=false;U1@pdb2> create table t3(id number) ;Table created.U1@pdb2> select segment_name,segment_type,segment_subtype,tablespace_name,bytes,blocks,extents from user_segments; SEGMENT_NAME                   SEGMENT_TYPE       SEGMENT_SU
------------------------------ ------------------ ----------
TABLESPACE_NAME                     BYTES     BLOCKS    EXTENTS
------------------------------ ---------- ---------- ----------
T1                             TABLE              ASSM
USERS                              196608         24          3T2                             TABLE              ASSM
USERS                               65536          8          1T3                             TABLE              ASSM
USERS                               65536          8          1

创建带主键的表,观察段的分配情况:

U1@pdb> create table t3(id number primary key) segment creation immediate;Table created.U1@pdb>  select * from user_segments;这里可以看到表和主键上的索引都会分配单独的段

创建带大对象字段的表,观察段的分配情况:

U1@pdb> create table t4(id number,name clob) segment creation immediate;Table created.U1@pdb> select * from user_segments;
这里可以看到上面的语句,产生了段类型为TABLE及段类型为LOBSEGMENT的对象

2.2 延迟段分配在索引上的使用

--创建索引时指定不分配空间
CREATE INDEX IDX_T1 ON T1(ID) UNUSABLE;--查看索引段分配情况
select segment_name,segment_type,segment_subtype,tablespace_name,bytes,blocks,extents from user_segments; 
发现索引未分配空间--将索引重建ALTER INDEX idx_t1 REBUILD;--再次查看索引段分配情况
select segment_name,segment_type,segment_subtype,tablespace_name,bytes,blocks,extents from user_segments; 
发现索引分配空间--将索引分配的空间回收
ALTER INDEX idx_t1 unusable;

三、区 extent

区是 ORACLE 进行存储空间分配的最小单位,是由一系列逻辑上连续的 Oracle 数据块组成的逻辑存储结构。段中第一个区叫初始区,随后分配的区叫后续区。

 管理方式有两种:基于字典的管理(已淘汰)和基于本地管理。

3.1 表和区(extent)的关系

当建立表的时候建立段(segment),然后自动分配相应的 extent(1 个或者多个)

select * from dba_extents t where t.owner='U1' and t.segment_name='T1';

可以手工 allocate extent 提前分配 extent(用于需大量插入数据的表)。

1、u1用户下创建表t5,设置立即创建段
U1@pdb> create table u1.t5(id number) segment creation immediate;Table created.2、查看t5表分配的区
U1@pdb> select * from user_extents where segment_name='T5';
SEGMENT_NAME                   PARTITION_NAME                                                                                     SEGMENT_TYPE        TABLESPACE_NAME                 EXTENT_ID      BYTES     BLOCKS
------------------------------ -------------------------------------------------------------------------------------------------------------------------------- ------------------ ------------------------------ ---------- ---------- ----------
T5                                                                                                                                TABLE               USERS                                   0      65536          83、sys用户插入数据
SYS@pdb> insert into u1.t5 select object_id from dba_objects where rownum<=10000;
SYS@pdb> COMMIT;Commit complete.4、再次查看t5表分配的区
U1@pdb> select * from user_extents where segment_name='T5';
SEGMENT_NAME                   PARTITION_NAME                                                                                     SEGMENT_TYPE        TABLESPACE_NAME                 EXTENT_ID      BYTES     BLOCKS
------------------------------ -------------------------------------------------------------------------------------------------------------------------------- ------------------ ------------------------------ ---------- ---------- ----------
T5                                                                                                                                TABLE               USERS                                   0      65536          8
T5                                                                                                                                TABLE               USERS                                   1      65536          8
T5                                                                                                                                TABLE               USERS                                   2      65536          8

可以看到段T5的初始区 ID 为 0,大小为 65536 bytes, 向表段中插入数据,观察Oracle 为该段分配更多的区(extent) 。

3.2 区和表数据的关系

将表 T5 数据全部删除,观察区的情况:

1、删除前,查看一下全表扫描T5所需要的块数
Execution Plan
----------------------------------------------------------
Plan hash value: 1231860717-------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     7   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |            |          |
|   2 |   TABLE ACCESS FULL| T5   | 10000 |     7   (0)| 00:00:01 |
-------------------------------------------------------------------Note
------ dynamic statistics used: dynamic sampling (level=2)Statistics
----------------------------------------------------------0  recursive calls0  db block gets23  consistent gets0  physical reads0  redo size550  bytes sent via SQL*Net to client388  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)1  rows processedSYS@pdb>  delete from u1.t5;10000 rows deleted.SYS@pdb> commit;Commit complete.3、再次查看执行计划,仍然扫描和删除前一样的blocks
SYS@pdb> set autot trace
SYS@pdb> select count(1) from u1.t5;Execution Plan
----------------------------------------------------------
Plan hash value: 1231860717-------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     7   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |            |          |
|   2 |   TABLE ACCESS FULL| T5   | 10000 |     7   (0)| 00:00:01 |
-------------------------------------------------------------------Note
------ dynamic statistics used: dynamic sampling (level=2)Statistics
----------------------------------------------------------0  recursive calls0  db block gets23  consistent gets0  physical reads0  redo size549  bytes sent via SQL*Net to client388  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)1  rows processed4、查看T5表区的信息
SYS@pdb> select segment_name,file_id,extent_id,blocks,bytes from dba_extents where segment_name='T5';SEGMENT_NAME                      FILE_ID  EXTENT_ID     BLOCKS      BYTES
------------------------------ ---------- ---------- ---------- ----------
T5                                     12          0          8      65536
T5                                     12          1          8      65536
T5                                     12          2          8      65536

此时表T5段的数据已经删除,但所有 extent 依然健在,无法回收 T5段的所有区。

也可以要求一个预分配的所需空间(但要注意,所需要的空间一定是在表空间可达到的空间范围内)

 alter table u1.t5 allocate extent(datafile '/opt/oracle/oradata/ORCLCDB/ORCLPDB1/users01.dbf' size 8m);col SEGMENT_NAME for a30select segment_name,file_id,extent_id,bytes from dba_extents where segment_name='T5';SEGMENT_NAME                      FILE_ID  EXTENT_ID      BYTES
------------------------------ ---------- ---------- ----------
T5                                     12          0      65536
T5                                     12          1      65536
T5                                     12          2      65536
T5                                     12          3    1048576
T5                                     12          4    1048576
T5                                     12          5    1048576
T5                                     12          6    1048576
T5                                     12          7    1048576
T5                                     12          8    1048576
T5                                     12          9    1048576
T5                                     12         10    104857611 rows selected.

回收未使用的分区用 deallocate, 注意:只能收回从未使用的 extent

SYS@pdb> alter table u1.t5 deallocate unused;Table altered.SYS@pdb>  select segment_name,file_id,extent_id,bytes from dba_extents where segment_name='T5';SEGMENT_NAME                      FILE_ID  EXTENT_ID      BYTES
------------------------------ ---------- ---------- ----------
T5                                     12          0      65536
T5                                     12          1      65536
T5                                     12          2      65536

思考:为什么delete无法释放已经使用的区?如何释放这些实际上未使用的区?

提示:高水位线和shrink,关于这两个知识点,后面我们再详细介绍。

四、数据块 block

block是数据库内 I/O 最小单位,数据库需要读取数据时总是无法只读取一条记录,而是必须读整个数据块,只有在创建表空间时就能指定数据块容量,无法修改数据块容量。

BLOCK 的管理方法是区的管理和段管理的具体体现

自动管理方式(ASSM) :如创建表空间时区(extent)为本地管理方式,并且将段(segment)的存储空间方式设置为 AUTO(即 ASSM),该表空间的所有块均采用位图自动管理方式,这是系统默认的。

空闲列表方式(MSSM):引入 FREELIST 概念,以及 PCTFREE 和 PCTUSED 两个参数控制可用存储区的大小,避免行迁移现象的发生。这两个参数可在创建表空间时设置,也可在建立数据库的模式对象(表,索引)中设置。模式对象中设置的优先级比表空间的要高。就是说;如表和索引中没有设置,则按表空间的设置,如表空间也没设置,则按自动管理方式管理块。

引用一下ORACLE官方文档中块的格式

 

4.1 数据块的管理方式

freelist:空闲列表中登记了可以插入数据的可用块,位置在段头,插入表行数据时首先查找该列表。

pctfree:用来为一个块保留的空间百分比,以防止在今后的更新操作中增加一列或多列值的长度。达到该值,从 freelist 清除该块信息。

pctused:一个块的使用水位的百分比,这个水位将使该块返回到可用列表中去等待更多的插入操作。达到该值,该块信息加入 freelist,这个参数在 ASSM 下不使用。

查看表的默认定义

-- Create table
create table ROW_MIG_DEMO
(x INTEGER not null,a CHAR(1000),b CHAR(1000),c CHAR(1000),d CHAR(1000),e CHAR(1000)
)
tablespace USERSpctfree 10initrans 1maxtrans 255storage(initial 64Knext 1Mminextents 1maxextents unlimited);

4.2 数据块的pctfree 和 pctused

接下来我们更加详细的了解下 pctfree 和 pctused

pctfree 这个参数定义了一个块保留空间的百分比,保留空间是为了将来可能发生的更新操作,因为更新可能增大被更新行占用的空间,如果此时该块没有可利用空间,那么只有发生 row migrate 了,从而会降低 I/O 性能。

换句话说,就是当一个块的利用率达到pctfree 的时候, oracle 就将该块从 freelist 中移除,不再向该块插入数据。所以说 pctfree是控制什么时候将块从 freelist 中移除的。

示例: 

CREATE TABLE test_table(n NUMBER) PCTFREE 20;

pctused 这个参数控制一个块什么时候被重新启用来插入数据,例如当一个块达到 pctfree 利用率的时候, oracle 停止向该块插入数据,同时从 freelist 移除该块,但是后来发生一些删除操作,使得该块的利用率下降,当该块的利用率降到 pctused 以下的时候该块就被重新启用来插入数据了,也即是将该块重新加入到 freelist 列表中,所以说 pctused是控制什么时候将一个块重新加入 freelist 的。

举个例子:比如一个块的 pctfree 定为 10%, pctused 定为 40%(oracle 默认设置)。那么一个块的使用率达到 90%(即 1-10%)的时候, oracle 将该块从 freelist 中移除,停止使用该块来插入数据(但可更新)。

后来该块上发生了一些删除操作,使得该块的利用率下降,当使用率下降到 40%以下的时候, oracle 重新将该块加入 freelist,可用于新的插入。

在 Oracle11g 中,表空间默认使用本地位图自动管理, PCTFREE 的默认值是 10,且无法自定义管理 PCTUSED 属性。

结语

通过本文的探讨,我们深入了解了 Oracle 数据库中段和区这两个核心逻辑存储单元的角色及其管理方式。从早期依赖手动参数的复杂时代,到如今广泛采用的自动化段空间管理(ASSM)和本地管理区(LME),Oracle 的存储管理机制经历了显著的演进,旨在提供更高效、更简便、更少出错的管理体验。

特别是 ASSM 基于位图的精细化空间管理和 LME 避免数据字典争用的优势,使其成为现代 Oracle 数据库存储的最佳实践。正确地配置和利用 ASSM 和 LME(尤其是 AUTOALLOCATE 模式),不仅能极大地简化数据库管理员的工作,更能有效提升数据库的整体性能、优化空间利用率、减少碎片,从而确保数据库系统的稳定运行和持续高效。

掌握这些知识,是每一位 Oracle 数据库从业者不可或缺的技能。

#数据库 #oracle #段区块 #逻辑结构

相关文章:

  • MCP 规范新版本特性全景解析与落地实践
  • 二叉查找树,平衡二叉树(AVL),b树,b+树,红黑树
  • 41.防静电的系列措施
  • CTK的插件框架和QTUI集成方法
  • C++回顾 Day5
  • upload-labs靶场通关详解:第二关
  • 代码随想录算法训练营第60期第二十九天打卡
  • 超越 DeepSeek-R1,英伟达新模型登顶
  • 在cursor中使用MCP插件生成旅行规划到桌面的执行步骤分析
  • 统计匹配的二元组个数 - 华为OD机试真题(A卷、JavaScript题解)
  • 破解逆向专辑(一)
  • Qt界面设计时窗口中各控件布局及自适应方法
  • 如何用FastMCP快速开发自己的MCP Server?
  • 云硬盘的原理
  • 分布式-Redis分布式锁
  • 从零开始学习three.js(15):一文详解three.js中的纹理映射UV
  • SimpleMindMap:一个支持AI的思维导图软件
  • UGUI如何使用EventTrigger
  • AI Workflow
  • 数据中心机电建设
  • 时代中国控股:前4个月销售额18.1亿元,境外债重组协议押后聆讯至5月底
  • 构建菌株有效降解有机污染物,上海交大科研成果登上《自然》
  • 国办印发《关于进一步加强困境儿童福利保障工作的意见》
  • “上海之帆”巡展在日本大阪开幕,松江区组织企业集体出展
  • 习近平同俄罗斯总统普京举行会谈
  • 毗邻三市人均GDP全部超过20万元,苏锡常是怎样做到的?