Oracle 表空间设计哲学——LMT、ASSM、BIGFILE 与碎片治理

表空间是数据库的"土地规划",规划不好,后面全是坑。我见过一个库,字典管理表空间(DMT)用到 2019 年,extent 分配慢得像蜗牛,加数据文件要停业务。还有客户用 MSSM(手动段空间管理),高并发插入时 freelist 争用严重,buffer busy waits 满天飞。这篇文章把本地管理表空间(LMT)、ASSM 位图管理、BIGFILE 的利弊、以及碎片治理的实战方法拆清楚。

1 DMT 已死,LMT 是唯一选择

字典管理表空间(DMT)用数据字典(FET$、UET$)记录空闲和使用 extent,每次分配 extent 都要更新字典,产生递归 SQL 和锁争用。Oracle 10g 以后 DMT 被废弃,但老库可能还有。本地管理表空间(LMT)用数据文件头的位图(bitmap)管理 extent,分配和释放只在文件头操作,不碰数据字典,速度快 10 倍以上。如果你的库还有 DMT,尽快迁移。

 

LMT 的 extent 分配策略分两种:AUTOALLOCATE(Oracle 自动决定 extent 大小,从小到大)和 UNIFORM SIZE(统一大小)。AUTOALLOCATE 适合混合负载,小表用小 extent,大表用大 extent;UNIFORM SIZE 适合数据仓库,extent 大小一致,减少碎片。我们给 OLTP 系统用 AUTOALLOCATE,给数仓用 UNIFORM SIZE 1G。

2 ASSM vs MSSM:位图打败了空闲列表

手动段空间管理(MSSM)用空闲列表(freelist)管理段内的空闲块。每个段有几个 freelist,插入时从 freelist 找空块。高并发插入时,多个会话抢同一个 freelist,或者同一个空闲块,导致 buffer busy waits。ASSM(自动段空间管理)用位图块(L1/L2/L3 bitmap block)标记段内哪些块有空闲空间,多个会话可以并发找到不同的块插入,几乎消除 freelist 争用。

 

ASSM 的代价是:空间利用率略低(位图块占用额外空间),且某些场景下全表扫描性能稍差(因为块分布不如 MSSM 紧凑)。但对于 OLTP 高并发插入,ASSM 是必选。

3 BIGFILE:大容量但大风险

BIGFILE 表空间每个表空间只有一个数据文件,最大 128TB(32K 块大小)。好处是管理简单,加数据文件的操作省了;坏处是文件太大,恢复时间长,而且某些 OS 或存储对大文件支持不好(比如文件系统有 16TB 限制)。我们给数仓用过 BIGFILE,单文件 20TB,备份时用 RMAN 的 SECTION SIZE 分片,恢复时也可以并行。但 OLTP 核心表空间不建议用 BIGFILE,因为一旦文件损坏,整个表空间离线,影响面太大。

4 实验:ASSM vs MSSM 高并发插入对比

实验环境:Oracle 19c。两个表空间:users_mssm(MSSM,freelist 1)和 users_assm(ASSM)。

-- 创建 MSSM 表空间
CREATE TABLESPACE users_mssm
DATAFILE '/u01/oradata/users_mssm.dbf' SIZE 1G
EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M
SEGMENT SPACE MANAGEMENT MANUAL;

-- 创建 ASSM 表空间
CREATE TABLESPACE users_assm
DATAFILE '/u01/oradata/users_assm.dbf' SIZE 1G
EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M
SEGMENT SPACE MANAGEMENT AUTO;

-- 建表
CREATE TABLE test_mssm (id NUMBER, val VARCHAR2(100)) TABLESPACE users_mssm;
CREATE TABLE test_assm (id NUMBER, val VARCHAR2(100)) TABLESPACE users_assm;

-- 100 会话并发插入,各跑 10000 行
-- 会话 1-50:INSERT INTO test_mssm VALUES (seq.NEXTVAL, 'TEST');
-- 会话 51-100:INSERT INTO test_assm VALUES (seq.NEXTVAL, 'TEST');

-- 监控等待事件
SELECT event, count(*) FROM v$session_wait WHERE event='buffer busy waits' GROUP BY event;

-- 监控执行时间和 TPS

实验结果:MSSM 表,100 并发插入,buffer busy waits 每秒 4500 次,平均插入 TPS 3200。ASSM 表,buffer busy waits 每秒 180 次,TPS 9800。ASSM 性能是 MSSM 的 3 倍。而且 MSSM 表的段头块(Segment Header Block)成为严重热块,因为所有会话都要读 freelist。

碎片测试:

-- 删除 50% 数据
DELETE FROM test_assm WHERE MOD(id,2)=0;
COMMIT;

-- 查看碎片
ANALYZE TABLE test_assm COMPUTE STATISTICS;
SELECT blocks, empty_blocks, avg_space, chain_cnt, avg_row_len
FROM dba_tables WHERE table_name = 'TEST_ASSM';

-- 查看 extent 分布
SELECT extent_id, bytes/1024/1024 mb, blocks
FROM dba_extents WHERE segment_name = 'TEST_ASSM';

实验结果:DELETE 后,ASSM 表有 40% 的空闲空间在块内部(因为删除只是标记,不压缩),但 extent 没有碎片(LMT 的 UNIFORM SIZE 保证 extent 连续)。用 ALTER TABLE test_assm SHRINK SPACE 后,空闲空间回收,块数减少 45%。

5 那个字典管理表空间拖垮批量的案例

客户是一个 2005 年建的库,表空间还是 DMT。每天晚上批量 ETL,要插入 2000 万行,每次分配 extent 都要更新 FET$/UET$,产生大量递归 SQL 和 enq: ST - contention(空间事务锁)。ETL 从晚上 10 点跑到早上 6 点,其中 4 小时花在 extent 分配上。我们花了两个周末,用 DBMS_SPACE_ADMIN 和在线重定义,把所有 DMT 表空间迁移到 LMT+ASSM,ETL 时间降到 2.5 小时。这个案例让我深刻认识到:基础设施的债,迟早要还。

【踩坑笔记】新建库一律用 LMT + ASSM,别为了"兼容老系统"用 MSSM。BIGFILE 只适合归档表空间或只读表空间,OLTP 核心表空间用 SMALLFILE(多数据文件),分散风险。表空间碎片定期用 SHRINK SPACE 或 MOVE 整理,但注意这些操作会产生大量 redo 和锁,要放在低峰期。

6 总结:表空间设计的三条铁律

第一,LMT + ASSM 是标配,DMT 和 MSSM 只应出现在博物馆里。第二,OLTP 用 AUTOALLOCATE,数仓用 UNIFORM SIZE,别混用。第三,BIGFILE 谨慎用,核心数据分散到多个 SMALLFILE,降低单点故障风险。最后送一句话:表空间是数据库的地基,地基不稳,楼盖再高也会塌。别等到系统慢了才发现是 extent 分配在拖后腿。


请使用浏览器的分享功能分享到微信等