【DB2 数据库】02 模拟故障排查系列:构造TS2表空间打满并恢复
一、这篇文章要解决什么问题
在 DB2 日常运维里,“表空间打满”是非常常见、也非常适合拿来做入门演练的一类故障。
这一篇文章基于真实实验,完整复现了下面这个过程:
- 手工创建一个很小的表空间
TS2 - 在
TS2中创建测试表T3 - 持续插入大字段数据,直到表空间被打满
- 观察表空间打满时的状态
- 删除测试表后,验证空间是否恢复可用
- 进一步解释“为什么表删了,但底层文件系统空间没有自动变小”
这不是一篇只给命令的操作稿,而是一篇把实验现象、排查思路和概念理解放在一起的故障模拟文章。
二、实验环境
- 操作系统:
SUSE Linux Enterprise Server 12 SP5 - DB2 版本:
DB2 v9.7.0.6 Fix Pack 6 - 实例:
db2inst1 - 数据库:
TESTDB - 用于故障演练的目标表空间:
TS2 - 容器路径:
/DB2_DATA/TESTDB/ts2/ts2_01.dat
在上一篇文章中,我们已经完成了:
TESTDB的创建- 默认表空间
USERSPACE1的验证 - 独立表空间
TS1的创建和扩容
这一次为了更稳定地复现“表空间打满”,我没有直接拿 TS1 开刀,而是额外新建了一个更小的表空间 TS2。
三、为什么要单独创建一个小表空间 TS2
如果直接拿现有较大的表空间去做“打满”实验:
- 需要插入的数据量会更多
- 现象可能不够稳定
- 对已有实验对象影响更大
所以这次我专门创建了一个容量很小的独立表空间 TS2,这样做有几个好处:
- 更容易快速打满
- 故障现象更集中
- 回滚和恢复更简单
- 更适合写成博客给读者复现
四、准备 TS2 的容器目录
先用 root 准备容器目录:
mkdir -p /DB2_DATA/TESTDB/ts2
# 创建 TS2 的容器目录
chown -R db2inst1:db2iadm1 /DB2_DATA/TESTDB/ts2
# 把目录属主改成实例用户,确保 DB2 有权限创建容器文件
chmod 755 /DB2_DATA/TESTDB/ts2
# 赋予正常目录权限
ls -ld /DB2_DATA/TESTDB/ts2
# 确认目录状态五、创建一个很小的表空间 TS2
切到实例用户后,连接数据库:
su - db2inst1
# 切换到 DB2 实例用户
db2 connect to TESTDB
# 连接到 TESTDB创建表空间:
db2 "create regular tablespace TS2 pagesize 4 K managed by database using (file '/DB2_DATA/TESTDB/ts2/ts2_01.dat' 2000) bufferpool IBMDEFAULTBP"
# 创建一个很小的普通表空间 TS2
# 容器只有 2000 页,页大小是 4KB验证表空间是否创建成功:
db2 "select tbspaceid, tbspace from syscat.tablespaces where tbspace = 'TS2'"
# 查询 TS2 的编号和名称这次实验的真实结果是:
TS2的TBSPACEID = 5
所以后面查看容器时,我直接使用:
db2 list tablespace containers for 5 show detail六、关闭 TS2 的自动扩容
这次实验的目标是稳定制造“表空间打满”,所以不希望表空间自己偷偷长大。
执行:
db2 "alter tablespace TS2 autoresize no"
# 明确关闭 TS2 的自动扩容然后查看容器:
db2 list tablespace containers for 5 show detail
# 查看 TS2 当前的容器此时 TS2 只有一个容器:
/DB2_DATA/TESTDB/ts2/ts2_01.dat
七、创建测试表 T3,并明确放到 TS2
db2 "create table T3(id int not null, c1 varchar(3000)) in TS2"
# 创建测试表 T3,并明确指定落在 TS2
db2 "select tabschema, tabname, tbspace from syscat.tables where tabname = 'T3'"
# 验证 T3 的所属表空间实验时验证结果为:
T3落在TS2
为了先确认表本身没有问题,可以先少量插入:
db2 "insert into T3 values (1, repeat('A',3000))"
# 插入第一条测试数据
db2 "insert into T3 values (2, repeat('B',3000))"
# 插入第二条测试数据
db2 "select count(*) from T3"
# 确认当前写入正常八、持续插入数据,构造表空间打满
接下来通过循环持续插入大字段数据:
i=3
while [ $i -le 50000 ]
do
db2 "insert into T3 values ($i, repeat('X',3000))" || break
i=$((i+1))
done这段脚本的作用很简单:
- 不断向
TS2里的T3插入较大的记录 - 一旦插入失败就立刻停止
由于 TS2 容量非常小,所以很快就能把它打满。
九、表空间打满后的真实现象
这次实验里,表空间详细信息中 TS2 的状态如下:
Tablespace ID = 5
Name = TS2
Type = Database managed space
Contents = All permanent data. Regular table space.
State = 0x0000
Detailed explanation:
Normal
Total pages = 2000
Useable pages = 1952
Used pages = 1952
Free pages = 0
High water mark (pages) = 1952
Page size (bytes) = 4096
Extent size (pages) = 32
Prefetch size (pages) = 32
Number of containers = 1这里最关键的两个字段是:
Used pages = 1952Free pages = 0
这已经足以说明:
TS2 在实验当时已经被打满。
也就是说,表空间已经没有多余的可用页再继续分配。
十、判断表空间打满时,重点看哪些字段
很多人第一次看 list tablespaces show detail 时会被很多字段绕晕,其实这类实验先重点看 4 个就够了:
1. Total pages
表空间总页数。
2. Used pages
当前已经使用掉的页数。
3. Free pages
当前还能继续分配的空闲页数。
4. High water mark
表空间历史上曾经用到的最高水位。
在这次实验里:
Free pages = 0
这就是最直观的故障信号。
十一、恢复动作:删除测试表 T3
为了恢复实验环境,我执行了:
db2 "drop table T3"
# 删除测试表 T3,释放其占用的页这一步是本次实验最值得继续深入理解的地方。
很多人第一反应会以为:
drop table 之后,底层文件系统空间应该立刻回来。
但真实情况并不是这样。
十二、为什么删表后,底层文件系统空间没有自动变小
这是本次实验里最有价值的结论之一。
更准确地说:
删除表之后,空间先回到了 DB2 表空间内部,可被数据库重新分配使用;但不会自动返还给操作系统文件系统。
也就是说,这里发生的是两层不同的“释放”:
第一层:DB2 内部释放
T3不再占用这些页- 这些页重新回到
TS2内部 - 后续其他对象还可以继续使用这些页
第二层:OS 文件系统释放
- 容器文件
ts2_01.dat真的缩小 - 磁盘空间回到操作系统可用空间
这次实验中,发生的是第一层,没有发生第二层。
也正因为如此,哪怕删了表,通常也不会看到容器文件在操作系统层面自动缩回去。
一句话总结:
数据删掉后,空间先回 DB2,不会自动回 OS。
十三、如何验证空间确实已经回到表空间内部
为了验证“空间是可复用的”,我没有停留在理论上,而是继续做了一个非常关键的实验:
重新创建 T3,并再次插入数据。
db2 "create table T3(id int not null, c1 varchar(3000)) in TS2"
# 重新创建 T3
db2 "insert into T3 values (1, repeat('A',3000))"
# 再插入一条数据实验结果是:
DB20000I The SQL command completed successfully.
DB20000I The SQL command completed successfully.这就证明了一件事:
虽然操作系统文件空间没有自动缩小,但 TS2 内部的页已经重新变成可用状态,可以再次写入。
这个验证比单纯看概念更有说服力。
十四、这次实验到底说明了什么
通过这次实验,我们实际验证了下面这些结论:
- 可以通过“很小的 DMS 表空间 + 大字段循环插入”稳定复现表空间打满
- 判断是否打满,重点看
Free pages - 当
Free pages = 0时,表空间已经没有可继续分配的页 - 删除表后,空间优先回到 DB2 表空间内部,而不是立即返还给底层文件系统
- 表空间容器文件不会因为删表而自动缩小
- 只要页已经被 DB2 重新纳入可分配范围,就可以继续建表、继续写入
十五、这篇实验和上一篇有什么衔接关系
如果说上一篇文章重点解决的是:
- 默认表空间和独立表空间的区别
- 表、Schema、表空间、容器到底是什么关系
那么这一篇文章重点解决的是:
- 表空间真的满了会是什么样
- 该怎么看
list tablespaces show detail - 为什么“删表了”不等于“OS 空间马上回来”
也就是说,这一篇已经从“环境搭建”开始进入真正的“故障模拟与排查”阶段了。
十六、这次实验特别适合写给谁
这篇文章特别适合下面几类读者:
- 刚接触 DB2 运维的人
- 知道表空间概念,但对 DMS 行为不够直观的人
- 以为删表后文件系统空间一定会立刻回来的读者
- 想搭一个可重复、可回滚、可写博客的 DB2 故障实验环境的人
十七、下一篇可以继续做什么
基于目前环境,下一篇最自然的方向有几个:
- 给
TS2扩容并恢复业务 - 模拟容器权限异常
- 模拟日志空间不足
- 模拟实例未启动或异常终止
如果继续沿着表空间这条线往下走,那么最适合的下一篇就是:
表空间打满之后,如何扩容并恢复
这样整套系列会非常顺。