最近在一个生产环境里遇到一个挺典型的Oracle ASM问题:磁盘组监控显示还剩几百GB可用,但业务侧创建数据文件就是报ORA-15041,DBA现场一脸懵。后来排查下来,问题不在磁盘“有没有空间”,而在大家对ASM磁盘组的空间分配逻辑理解不一致——尤其是普通冗余和故障组机制下,“物理剩余空间”和“实际可写入空间”根本是两码事。
这篇文章我想把Oracle ASM磁盘组的空间分配模型、冗余机制和动手计算方法完整梳理一遍。不管是刚接触ASM的初学者,还是已经被ORA-15041坑过的运维老手,只要你需要规划磁盘组、扩容数据盘、判断“到底还能不能加文件”,这篇内容应该都能给你一个比较清晰的参考。我会先讲ASM底层的存储模型,再讲冗余策略对空间的真实影响,然后给出实际可用的估算方法,最后聊几个我在巡检和扩容中反复踩过的坑。
1. 磁盘组空间为什么经常“不够用”:从一次ORA-15041说起
1.1 告警场景
那个现场是这样的:一套双节点集群,DATA磁盘组类型是NORMAL REDUNDANCY,由12块1TB的物理盘组成,两个故障组各6块盘。监控面板上Free_MB还有六七百GB,结果应用在跑一个大批量插入的时候,临时表空间自动扩展直接失败,报错码就是ORA-15041。当时第一反应是磁盘真的满了,但看裸容量确实还有剩余,于是开始怀疑是ASM元数据问题,甚至有人提议去清理归档日志。
后来查了V$ASM_DISKGROUP才发现,Usable_file_MB已经非常接近0。也就是说,ASM视角下的“可用文件空间”早就耗尽,只是物理层面的Free_MB还没归零。这个差异,本质上就是冗余镜像所占用的那部分空间在“作祟”。
1.2 问题本质:自由空间和可写入空间不是一回事
普通冗余下,每一份数据extent都会被ASM复制成两个镜像副本,分别放在不同的故障组里。这意味着当你看到磁盘组有100GB的空闲容量时,ASM并不能把这100GB全部拿去做“一份数据”的存储,它至少要预留一半的容量用来放镜像副本。
所以你真正能往里写入的“有效数据量”,在NORMAL冗余下通常只有物理空闲空间的一半左右,在HIGH冗余下则是1/3。这个比例和文件系统(比如ext4或者XFS)完全不同——文件系统看到100GB剩余就能写100GB,ASM则必须先扣掉镜像复制带来的开销。这也是很多DBA第一次接触ASM时最容易产生认知偏差的地方。
除此之外,ASM的可用空间还受故障组之间的容量平衡影响。如果两个故障组大小不一致,容量较小的故障组会成为写入瓶颈,物理剩余空间再多,真正可用的数据容量也会被“短板”拖住。下面几节我会把AU、Extent、故障组这些概念逐一拆开讲,然后再回到空间计算这件事上。
2. ASM的基本存储单元:AU、Extent和故障组
ASM不是简单地把磁盘格式化成文件系统,它的核心是把多个物理磁盘聚合成一个逻辑存储池,然后再按固定大小的“分配单元”去管理数据。理解这三个概念,是搞懂空间分配的前提。
2.1 AU:分配的基本单位
AU(Allocation Unit)是ASM分配空间的最小单位。在Oracle 11g中,新建磁盘组的默认AU大小是1MB,Oracle 12c及之后版本默认是4MB。AU越大,单个磁盘组能够支撑的存储上限越高,因为ASM的数据区描述能力是有限的;但AU越大,小文件写入时的空间浪费也越明显。
你可以把AU想象成一个大楼里的标准车位,无论停什么车,都必须占一个完整的车位。ASM给任何文件分配空间时,最少给一个AU,文件增长时也是一块块AU叠加。对于数据文件这类连续写入的负载,1MB或4MB的粒度影响有限,但如果你在ASM上存了大量几十KB的小文件,AU的浪费就会变得可观。
查看当前磁盘组的AU大小很简单:
SELECT name, allocation_unit_size, state, type FROM v$asm_diskgroup;2.2 Extent:带冗余的分配单位
文件在ASM里不是直接跟AU打交道的,而是通过“Extent”这个逻辑单位。一个Extent对应一个或多个AU,具体对应关系会随着文件大小增长而变化。早期文件分配的Extent比较小,比如1个Extent等于1个AU;当文件已经分配了一定规模的Extent后,后续的Extent会变成8个AU甚至64个AU。
这种变长Extent设计的目的,是减少大规模文件的元数据数量。如果一个1TB的数据文件全部用1MB的Extent来管理,那就得有上百万条空间映射记录,那是不可接受的;用64MB甚至更大的Extent去覆盖文件尾部的大量空间,元数据量就小得多。
正是因为ASM以Extent为粒度做镜像和管理,而不是像传统RAID那样以整个条带或整块磁盘为粒度,ASM才能做到把一份数据的多个副本灵活分布到不同故障组,从而在故障隔离上获得更大的自由度。
2.3 故障组:镜像放置的逻辑边界
故障组(Failure Group)是ASM里最容易理解错的概念。它不是物理硬盘本身,而是一个“可能同时失效的磁盘集合”。在普通冗余下,一份Extent的镜像绝对不能放在同一个故障组里;在高冗余下,三份副本必须跨三个故障组。
最常见的故障组划分方式是按存储柜或HBA卡来分。比如一台服务器挂了两个磁盘柜,每个柜6块盘,那么你可以把第一个柜的6块盘设成FG1,第二个柜的6块盘设成FG2。这样即使其中一个存储柜整体断电,ASM仍然能通过另一个柜上的副本继续提供服务。
这里有个关键点:故障组不是RAID组,也不是磁盘组内简单的“盘分组”。ASM在做镜像放置时,会优先选择不同故障组内的磁盘,从而保证容灾逻辑是围绕“故障域”而不是“单块磁盘”来设计的。这一点也直接决定了磁盘组的空间利用率——如果两个故障组容量不均衡,低容量的故障组就会限制整个磁盘组的可用空间。
3. 三种冗余策略到底怎么吃空间
Oracle ASM一共支持三种冗余类型:外部冗余、普通冗余、高冗余。它们的目标都是保证数据不因磁盘故障而丢失,但空间代价和逻辑复杂程度差异很大。
3.1 外部冗余:最省空间但不是ASM在做保护
外部冗余(EXTERNAL REDUNDANCY)模式下,ASM不复制任何数据副本,一份数据只写一份,磁盘组的可用空间约等于物理容量总和。但ASM也不会帮你做任何磁盘故障恢复,它的前提假设是“底层存储已经做了RAID保护”。
比如你在存储阵列上做了RAID10,然后把LUN直接交给ASM,这时再让ASM做镜像就是双重浪费。外部冗余很适合这种场景。它的可用率最高、性能开销最小,但代价是ASM本身失去了跨物理盘的故障恢复能力,一旦底层存储真的坏了,ASM只能看着磁盘组脱机。
很多初学ASM的人会踩一个坑:图省事把所有磁盘组都建成外部冗余,却没确认底层存储是否真的具备足够的可靠性。等到底层一块盘故障、数据无法恢复时,再回头怪ASM“不给力”,其实从设计之初就已经埋下了隐患。
3.2 普通冗余:两路镜像的空间账
普通冗余(NORMAL REDUNDANCY)是数据库环境中最常见的类型。每个数据Extent都会保存两份,一份是主副本,一份是镜像副本,且两份副本必须在不同的故障组。这带来两个直接后果:
第一,可用空间大幅缩水。理论上,正常冗余下磁盘组的可用空间约等于物理容量的1/2。注意,这只是理论值,实际还要受故障组容量平衡约束。第二,故障容忍能力提升。只要不是同一份数据的两个副本同时损坏,ASM都能继续工作;即使一个故障组整体脱机,另一个故障组仍保留完整数据。
用生活里的例子来说,普通冗余就像你给重要文件做了两份拷贝,分别存放在两个安全屋。每个安全屋都能独立撑起整套数据,但你实际能存的新文件数量,最多只有两个安全屋总容量的一半。因为每份新文件都要在两个安全屋里各占一份空间。
3.3 高冗余:三副本的适用场景
高冗余(HIGH REDUNDANCY)要求数据有三份副本,分别放在至少三个故障组里,可用空间进一步下降到物理容量的1/3。它是三种模式中容错能力最强的,能容忍两个故障组同时失效。
在大多数OLTP生产环境里,高冗余并不常见,因为它牺牲了太多存储容量。通常只有在跨机房、跨存储阵列、且对RPO要求极高的场景下才会考虑。而且要注意,ASM对高冗余的故障组数量有硬性要求:至少三个故障组。有些人在两个故障组里硬建HIGH磁盘组,直接报错。
3.4 冗余与容错能力的对照
下面这个表可以比较直观地展示三种冗余的差异:
| 冗余类型 | 数据副本数 | 最少故障组数 | 理论可用空间 | 容错能力 |
|---|---|---|---|---|
| 外部冗余 | 1 | 1 | 约等于物理容量 | 不能容忍ASM盘故障 |
| 普通冗余 | 2 | 2 | 约等于物理容量的一半 | 容忍一个故障组失效 |
| 高冗余 | 3 | 3 | 约等于物理容量的1/3 | 容忍两个故障组失效 |
需要强调,“理论可用空间”只是估算。实际生产环境里,空间使用率不会真的顶到50%或33%,因为磁盘组还需要留出一部分余量给日常写入、临时排序和rebalance操作。
4. 磁盘组可用空间的计算与常见误区
很多人问我:到底怎么看磁盘组还剩多少空间?最直接的方法是看V$ASM_DISKGROUP里的Usable_file_MB字段,它已经考虑了冗余因子,表达的是“当前还能容纳多少份有效数据”。也可以用asmcmd lsdg查看,输出里同样有Usable_file_MB列。
4.1 利用V$ASM_DISKGROUP和asmcmd算真正可用空间
下面的SQL可以快速看到每个磁盘组的冗余类型、物理总容量、物理剩余空间,以及带冗余之后的有效可用空间:
SELECT name, type, total_mb, free_mb, usable_file_mb, offline_disks FROM v$asm_diskgroup;如果你习惯于命令行,进入ASM实例后执行:
asmcmd lsdg输出结果里有几个字段需要重点看。Free_MB是物理层面的剩余空间;Usable_file_MB是考虑了冗余镜像后的可写入空间;两者的关系大致是:Usable_file_MB约等于Free_MB除以镜像份数。外部冗余因为是单副本,基本相等;普通冗余约等于1/2;高冗余约等于1/3。
这也是那个生产现场的关键所在:Free_MB看起来还有六七百GB,但除以2之后,有效可用空间只剩三四百GB,再扣掉ASM预留和PTF开销,实际已接近临界值,大事务一冲就撞上ORA-15041。
4.2 一个手工估算的思路
如果你在规划阶段还没有创建磁盘组,可以用一个估算思路来粗算可用空间。假设物理磁盘总容量为T,冗余镜像份数为R,故障组数量为N,那么在故障组容量完全均衡的理想情况下:
可用空间 ≈ T / R但在故障组容量不均时,需要进一步取短板。比如普通冗余下有两个故障组,容量分别为A和B,假设A < B,那么可靠可用空间更接近A × 2还是A,取决于你如何评估容灾目标。如果要求任意一个故障组失效后数据不丢、服务不停,那么有效可用容量上限其实会被较小的故障组卡住,因为所有镜像对的一半必须落在小故障组里。
这也就是ASM空间规划中“木桶效应”的由来。举例:故障组1有5TB,故障组2有4TB,普通冗余下理论总物理容量9TB,但真正能安全写入的数据量更接近4TB左右,而不是9/2=4.5TB。多出来的那0.5TB在故障组失效时是救不了命的。
4.3 磁盘大小不均为什么特别吃亏
很多扩容动作,都是在磁盘组里“贴一块盘”。比如原来12块盘都是1TB,最近空间紧张,加了一块2TB的盘进故障组1。结果你会发现,磁盘组物理容量增加了,但可用空间并没有等比例增加,因为故障组2的总容量没变,镜像的“短板”反而被拉得更明显。
所以我一直建议,ASM磁盘组内的磁盘大小和故障组容量一定要尽量保持一致。如果实在做不到,至少要保证各故障组的总容量在同一水平线上。否则,你花真金白银买的容量,很大一部分会被冗余机制变成永远用不上的“镜像死角”。
4.4 rebalance和故障恢复还要占空间
还有一个容易被忽略的空间消耗者:rebalance。当你往磁盘组里增加磁盘、移除磁盘,或者修改磁盘组的冗余属性时,ASM会触发重新平衡操作,把现有数据extent重新分布到所有磁盘上。这个过程中的数据搬移不是瞬间完成的,ASM需要额外的空间作为“中转站”。
如果磁盘组的可用空间已经非常紧张,rebalance可能根本跑不动,甚至失败。这就陷入了一个恶性循环:空间不足所以扩容,扩容触发rebalance,rebalance又因为空间不足而卡住。
因此,日常使用中我建议把磁盘组的空间水位控制在有效容量的75%以内,最多不超过80%。留出的这部分空间,既为正常的写入抖动提供了缓冲,也为rebalance和故障副本重建留了余地。很多ORA-15041并不是“完全满了”才触发,而是在水位过高、ASM判断没有足够安全余量时就拒绝分配了。
5. 扩容、建组与日常监控的实操建议
说了这么多原理,最后落回实战。下面是我在规划磁盘组、扩容以及日常巡检中比较常用的一些做法,供参考。
5.1 创建磁盘组时的冗余规划
新建磁盘组时,先想清楚底层存储已经提供了什么级别的保护。如果底层是RAID10或RAID5,并且你能够接受“整台存储柜故障”这种极端情况,通常会选外部冗余加故障组规划;如果底层就是普通磁盘,没有额外RAID保护,那就必须用普通冗余或高冗余。
创建普通冗余磁盘组时,至少要规划两个故障组,并且两个故障组的总容量要接近。一个比较稳的创建方式是在SQL里显式指定故障组:
CREATE DISKGROUP data NEW NORMAL REDUNDANCY FAILGROUP fg1 DISK '/dev/asm/data01' NAME data01, '/dev/asm/data02' NAME data02, '/dev/asm/data03' NAME data03 FAILGROUP fg2 DISK '/dev/asm/data04' NAME data04, '/dev/asm/data05' NAME data05, '/dev/asm/data06' NAME data06;创建后,立即用asmcmd lsdg确认每个故障组的容量是否均衡。如果发现某一边明显偏大或偏小,趁数据还少、rebalance成本低的时候及时调整,远比日后迁移划算。
5.2 扩容前先算Three-way空间账
扩容时,不要只看“加了几块盘、物理空间增加了多少”。我习惯这样算账:先记录当前磁盘组的Usable_file_MB,再估算新盘加入后各故障组的总容量变化,最后按镜像份数折算有效增加量。
举个例子。一个NORMAL冗余磁盘组,FG1当前5TB、FG2当前5TB,接近满载。现在只有一块2TB盘可以加。如果加到FG1,FG1变成7TB,FG2还是5TB,短板不变,有效可用容量不会有实质提升。正确做法是至少同时为FG1和FG2各增加等量容量,或者两块盘分别放进两个故障组,才能让有效空间真正增加。
这个计算并不复杂,但很多扩容事故恰恰是因为只看了物理容量增长,没有站在“木桶效应”和镜像开销的角度去思考。
5.3 日常监控里我习惯盯的几个指标
除了常规的Total_MB、Free_MB,我会额外盯三个指标。
第一个是Usable_file_MB,这是带冗余后的有效剩余空间,比Free_MB更能反映真实容量压力。第二个是Offline_disks,只要有磁盘处于离线状态,整个磁盘组的可用空间会因为缺少冗余副本而进一步下降。第三个是REBALANCE状态,如果长时间处于RUNNING,要关注是不是空间不足导致的数据搬移停滞。
再补充一个小小的经验:每当磁盘组可用空间接近20%时,我就会主动检查是否有无效RMAN备份、过期归档、超大日志文件等可以清理的对象。与其等到ORA-15041打爆应用,不如让空间水位始终处于可控区间。
最后说一点个人的体会。ASM空间管理从原理上看并不复杂,真正容易出问题的地方,往往是你把一个“文件系统思维”带到了ASM里。文件系统里剩余空间就是可写空间,ASM里还要叠加冗余、故障组、rebalance这些变量。只要你在建组时把故障组容量规划均衡、平时监控带冗余后的有效可用空间、扩容前先算清楚短板在哪,绝大多数空间相关的坑都是可以提前避开的。遇到ORA-15041也别急着清数据,先看一眼Usable_file_MB和故障组分布,答案通常就在那里。