Oracle的DBMS_SPACE.SPACE_USAGE和dba_segments计算的对象块数为什么不一样?表空间异常暴增的秘密可能就在这里!

问题现象

Oracle 11.2.0.4中用DBMS_SPACE.SPACE_USAGEdba_segments查某个对象的blocks,为什么不一样呢?

你有想过为什么吗?

场景再现

sql 复制代码
Oracle 11.2.0.4中用下面两种方法查询:
查询1-使用DBMS_SPACE.SPACE_USAGE:
SET SQLBLANKLINES ON
SET SERVEROUTPUT ON

DECLARE
    v_unformatted_blocks NUMBER;
    v_unformatted_bytes  NUMBER;
    v_fs1_blocks         NUMBER;
    v_fs1_bytes          NUMBER;
    v_fs2_blocks         NUMBER;
    v_fs2_bytes          NUMBER;
    v_fs3_blocks         NUMBER;
    v_fs3_bytes          NUMBER;
    v_fs4_blocks         NUMBER;
    v_fs4_bytes          NUMBER;
    v_full_blocks        NUMBER;
    v_full_bytes         NUMBER;
BEGIN
    DBMS_SPACE.SPACE_USAGE(
        segment_owner      => 'WEWIN',
        segment_name       => 'IDX_ID_TEST1',
        segment_type       => 'INDEX',
        unformatted_blocks => v_unformatted_blocks,
        unformatted_bytes  => v_unformatted_bytes,
        fs1_blocks         => v_fs1_blocks,
        fs1_bytes          => v_fs1_bytes,
        fs2_blocks         => v_fs2_blocks,
        fs2_bytes          => v_fs2_bytes,
        fs3_blocks         => v_fs3_blocks,
        fs3_bytes          => v_fs3_bytes,
        fs4_blocks         => v_fs4_blocks,
        fs4_bytes          => v_fs4_bytes,
        full_blocks        => v_full_blocks,
        full_bytes         => v_full_bytes
    );

    DBMS_OUTPUT.PUT_LINE('Unformatted Blocks = ' || v_unformatted_blocks);
    DBMS_OUTPUT.PUT_LINE('FS1 Blocks = ' || v_fs1_blocks);
    DBMS_OUTPUT.PUT_LINE('FS2 Blocks = ' || v_fs2_blocks);
    DBMS_OUTPUT.PUT_LINE('FS3 Blocks = ' || v_fs3_blocks);
    DBMS_OUTPUT.PUT_LINE('FS4 Blocks = ' || v_fs4_blocks);
    DBMS_OUTPUT.PUT_LINE('Full Blocks = ' || v_full_blocks);
END;
/
查询结果:
Unformatted Blocks = 0
FS1 Blocks = 0
FS2 Blocks = 1
FS3 Blocks = 0
FS4 Blocks = 0
Full Blocks = 21

如果Unformatted Blocks 的值很大,可能出现空间泄露。

查询2-使用dba_segments:
select SEGMENT_NAME,OWNER,BLOCKS from dba_segments where segment_name='IDX_ID_TEST1';
SEGMENT_NAME     OWNER  BLOCKS                                         
---------------  ----- ---------
IDX_ID_TB0101_3  WEWIN  32

浅析

DBMS_SPACE.SPACE_USAGEDBA_SEGMENTS.BLOCKS 统计的是不同的内容

1. DBA_SEGMENTS.BLOCKS (32块)

  • 这是段分配的总块数
  • 包括所有分配给该段的存储空间,无论是否使用
  • 包含了段头块(Segment Header block)、扩展块等管理结构
  • 高水位线以下的块(HWM以下,已格式化或未格式化)
  • 高水位线以上的块(HWM以上,从未使用)
官方依据

根据文档《How to Determine Real Space used by a Table (Below the High Water Mark) (Doc ID 77635.1)》:

"DBA_SEGMENTS.BLOCKS holds the total number of blocks allocated to the table."

2. DBMS_SPACE.SPACE_USAGE 统计结果 (22块)

  • 这统计的是已格式化的数据块中实际用于存储索引数据的部分
  • 只包含HWM(高水位线)以下的已格式化块
官方依据

根据文档《Oracle 11.2 Concepts》描述:

"Every data block in an ASSM segment is in one of the following states: ■ Above the HWM - These blocks are unformatted and have never been used. ■ Below the HWM - These blocks are in one of the following states..."

3.具体的差异分析

从结果可以看出:

复制代码
SPACE_USAGE统计:
- FS2 Blocks = 1      (空闲25-50%的块)
- Full Blocks = 21    (完全使用的块)
总计:22块

DBA_SEGMENTS统计:
- BLOCKS = 32         (总分配块数)

差异:32 - 22 = 10块

这10块的差异通常由以下几部分组成:

4.差异的组成

