问题现象

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

你有想过为什么吗?

场景再现

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.验证差异原因

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

-- 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

空间使用示意图

分配给索引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)重建时机

-- 计算空间利用率
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 的正常空间管理机制,旨在减少频繁的空间分配操作,提高性能。