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

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 #段区块 #逻辑结构

http://www.xdnf.cn/news/4599.html

相关文章:

  • 制作一款打飞机游戏40:点选敌人
  • 编写程序,统计两会政府工作报告热词频率,并生成词云
  • 【icpc陕西省赛】树的最大深度+基环树找环的大小
  • 注意力(Attention)机制详解(附代码)
  • LeetCode205_同构字符串
  • NHANES指标推荐:AISI
  • QML 动态加载组件
  • 论文解析[13] MIXED TRANSFORMER U-NET FOR MEDICAL IMAGE SEGMENTATION
  • 【C++】C++11(上)
  • RDD的处理过程
  • vue3的新特性
  • Spring cloud loadBalancer 负载均衡
  • Qwen2-VL详解
  • Unity3D 游戏内存优化策略
  • Anchor-based 和 Anchor-free
  • 修改图像分辨率
  • SLAM:单应矩阵,本质矩阵,基本矩阵详解和对应的c++实现
  • AtCoder 第404场初级竞赛 A~E题解
  • 【无标题】云计算运维
  • 代码随想录算法训练营第60期第二十九天打卡
  • 前端代码规范详细配置
  • CSS手动布局
  • 60页PDF | 四川电信数据湖 + 数据中台实施方案:覆盖数据能力、数据资产及数据治理的全流程建设指南
  • 从xjtu-sy数据集中看轴承故障的发展趋势与基本特征
  • 南京大学OpenHarmony技术俱乐部正式揭牌 仓颉编程语言引领生态创新
  • 5. HTML 转义字符:在网页中正确显示特殊符号
  • Linux系列:如何用perf跟踪.NET程序的mmap泄露
  • 水印落幕 7.0 | 专门用于去除图片和视频中水印的工具,支持自定义水印添加
  • 【测试开发】BUG篇 - 从理解BUG到如何处理
  • 递归element-ui el-menu 实现无限级子菜单