数据文件所在的 ASM 磁盘组快满时,扩容本身不复杂,容易出问题的是前置判断:新盘在两个节点上是否都能看到、应该用 asmlib 还是 udev 绑定、分区后要不要在节点二刷新、加盘后 rebalance 要跑多久。本文把两种方式放在一起讲,先给选择依据,再分别给出可复制执行的流程,最后用同一组视图验证结果。
示例环境是两节点 RAC,新增一块 200G 裸盘,未做多路径。asmlib 流程中的新盘为 /dev/sdk,udev 流程中的新盘为 /dev/sdi。盘符、大小和磁盘组名都要按实际环境替换,不要照抄。
先判断用哪种方式
两种方式最终都是把一个块设备交给 ASM,区别在于“谁负责把设备名固定下来”。
| 维度 | asmlib | udev |
|---|---|---|
| 依赖 | oracleasm 服务及内核模块 | 系统自带 udev、scsi_id |
| 磁盘名 | createdisk 时指定,路径为 /dev/oracleasm/disks/ 下 | 规则里用 SYMLINK 指定,如 /dev/asm-diskd |
| 节点一致性 | 每个节点执行 scandisks | 每个节点放置相同的规则文件 |
| 适合场景 | 存量库已经在用 asmlib | 新环境、希望少一层内核模块依赖 |
判断原则很简单:现有磁盘组用哪种,就延续哪种,不要在一个磁盘组里混用两套命名体系。新环境如果没有历史包袱,udev 用 WWID 绑定,重启后盘符变化也不会影响 ASM;asmlib 的名字更直观,但依赖 oracleasm 服务,节点上少装一个包就跑不起来。
无论选哪种,动手前先确认这几件事:
- 两个节点都能看到同一块新盘,且盘的 WWID/序列号一致。
- 新盘没有被文件系统、LVM 或其他磁盘组占用。
- 已有数据库备份,扩容安排在业务低峰;rebalance 会持续占用 I/O。
- 磁盘组名、冗余级别、剩余空间已确认,新增容量要按冗余级别折算。
扩容前的容量测算
加盘之前先把“还差多少”算清楚,避免加完发现仍然不够。查磁盘组整体水位:
set linesize 200
col name for a20
select name,state,total_mb,free_mb,
round((total_mb-free_mb)/total_mb*100,2) used_pct
from v$asm_diskgroup
order by name;
再看每个磁盘的使用是否均衡。某个盘明显比其他盘满,说明之前的 rebalance 没有跑完,或者磁盘大小不一致:
select group_number,disk_number,name,total_mb,free_mb,
round((total_mb-free_mb)/total_mb*100,2) used_pct
from v$asm_disk
order by group_number,disk_number;
需要的容量按“业务增长量 ÷ 冗余折算系数”估算。external 冗余可用容量约等于裸容量,normal 约一半,high 约三分之一;再预留 15%–20% 给 rebalance 和突发增长,不要按 100% 用满。磁盘组长期高于 85% 时,除了加盘,还要检查是否有归档、临时文件或审计表在异常增长。
方式一:asmlib 扩容
1. 确认新盘和添加时间
[root@if-db01 ~]# ls -lrth /dev/sdk
brw-rw---- 1 root disk 8, 160 Jul 31 10:16 /dev/sdk
[root@if-db02 ~]# ls -lrth /dev/sdk
brw-rw---- 1 root disk 8, 160 Jul 31 10:19 /dev/sdk
两个节点看到的时间可能差几分钟,这是正常的。重点确认设备号、大小和权限一致;如果节点二根本没有这个设备,先解决存储映射,不要继续分区。
原文记录该盘的序列号为:
Serial number: 6000c29f225306dbce07eed464682681
2. 分区
[root@if-db01 ~]# fdisk /dev/sdk
欢迎使用 fdisk (util-linux 2.23.2)。
更改将停留在内存中,直到您决定将更改写入磁盘。
使用写入命令前请三思。
Device does not contain a recognized partition table
使用磁盘标识符 0x5554dbd5 创建新的 DOS 磁盘标签。
命令(输入 m 获取帮助):n
Partition type:
p primary (0 primary, 0 extended, 4 free)
e extended
Select (default p):
Using default response p
分区号 (1-4,默认 1):
起始 扇区 (2048-83886079,默认为 2048):
将使用默认值 2048
Last 扇区, +扇区 or +size{K,M,G} (2048-83886079,默认为 83886079):
将使用默认值 83886079
分区 1 已设置为 Linux 类型,大小设为 200 GiB
命令(输入 m 获取帮助):
命令(输入 m 获取帮助):w
The partition table has been altered!
Calling ioctl() to re-read partition table.
正在同步磁盘。
分区完成后,节点一的内核已经识别到 /dev/sdk1,节点二还没有。必须在节点二刷新分区表:
[root@if-db02 ~]# partprobe /dev/sdk
这一步漏掉的典型现象是:节点一 createdisk 成功,节点二 scandisks 之后仍然看不到新盘。
3. 确认现有 ASM 磁盘
[root@if-db01 ~]# /etc/init.d/oracleasm listdisks
OCRDG01
ARCHDG01
DATADG01
先确认 DATADG02 这个名称没有被占用。ASM 磁盘名在集群内必须唯一,重复创建会报错,也不会覆盖已有磁盘。
4. 创建 ASM 磁盘
[root@if-db01 ~]# /etc/init.d/oracleasm createdisk DATADG02 /dev/sdk1
createdisk 只需要在节点一执行。它会把分区标记为 ASM 磁盘,并在 /dev/oracleasm/disks/ 下生成对应设备。
5. 节点二扫描并确认
[root@if-db02 ~]# /etc/init.d/oracleasm listdisk
OCRDG01
ARCHDG01
DATADG01
[root@if-db02 ~]# /etc/init.d/oracleasm scandisks
[root@if-db02 ~]# /etc/init.d/oracleasm listdisk
OCRDG01
ARCHDG01
DATADG01
DATADG02
原文节点二使用的是 listdisk,实际命令是 listdisks;如果执行时报“命令不存在”,换成复数形式即可。判断标准不是命令有没有输出,而是第二次列表里是否出现了 DATADG02。
6. 以 sysasm 身份查看磁盘组
[root@if-db01 ~]# su - grid
-- 进入sysam用户
[grid@if-db01 ~]# sqlplus / as sysasm
-- 查看数据库盘符组
set linesize 600
select name,STATE,TOTAL_MB,FREE_MB,VOTING_FILES from v$asm_diskgroup;
-- 查看数据盘符组状态
set pagesize 900
set linesize 900
col MOUNT_STATUS for a30
col HEADER_STATUS for a10
col STATE for a6
col path for a40
select DISK_NUMBER,GROUP_NUMBER,MOUNT_STATUS,HEADER_STATUS,STATE,TOTAL_MB,FREE_MB,NAME,PATH,CREATE_DATE,MOUNT_DATE from v$asm_disk;
这里要确认两件事:DATADG 处于 MOUNTED 状态;v$asm_disk 里没有同名或同路径的旧记录。
7. 加入磁盘组并监控 rebalance
-- 添加磁盘到数据盘符组
alter diskgroup DATADG add disk '/dev/oracleasm/disks/DATADG02' rebalance power 6;
-- 查看自动平衡的工作进度
select * from v$asm_operation;
power 6 是 rebalance 的并行度,取值范围通常是 1 到 11。值越大,平衡越快,但对存储和业务的 I/O 冲击也越大。生产库在业务时段建议用较低的 power,或者等维护窗口再提高;不要在业务高峰直接照抄 6。
方式二:udev 扩容
udev 的核心是用磁盘的 WWID 生成一个固定符号链接。盘符 /dev/sdi 可能变化,WWID 不会。
1. 获取 WWID
/usr/lib/udev/scsi_id -g -u -d /dev/sdi
36000c29c93dea3b8028e2c0d21dd7000
两个节点都要执行,确认输出完全一致。若不一致,说明看到的不是同一块盘,后续规则一定不能继续写。
2. 备份并修改 udev 规则
RAC 环境双节点都需要添加。添加前先备份原文件:
cp /usr/lib/udev/rules.d/99-oracle-asmdevices.rules /usr/lib/udev/rules.d/99-oracle-asmdevices.rules.$(date +%F)
然后追加规则:
cat /usr/lib/udev/rules.d/99-oracle-asmdevices.rules
......
KERNEL=="sd*", SUBSYSTEM=="block", PROGRAM=="/usr/lib/udev/scsi_id --whitelisted --replace-whitespace --device=/dev/$name", RESULT=="36000c29c93dea3b8028e2c0d21dd7000", SYMLINK+="asm-diskd", OWNER="grid", GROUP="asmadmin", MODE="0660"
几个参数不要改错:
RESULT必须和上一步的 WWID 完全一致。SYMLINK是本节点使用的固定路径,示例为/dev/asm-diskd。OWNER/GROUP要能让 grid 用户读写,通常是grid:asmadmin,权限0660。- 规则文件在两个节点上保持一致,但每块盘只能有一条对应 WWID 的规则。
3. 重新加载规则
RAC 环境双节点都需要执行:
/sbin/udevadm trigger --type=devices --action=change
执行后建议确认符号链接已经生成、属主正确:
ls -l /dev/asm-diskd
原文没有记录这条检查命令,但它能提前发现“规则写了但没生效”的问题,避免到 alter diskgroup 才报路径不存在。
4. 查看磁盘组并加入新盘
su - grid
sqlplus / as sysasm
set linesize 600
select name,STATE,TOTAL_MB,FREE_MB,VOTING_FILES from v$asm_diskgroup;
-- 查看数据盘符组状态
set pagesize 900
set linesize 900
col MOUNT_STATUS for a30
col HEADER_STATUS for a10
col STATE for a6
col path for a40
select DISK_NUMBER,GROUP_NUMBER,MOUNT_STATUS,HEADER_STATUS,STATE,TOTAL_MB,FREE_MB,NAME,PATH,CREATE_DATE,MOUNT_DATE from v$asm_disk;
确认新设备可见后加盘:
-- 添加磁盘到数据盘符组
alter diskgroup DATADG add disk '/dev/asm-diskd' rebalance power 6;;
原文这条语句末尾是两个分号(;;),代码块按原文保留。SQL*Plus 会把多余的分号当成空语句,通常不报错;实际执行时写一个即可。
5. 查看挂载和平衡情况
set lines 200 pages 50000
col name for a25
col path for a55
select group_number,disk_number,name,path,total_mb/1024 total_G,path,mode_status,repair_timer from v$asm_disk order by group_number,disk_number;
-- 查看自动平衡的工作进度
select * from v$asm_operation;
多路径环境的差异
原文这次是裸盘直通,没有做多路径,所以 udev 规则直接绑定 /dev/sdi 的 WWID,asmlib 直接对 /dev/sdk1 执行 createdisk。如果存储侧做了 multipath,做法要改两处。
第一,绑定对象换成 multipath 设备,而不是物理盘。先确认 multipath -ll 输出里的 WWID 和 /dev/mapper/ 下的设备名,再对 /dev/mapper/xxx 取 scsi_id,或者在规则里直接用 ENV{DM_UUID} 匹配。对物理盘 /dev/sdX 绑定,多路径切换后符号链接会指向一条已经失效的链路。
第二,asmlib 的 createdisk 也要传 multipath 设备名(/dev/mapper/xxx),不要传 /dev/sdk1。否则 ASM 只认其中一条路径,另一条路径的故障切换对它没有意义。
判断是否已经正确接管,可以看 v$asm_disk 的 PATH 列:多路径环境下应该是 /dev/mapper/ 开头的路径,而不是 /dev/sd 开头。这一列在加盘后要专门确认一次。
另外,多路径环境下新加 LUN 后需要先 multipath -r 重新加载配置,再执行 partprobe。顺序反了,udev 规则可能匹配不到设备。原文环境没有这一层,照抄流程时容易漏掉。
两个节点的检查顺序
扩容时最容易漏的是“只在节点一验证”。正确的检查顺序是:
- 两个节点分别
ls -lrth /dev/sdk(或对应的 udev 符号链接),确认设备存在、权限一致。 - 两个节点分别
scsi_id取 WWID,确认一致(udev 方式)。 - 节点一
createdisk,节点二scandisks后listdisks确认能看到。 - 任意一个节点的 ASM 实例执行
alter diskgroup ... add disk,ASM 会自动同步到另一个节点。 - 两个节点分别查
v$asm_disk,确认PATH和MOUNT_STATUS一致。
第 4 步不需要在两个节点各执行一次。add disk 是集群级操作,重复执行会报磁盘已在组内。如果节点二的视图里暂时看不到新盘,先等几秒再查,不要立刻重复加盘。
lsdsk 的输出也可以在两个节点分别执行一次做对比,正常情况下两个节点看到的成员列表应该完全相同。节点二缺少新盘,通常是 udev 规则没生效或 oracleasm scandisks 没跑,回到对应步骤排查。
统一验证
两种方式加盘完成后,验证动作是一样的。
先看磁盘组容量。TOTAL_MB 应该增加,FREE_MB 也会随之增加;如果是 normal/high redundancy,可用容量不等于裸盘容量,要按故障组折算。
set linesize 600
select name,STATE,TOTAL_MB,FREE_MB,VOTING_FILES from v$asm_diskgroup;
再看新盘是否真正属于目标磁盘组,状态是否为 NORMAL:
set pagesize 900
set linesize 900
col MOUNT_STATUS for a30
col HEADER_STATUS for a10
col STATE for a6
col path for a40
select DISK_NUMBER,GROUP_NUMBER,MOUNT_STATUS,HEADER_STATUS,STATE,TOTAL_MB,FREE_MB,NAME,PATH,CREATE_DATE,MOUNT_DATE from v$asm_disk;
最后看 rebalance 是否结束:
select * from v$asm_operation;
v$asm_operation 有记录,说明平衡还在进行;查询返回空行,才表示本次操作完成。重点看 OPERATION(应为 REBALANCE)、STATE、POWER/ACTUAL_POWER、SOFAR、EST_WORK、EST_RATE 和 EST_MINUTES。EST_MINUTES 只是估算,会随着 I/O 情况变化,不要用它判断是否卡死;判断依据是 SOFAR 是否持续增长。
在 rebalance 期间不要重启集群、不要下线磁盘,也不要执行其他会改变磁盘组结构的操作。需要确认进度时,反复查 v$asm_operation 即可,不要重复执行 add disk。
用 asmcmd 交叉确认
视图之外,再用 asmcmd 看一眼磁盘组的成员,能快速确认新盘的路径和状态:
asmcmd lsdsk -G DATADG
输出里 asmlib 方式的路径是 /dev/oracleasm/disks/DATADG02,udev 方式是 /dev/asm-diskd。如果新盘出现在 lsdsk 里但不在 v$asm_disk 中,说明磁盘头还没被 ASM 识别,先查 ASM 告警日志里有没有 ORA-15000 系列的报错,不要急着重复执行 add disk。
扩容期间也建议盯一下 ASM 告警日志($ORACLE_BASE/diag/asm/+asm/+ASM1/trace/alert_+ASM1.log,实例名按实际替换),重点看有没有磁盘离线、读错误或 rebalance 失败的记录。存储侧的链路抖动在视图里未必立刻体现,但告警日志通常会先报出来。
加错盘怎么退
如果新盘加进了错误的磁盘组,或者加完发现盘本身有问题,可以在 ASM 实例里把磁盘摘掉:
alter diskgroup DATADG drop disk DATADG02 rebalance power 4;
这条命令会触发一次反向 rebalance,把该盘上的数据迁走,不是立即生效。执行后必须用 v$asm_operation 确认迁移完成,再检查 v$asm_disk 中该盘是否已消失。迁移期间同样有 I/O 压力,且磁盘组剩余空间必须够承接这部分数据,否则可能失败。drop 之前确认磁盘名没有写错——摘错盘会直接触发冗余降级。
风险与注意事项
盘符漂移。 原文明确这次是裸盘、没有做多路径。asmlib 方式在 createdisk 之后由 ASM 管理,重启影响较小;udev 方式靠 WWID 绑定,也不依赖 /dev/sdX 不变。真正危险的是在 udev 规则里写错 WWID,或者两个节点用了不同的规则,导致同一个 ASM 磁盘在不同节点映射到不同设备。
不要在 OS 层重新格式化。createdisk 或 udev 绑定完成后,这块盘就交给 ASM 了。再执行 mkfs、重新分区或加入 LVM,会破坏 ASM 头。
rebalance 的 I/O 成本。power 6 不是固定推荐值。数据量大、存储性能一般时,先低 power 跑,观察业务响应和存储延迟,再决定是否提高。扩容期间归档备份、大批量导入等重 I/O 任务应错开。
扩容不等于空间立刻可用。 新盘加入后要等 rebalance 完成,数据才真正重新分布;在完成前,FREE_MB 的读数可能波动。
操作前备份。 磁盘组扩容本身一般不动数据文件,但误加盘、加错磁盘组、规则写错都可能引发故障。至少确认数据库有可用备份,并记录当前 v$asm_diskgroup/v$asm_disk 的输出,便于回退比对。
排错清单
- 节点二看不到新分区:先在节点二执行
partprobe /dev/sdk,再scandisks。 createdisk报磁盘已存在:用listdisks确认名称,换一个未占用的磁盘名,不要强行覆盖。- 节点二
listdisks仍没有新盘:检查/dev/oracleasm/disks/下是否有对应设备,确认oracleasm服务在两个节点都正常。 - udev 规则写了但
/dev/asm-diskd不存在:确认RESULT与scsi_id输出一致,确认规则文件路径正确,再执行udevadm trigger。 alter diskgroup报路径不存在:先确认 ASM 实例能看到该路径,再检查权限和属主。v$asm_operation长时间有记录:看SOFAR是否增长、存储是否有异常延迟;不要因为EST_MINUTES变大就重启。
待核对
- 原文节点二使用
listdisk,Oracle 的标准命令是listdisks;代码块按原文保留,实际执行建议用复数形式。 - 原文 udev 流程中
sqlpuls为拼写错误,文中改为sqlplus;alter diskgroup末尾的;;按原文保留。 - asmlib 示例盘为
/dev/sdk(200G),udev 示例盘为/dev/sdi,两者不是同一块盘,盘符和容量需按实际替换。 - 原文未记录
ls -l /dev/asm-diskd的输出,该检查为补充。 - 原文未说明磁盘组冗余级别,实际可用容量需按 normal/high redundancy 折算。
alter diskgroup ... drop disk为补充的回退命令,原文未涉及;执行前务必确认磁盘名,并在维护窗口操作。
原文首发于微信公众号「止水研习社」。
文章评论