第一部分:官方严谨的详细阐述
一、 核心概念:为什么需要ASSM?
在传统的手动段空间管理(MSSM)中,空间管理通过空闲列表(Freelist) 完成。高并发INSERT操作会激烈争用段头块(Segment Header)中的Freelist,导致著名的 buffer busy waits 和 enq: TX - allocation 等待事件。
ASSM(Automatic Segment Space Management)的引入,旨在消除这种争用。其核心思想是:用分散的、分层级的位图结构来替代集中式的Freelist。
二、 ASSM的层级位图结构
ASSM使用一种三层的位图元数据架构来管理空间,这些位图块本身是段的一部分,存储在数据块中。
-
段头块(Segment Header Block):
- 这是段的起点,包含段的概要信息。
- 在ASSM中,它包含指向第一个二级位图块(L2 BMB) 的指针。
- 一个段只有一个段头块。
-
二级位图块(Level 2 Bitmap Block, L2 BMB):
- 作用: 不直接管理数据块,而是管理一组一级位图块(L1 BMB)。它是L1 BMB的“管理者”。
- 寻址范围: 一个L2 BMB可以管理多达数十万个数据块(具体数量因块大小和操作系统而异)。这意味着一个L2 BMB可以管理一个非常大的数据区域。
- 数量: 一个段可以有多个L2 BMB。当段不断扩展,现有的L2 BMB无法覆盖新的区间(Extent)时,Oracle会分配新的L2 BMB。段头块会记录所有L2 BMB的位置。
-
一级位图块(Level 1 Bitmap Block, L1 BMB):
- 作用: 直接管理一组数据块(Data Blocks)。每个L1 BMB管理一组连续的数据块(通常约数十个到一百多个块)。
- 内容: 对于其管理的每一个数据块,L1 BMB都用一个位图条目来记录该块的空间使用情况。这个条目不是简单的“空闲”或“已用”,而是更精细的状态:
0- Unformatted: 块尚未格式化使用。1- < 25% free space: 块中空闲空间少于25%。2- 25% - 50% free space: 块中空闲空间在25%到50%之间。3- 50% - 75% free space: 块中空闲空间在50%到75%之间。4- >= 75% free space: 块中空闲空间大于等于75%。这是INSERT操作的首选块。
- 数量: 一个L2 BMB管理多个L1 BMB。
-
数据块(Data Blocks):
- 实际存储行数据的块。
层级关系总结: 段头块 -> (指向) -> L2 BMB -> (管理) -> L1 BMB -> (管理) -> 数据块
三、 空间分配与寻址的详细过程
当一个服务器进程需要为INSERT操作寻找一个有空闲空间的块时,其过程如下:
- 访问段头: 进程首先访问段头块,找到可用的L2 BMB的地址。
- 遍历L2 BMB: 进程读取L2 BMB。L2 BMB本身也是一个位图,其中的每一位代表它所管理的一个L1 BMB的状态(例如,该L1 BMB管理的区域中是否有
>= 75% free的块)。 - 定位L1 BMB: 进程扫描L2位图,寻找一个标记为“其管理的L1区域中存在可用空间”的位。找到后,它获取对应的L1 BMB的地址(DBA - Data Block Address)。
- 遍历L1 BMB: 进程读取L1 BMB。它扫描L1 BMB中的位图条目,寻找一个状态为
4(>= 75% free)的槽位。这个槽位直接对应一个数据块。 - 访问数据块: 进程根据找到的槽位,定位到具体的数据块地址,将其读入Buffer Cache,并进行数据插入。
- 更新位图: 插入数据后,数据块的空闲空间减少。服务器进程会就地更新L1 BMB中对应该数据块的位图状态(例如,从状态
4降为状态3)。这个更新也会产生重做日志。
关键优势:
- 并发性: 多个服务器进程可以同时扫描不同的L2和L1 BMB,极大地减少了了对单一资源(如段头块)的争用。
- 精确性: 进程可以直接定位到具有理想空闲空间的块,避免了盲目尝试,减少了I/O。
四、 争用、等待事件、排查与解决
虽然ASSM极大地缓解了空间管理的争用,但争用点转移了。
场景: 极端高并发的INSERT负载,插入到同一个ASSM段(例如,一个非常热门的表)。
1. L1/L2 BMB本身的争用
-
争用原因: 如果所有并发进程都倾向于访问同一个L2 BMB和其下的同一个L1 BMB(因为他们都在向段的同一部分插入数据,比如一个没有索引组织或序列填充的堆表),那么这些位图块本身就会成为新的热点。
-
等待事件:
buffer busy waits。这是最常见的等待事件,发生在进程试图读取或修改一个正在被另一个进程从磁盘读入缓冲区或正在缓冲区中被修改的块时。对于ASSM,buffer busy waits经常发生在BMB上,而不是段头上。 -
排查:
- 查询
V$SESSION_WAIT或V$ACTIVE_SESSION_HISTORY,查看等待事件为buffer busy waits的会话。 - 使用
P1(文件号)和P2(块号)参数定位热点块。
-- 根据等待事件找到热点块对应的对象 SELECT s.sid, s.event, s.p1 as file_id, s.p2 as block_id, o.owner, o.object_name, o.object_type, o.subobject_name FROM v$session s, dba_objects o WHERE s.event = 'buffer busy waits' AND o.data_object_id = ( SELECT data_object_id FROM dba_extents WHERE file_id = s.p1 AND s.p2 BETWEEN block_id AND block_id + blocks - 1 );- 如果这个对象是表或索引,而阻塞的块是段头、L2或L1 BMB,则说明是ASSM位图争用。
- 查询
-
解决:
- 使用序列填充主键: 确保
INSERT操作通过序列等机制均匀地分布到表的所有数据块中,从而分散到不同的L1/L2 BMB。这是最有效的方法。 - 增加
PCTFREE: 为更新预留更多空间,使数据块在插入后仍保持较高的空闲比例,让每个块能容纳更多行,减少扩展频率。 - 考虑分区(Partitioning): 将表分成多个分区,每个分区是独立的段,拥有自己独立的位图结构。这样并发插入可以分散到多个独立的段位图上,彻底消除争用。
- 使用更大的块大小: 例如,从8KB改为16KB。单个L1 BMB可以管理更多的数据块,减少了需要频繁更新的BMB的数量。
- 使用序列填充主键: 确保
五、 常用管理SQL语句
-- 1. 查看表空间是否使用ASSM
SELECT tablespace_name, segment_space_management
FROM dba_tablespaces;
-- 2. 查看段的扩展信息,其中可能包含位图块
SELECT segment_name, extent_id, file_id, block_id, blocks
FROM dba_extents
WHERE segment_name = 'YOUR_TABLE_NAME'
ORDER BY extent_id;
-- 3. 识别热点块(需要诊断缓冲池竞争时)
SELECT obj, file#, dbablk, class, state, tch -- tch is touch count
FROM v$bh -- Buffer Headers
WHERE obj = (SELECT data_object_id FROM dba_objects WHERE object_name = 'YOUR_TABLE_NAME')
ORDER BY tch DESC; -- 查找被访问次数最多的块
-- 4. 检查段的空间使用详情(需要诊断行锁争用等其他问题时)
-- 注意:DBMS_SPACE包对于ASSM段的分析非常强大
BEGIN
DBMS_SPACE.SPACE_USAGE(
segment_owner => 'SCOTT',
segment_name => 'EMP',
segment_type => 'TABLE',
unformatted_blocks => u_blocks,
unformatted_bytes => u_bytes,
fs1_blocks => fs1_blocks, -- 0-25% free
fs1_bytes => fs1_bytes,
fs2_blocks => fs2_blocks, -- 25-50% free
fs2_bytes => fs2_bytes,
fs3_blocks => fs3_blocks, -- 50-75% free
fs3_bytes => fs3_bytes,
fs4_blocks => fs4_blocks, -- 75-100% free
fs4_bytes => fs4_bytes,
full_blocks => full_blocks,
full_bytes => full_bytes
);
DBMS_OUTPUT.PUT_LINE('Blocks with >75% free: ' || fs4_blocks);
END;
/
第二部分:通俗易懂的解释
让我们用一个比喻来理解ASSM的层级结构。
把一个ASSM段想象成一个巨大的、分层的仓库。
- 数据块: 仓库里一个个独立的货架。每个货架有不同大小的空闲区域(空闲空间)。
- 一级位图块(L1 BMB): 仓库区域组长。他管理着一排货架(比如20个)。他手里有一个清单(位图),记录着他管理的每个货架当前的空闲情况:“1号货架快满了(<25%空间),2号货架半满(50%),3号货架很空(>75%)…”
- 二级位图块(L2 BMB): 仓库楼层经理。他管理着所有的区域组长(L1 BMB)。他手里也有一个清单(位图),记录着哪个组长管理的区域里还有非常空的货架。比如:“A组长那边还有空位,B组长那边全满了。”
- 段头块: 仓库总负责人。他知道所有楼层经理(L2 BMB)在哪。
一个新货品入库(INSERT操作)的流程:
- 新员工(服务器进程)来到仓库,问总负责人(段头块):“我把这箱货放哪?”
- 总负责人说:“去找二楼张经理(L2 BMB),他那边可能有空位。”
- 员工找到张经理。张经理看了一眼自己的清单说:“去找第三组的李组长(L1 BMB),他那边有个货架很空。”
- 员工找到李组长。李组长看了一眼自己详细的清单,精确地告诉他:“去3号货架,那边空间很大!”
- 员工把货物放到3号货架上,然后告诉李组长:“3号货架我用了些空间,现在没那么空了,你更新下你的清单。”(更新L1 BMB)
这种设计的好处(对比老式Freelist):
- 老式仓库只有一个总负责人,所有员工都围着他问,他忙得要死(段头争用)。
- 新式仓库把管理工作下放给了多个经理和组长。员工可以同时问不同的经理,效率极高。
新式仓库的新问题(争用转移):
- 如果所有新货品都是同一种类,只能放在仓库的某一个特定区域,那么会导致所有员工都去找同一个经理和同一个组长。这个经理和组长就会成为新的瓶颈(L2/L1 BMB的
buffer busy waits)。
解决方法:
- 均匀放货: 设计一个规则,让货物可以均匀地放到仓库的各个区域(使用序列主键,使数据分布均匀)。
- 分区: 直接盖好几个独立的仓库(分区),电器仓、食品仓分开,彻底分流。
通过这个比喻,ASSM的层级结构和其带来的性能优势与潜在的新争用点就非常清晰了。它的本质是通过分治(Divide and Conquer) 和元数据冗余的策略,将集中式的管理任务分散化,从而支撑极高的并发性。
欢迎关注我的公众号《IT小Chen》

6839

被折叠的 条评论
为什么被折叠?



