【DB2 数据库】01 模拟故障排查系列:从零搭建TESTDB并理解表空间与Schema
一、这篇文章解决什么问题
这篇文章不是单纯记录“怎么创建一个数据库”,而是基于一次真实实验,把下面几个初学时最容易混淆的问题一次理顺:
- DB2 实例、数据库、Schema、表空间,到底是什么层级关系
- 新建数据库后,表默认落在哪个表空间
- 为什么生产环境通常不会长期直接用默认
USERSPACE1 - 如何手工创建独立表空间,并指定容器路径
- 表空间扩容有哪些方式
ADD、EXTEND、AUTORESIZE、INCREASESIZE到底有什么区别
这篇文章里的结论,全部来自我在实验环境中的实际操作和即时验证,不是脱离环境的概念堆砌。
二、实验环境
- 操作系统:
SUSE Linux Enterprise Server 12 SP5 - DB2 版本:
DB2 v9.7.0.6 Fix Pack 6 - 实例:
db2inst1 - 新建实验库:
TESTDB - 数据目录:
/DB2_DATA - 备份目录:
/DB2_BACKUP
这次实验不是从“创建实例”开始,而是复用现有实例 db2inst1,在空实例环境上重新创建一个简单名字的数据库 TESTDB。
三、先厘清层级关系
这一步很重要,因为如果一开始把层级搞反,后面看表空间、Schema 和容器都会越看越乱。
正确关系是:
instance管数据库运行database管对象集合schema管对象命名归属tablespace管对象物理存放
可以先记成:
实例 > 数据库 > Schema > 表
而 tablespace 不在这条命名层级链上,它更像是“对象实际住在哪里”。
一个更准确的类比
db2inst1是实例TESTDB是数据库DB2INST1、APP、HR这种是数据库里的 SchemaT1、T2是具体表USERSPACE1、TS1是表空间
也就是说:
DB2INST1.T1表示逻辑名字USERSPACE1或TS1表示物理存放位置
一句话记忆:
Schema 决定表叫什么全名,tablespace 决定表住在哪里。
四、从空环境创建 TESTDB
1. 确认实例存在,数据库目录为空
先切到实例用户:
su - db2inst1
# 切换到 DB2 实例用户确认实例和数据库目录状态:
db2 get instance
# 查看当前实例名,确认是 db2inst1
db2 list db directory
# 查看当前实例下已经编目的数据库实验时这里返回的是空目录,说明可以从头开始建新库。
2. 创建新数据库
db2 "create db TESTDB on /DB2_DATA"
# 在 /DB2_DATA 上创建一个新数据库 TESTDB3. 验证数据库能正常连接
db2 list db directory
# 再次查看数据库目录,确认 TESTDB 已经存在
db2 connect to TESTDB
# 连接到 TESTDB
db2 "select current server from sysibm.sysdummy1"
# 验证当前连接到的数据库名
db2 terminate
# 结束当前 DB2 会话到这里为止,实验库 TESTDB 就创建完成了。
五、默认表会落到哪里
1. 创建一个最简单的测试表 T1
db2 connect to TESTDB
# 连接 TESTDB
db2 "create table T1(id int not null, name varchar(50))"
# 创建测试表 T1,这里没有显式指定表空间
db2 "insert into T1 values (1, 'A')"
# 插入第一条数据
db2 "insert into T1 values (2, 'B')"
# 插入第二条数据
db2 "select * from T1"
# 查询测试数据2. 查看 T1 实际属于哪个表空间
db2 "select tabschema, tabname, tbspace from syscat.tables where tabname = 'T1'"
# 查看 T1 的 schema 和 tablespace这次实验的真实结果是:
T1的tabschema是DB2INST1T1的tbspace是USERSPACE1
也就是说,如果建表时没有写 in 某个表空间,普通业务表会默认落在 USERSPACE1。
3. 查看 USERSPACE1 的容器路径
db2 list tablespace containers for 2 show detail
# 查看表空间 ID=2,也就是 USERSPACE1 的底层容器路径实验时查到的真实路径是:
/DB2_DATA/db2inst1/NODE0000/TESTDB/T0000002/C0000000.LRG
所以此时 T1 的完整归属可以写成:
- 逻辑名字:
DB2INST1.T1 - 所在表空间:
USERSPACE1 - 底层容器:
/DB2_DATA/db2inst1/NODE0000/TESTDB/T0000002/C0000000.LRG
六、为什么生产里通常要自己建独立表空间
只用默认 USERSPACE1 当然能跑,但不适合长期生产运维。
常见原因有:
- 所有业务对象混在一起,不利于后续扩容
- 做容量规划时边界不清楚
- 出现表空间满、容器异常时,不容易快速定位
- 不方便把不同业务、不同对象类型分层管理
所以生产里通常会自己规划:
- 数据表空间
- 索引表空间
- LOB 表空间
- 临时表空间
而不是把所有业务表长期都扔到默认表空间里。
七、手工创建独立表空间 TS1
这一步是整篇实验最关键的部分之一。
1. 先准备容器目录
用 root 创建目录:
mkdir -p /DB2_DATA/TESTDB/ts1
# 创建 TS1 的容器目录
chown -R db2inst1:db2iadm1 /DB2_DATA/TESTDB/ts1
# 把目录属主改成实例用户
chmod 755 /DB2_DATA/TESTDB/ts1
# 赋予正常访问权限2. 创建 TS1
su - db2inst1
# 切回实例用户
db2 connect to TESTDB
# 连接数据库
db2 "create regular tablespace TS1 pagesize 4 K managed by database using (file '/DB2_DATA/TESTDB/ts1/ts1_01.dat' 20000) bufferpool IBMDEFAULTBP"
# 创建普通表空间 TS1
# pagesize 4K:页大小 4KB
# managed by database:由 DB2 管理表空间
# using file ... 20000:使用一个文件容器,大小 20000 页3. 验证 TS1 是否创建成功
这次实验里,我一开始写错了查询字段名。
错误写法:
db2 "select tbsp_id, tbsp_name from syscat.tablespaces where tbsp_name = 'TS1'"在这台 DB2 9.7 上,这条会报错,因为正确字段不是 tbsp_id/tbsp_name,而是:
TBSPACEIDTBSPACE
正确写法:
db2 "select tbspaceid, tbspace from syscat.tablespaces where tbspace = 'TS1'"
# 查询表空间编号和名字本次实验查到:
TS1的TBSPACEID = 4
4. 查看 TS1 的容器路径
db2 list tablespace containers for 4 show detail
# 查看表空间 ID=4 的容器详情实验时容器路径为:
/DB2_DATA/TESTDB/ts1/ts1_01.dat
八、创建一张明确落在 TS1 的表 T2
1. 创建表并指定表空间
db2 "create table T2(id int not null, name varchar(50)) in TS1"
# 创建测试表 T2,并明确指定放到 TS1
db2 "insert into T2 values (1, 'A')"
# 插入第一条测试数据
db2 "insert into T2 values (2, 'B')"
# 插入第二条测试数据2. 验证 T2 确实落在 TS1
db2 "select tabschema, tabname, tbspace from syscat.tables where tabname = 'T2'"
# 查看 T2 所在表空间
db2 "select * from T2"
# 查询 T2 数据实验时真实结果是:
T2的tabschema = DB2INST1T2的tbspace = TS1
于是这两张表的区别就很清楚了:
T1-> 默认USERSPACE1T2-> 手工指定TS1
九、一个常见疑问:查询表时为什么不用指定表空间
这是实验过程中最有价值的一组问答之一。
很多人第一次接触表空间时都会疑惑:
我明明只是执行了:
db2 connect to TESTDB
db2 "select * from T1"又没有说“去哪个表空间里查”,DB2 怎么知道该去哪?
答案是:
你连的是数据库,不是表空间。
DB2 查询表时,先根据系统目录判断:
- 这张表叫什么
- 属于哪个 Schema
- 物理上放在哪个表空间
也就是说,表空间对普通 SQL 查询是透明的。
查询关注的是:
- 表名是谁
- Schema 是谁
而不是:
- 这个表放在哪个表空间
表空间更多是运维、存储规划和故障处理时才需要关注的维度。
十、再解释一次 Schema
Schema 是这次实验里另一个最容易和表空间混淆的概念。
可以先把它理解成:
Schema 是数据库里的逻辑命名空间。
它的作用不是决定表放哪,而是决定表叫什么全名。
例如:
DB2INST1.T1APP.T1
这两个表名虽然都叫 T1,但因为 Schema 不同,所以可以共存。
所以更准确地说:
- 同一个 Schema 里,不能有两个同名表
- 不同 Schema 里,可以有同名表
这也是为什么 select * from T1 很多时候能成功,因为当前用户的默认 Schema 恰好就是 DB2INST1。
十一、给 TS1 扩容:先做手工扩容
接下来开始进入运维动作。
1. 查看 TS1 当前容器数量
db2 list tablespace containers for 4 show detail
# 查看 TS1 当前的容器最开始只有一个容器:
ts1_01.dat
2. 新增第二个容器
db2 "alter tablespace TS1 add (file '/DB2_DATA/TESTDB/ts1/ts1_02.dat' 20000)"
# 给 TS1 增加第二个容器文件3. 验证扩容结果
db2 list tablespace containers for 4 show detail
# 再次查看 TS1 的容器
db2 list tablespaces show detail
# 查看 TS1 的总页数变化实验结果符合预期:
TS1从 1 个容器变成了 2 个容器- 总页数从
20000增加到40000
这一步属于:
手工扩容
十二、扩容时,容器能不能放在别的路径
可以。
对于这种手工定义容器路径的表空间来说:
- 新容器完全可以放到另一条目录
- 也可以放到另一块新盘
只要满足:
- 路径存在
- 实例用户有权限
- 底层挂载正常
就可以加入表空间。
这也是生产环境里很常见的扩容方式。
十三、容器放在不同介质上会不会有问题
从“能不能工作”的角度看:
- 可以工作
- 不会因为一个在 SSD、一个在 HDD 就自动把数据库搞坏
但从运维和性能角度看,要谨慎:
- 同一表空间里混用快慢差异很大的介质,性能往往会被慢盘拖住
- 某块慢盘或不稳定盘出现空间不足、挂载丢失、权限异常时,整个表空间仍会受到影响
所以生产里更稳妥的做法通常是:
- 同类介质尽量放在同一个表空间内
- 热点对象单独规划更快的表空间
十四、ADD、EXTEND、INCREASESIZE 到底有什么区别
这是本次实验里最值得总结的一组概念。
1. ADD
db2 "alter tablespace TS1 add (file '/DB2_DATA/TESTDB/ts1/ts1_02.dat' 20000)"含义:
- 现在立刻新增一个容器
- 表空间马上变大
2. EXTEND
含义:
- 不是加新文件
- 而是把已有容器变大
本质上也是:
手工立即扩容
3. INCREASESIZE
db2 "alter tablespace TS1 increasesize 10 percent"它和 ADD/EXTEND 不一样。
它不是“现在立刻增加 10% 空间”,而是:
如果以后发生自动扩容,那么每次自动扩容按 10% 增长。
所以最准确的记法是:
ADD/EXTEND是动作INCREASESIZE是规则
一句话总结:
ADD/EXTEND 是手工立即扩容,INCREASESIZE 是自动扩容时的增长策略。
十五、TS1 现在到底是手工管理,还是自动扩容
这个问题实验里也做了验证。
1. 创建 TS1 时的状态
最初创建 TS1 的命令是:
db2 "create regular tablespace TS1 pagesize 4 K managed by database using (file '/DB2_DATA/TESTDB/ts1/ts1_01.dat' 20000) bufferpool IBMDEFAULTBP"这里的:
managed by database
很容易让人误以为“数据库会自动帮我扩容”。
实际上不是。
它更准确的含义是:
- DB2 管理这个表空间内部页和 extent 的使用
- 但不等于默认就自动扩容整个表空间
所以刚创建完成时,TS1 仍然属于:
- 手工定义容器
- 容量需要人为控制
2. 后续我们又开启了自动扩容策略
实验中执行并成功的命令有:
db2 "alter tablespace TS1 autoresize yes"
# 开启自动扩容
db2 "alter tablespace TS1 increasesize 10 percent"
# 每次自动扩容时按 10% 增长所以到实验后半段,TS1 变成了:
- 先手工扩过一次
- 再开启了自动扩容策略
十六、一个很有价值的踩坑:DB2 9.7 的语法差异
这次实验还踩到了一个很重要的版本差异点。
我一开始尝试:
db2 "alter tablespace TS1 increasesize 5000 pages"结果报错:
Expected tokens may include: "PERCENT"说明在这台 DB2 9.7 上,INCREASESIZE 这里不接受 PAGES,而接受:
PERCENT
所以这条改成下面写法后成功:
db2 "alter tablespace TS1 increasesize 10 percent"然后我又尝试:
db2 "alter tablespace TS1 maxsize 80000 pages"依然报错,提示期望的是:
K
这说明在这台 DB2 9.7 上,MAXSIZE 更适合写容量单位:
KMG
这一点非常值得记下来,因为它不是纸面知识,而是实验现场真实踩出来的结论。
十七、这篇实验的最终结论
到这里,我们已经实际完成并验证了下面这些事情:
- 在空实例环境里创建了
TESTDB - 建立了默认测试表
T1 - 验证了
T1默认落在USERSPACE1 - 手工创建了独立表空间
TS1 - 建立了明确落在
TS1的测试表T2 - 验证了表、表空间、容器三层对应关系
- 对
TS1做了一次手工扩容ADD - 验证了
AUTORESIZE + INCREASESIZE的设置方式 - 实测发现了
DB2 9.7的语法差异
十八、对初学者最重要的三句话
最后把整篇文章压缩成三句话:
Schema 决定表叫什么全名,tablespace 决定表住在哪里。不指定表空间时,普通业务表会默认落到 USERSPACE1。ADD/EXTEND 是手工扩容,INCREASESIZE 是自动扩容规则。
十九、下一篇可以接着写什么
基于这篇实验,下一篇最自然的方向有两个:
- 继续做“表空间打满”故障演练
- 继续做“容器路径异常/权限异常”故障演练
因为现在我们已经有了:
- 一个默认表空间对象
T1 - 一个独立表空间对象
T2 - 一个可以继续操作的手工表空间
TS1
后续不管做容量故障还是容器故障,都已经有了非常合适的实验载体。