问题现象
Oracle 11.2.0.4中用DBMS_SPACE.SPACE_USAGE和dba_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_USAGE 和 DBA_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 的正常空间管理机制,旨在减少频繁的空间分配操作,提高性能。