1) 段头块(Segment Header)
  • 每个段至少有一个段头块
  • 用于存储段的存储段的元数据、extent map、freelist信息
2) 未格式化块(Unformatted Blocks)
  • 虽然结果显示 Unformatted Blocks = 0,但在某些情况下可能有少量未格式化的块
3) HWM以上的块(Above High Water Mark)可能8-9个块
  • 这是最主要的原因
  • 当段扩展时,Oracle 会一次性分配多个块
  • 这些块已分配但尚未格式化使用
  • 它们不计入 SPACE_USAGE 的统计
4) ASSM 位图块(对于自动段空间管理)
  • 如果表空间使用 ASSM,会有额外的位图块用于空间管理

5.验证差异原因

你可以通过以下查询进一步验证:

sql 复制代码
-- 1. 查看段的空间使用详情(包括未使用空间)
SET SERVEROUTPUT ON
DECLARE
    v_total_blocks NUMBER;
    v_total_bytes NUMBER;
    v_unused_blocks NUMBER;
    v_unused_bytes NUMBER;
    v_last_used_extent_file_id NUMBER;
    v_last_used_extent_block_id NUMBER;
    v_last_used_block NUMBER;
BEGIN
    DBMS_SPACE.UNUSED_SPACE(
        segment_owner => 'WEWIN',
        segment_name => 'IDX_ID_TEST1',
        segment_type => 'INDEX',
        total_blocks => v_total_blocks,
        total_bytes => v_total_bytes,
        unused_blocks => v_unused_blocks,
        unused_bytes => v_unused_bytes,
        last_used_extent_file_id => v_last_used_extent_file_id,
        last_used_extent_block_id => v_last_used_extent_block_id,
        last_used_block => v_last_used_block
    );
    
    DBMS_OUTPUT.PUT_LINE('Total Blocks = ' || v_total_blocks);
    DBMS_OUTPUT.PUT_LINE('Unused Blocks = ' || v_unused_blocks);
    DBMS_OUTPUT.PUT_LINE('Last Used Block = ' || v_last_used_block);
END;
/
Total Blocks = 32
Unused Blocks = 6
Last Used Block = 2

空间使用示意图

sql 复制代码
分配给索引IDX_ID_TB0101_3的总空间(32块):
├── 段头块:1块(管理段元数据)
├── 高水位线以下的块:22块(实际存储索引数据)
│   ├── FS1 Blocks: 0块(0-25%空闲)
│   ├── FS2 Blocks: 1块(25-50%空闲)
│   ├── FS3 Blocks: 0块(50-75%空闲)
│   ├── FS4 Blocks: 0块(75-100%空闲)
│   └── Full Blocks: 21块(100%满)
└── 高水位线以上的块:9块(从未使用)

实际应用建议

1) 空间优化:这个索引有 1/32 ≈ 3% 的空闲空间,空间利用率较高

2) 监控指标 :关注 Full Blocks 的增长情况,如果接近总块数,可能需要考虑重建索引

3)重建时机

sql 复制代码
-- 计算空间利用率
SELECT 
    (22 / 32) * 100 as used_percent,  -- 约68.75%
    (1 / 32) * 100 as free_percent    -- 约3.13%
FROM dual;

通常建议在空间利用率低于50%时考虑重建

5. 总结

这种差异是正常的,反映了:

  • DBA_SEGMENTS.BLOCKS = 总分配空间
  • SPACE_USAGE统计 = 实际使用的格式化空间

索引目前:

  • 总分配:32块
  • 实际使用:22块(其中21块满,1块半满)
  • 剩余10块是段管理结构和未使用的预分配空间

这是 Oracle 的正常空间管理机制,旨在减少频繁的空间分配操作,提高性能。

相关推荐
DBA小马哥9 小时前
Oracle迁移实战:如何轻松跨越异构数据库的学习与技术壁垒
数据库·学习·oracle·信创·国产化平替
hjjdebug12 小时前
ffmpeg -map 是什么意思?
ffmpeg·map
Luna-player16 小时前
在javaweb项目中,在表中的数据中什么是一对一,一对多,多对多
数据库·oracle
我科绝伦(Huanhuan Zhou)19 小时前
Oracle数据库内存管理实操指南:PGA与SGA优化实战
数据库·oracle
黑客思维者20 小时前
Python自动化截图/录屏3大方案(PIL/mss/ffmpeg)深度拆解
python·ffmpeg·自动化·录屏
雪球不会消失了21 小时前
MySQL(开发篇)
数据库·mysql·oracle
xuanloyer1 天前
oracle从入门到精通--启动与关闭数据库实例
数据库·oracle
一点晖光1 天前
ffmpeg处理视频命令整理
ffmpeg·音视频
询问QQ688238861 天前
西门子博图 1200PLC 大型项目程序:开启自动化编程新征程
dba