1. 三个方案被混为一谈的根源:它们保护的压根不是同一个东西
几年前参与过一次架构评审,会上有人提出一个问题:既然已经准备上 RAC 了,那双机热备是不是就可以省掉?当时现场没有人立刻反驳,因为在那套方案的汇报材料里,"高可用"三个字被反复提及,RAC、双机热备(HA)、Data Guard(DG)被并列写在"容灾方案"那一页,看上去像是三种实现程度不同的选项,RAC 最高级、HA 次之、DG 是补充。这个认知偏差非常普遍,也非常危险,因为它会直接导致资源规划、故障演练和应急预案全部跑偏。
真实情况是,Oracle RAC、双机热备(HA)、Data Guard 这三种方案,解决的是三层完全不同的问题:HA 保护的是服务器节点,RAC 解决的是实例层面的横向扩展与实例级故障,DG 保护的是数据本身和机房级灾难。它们之间有交集,但绝不存在"A 比 B 高级所以可以替代"的关系。你可以只用 DG 不用 RAC,也可以 RAC 与 DG 叠加,但你不能指望 RAC 帮你扛住存储阵列整体掉电,也不能指望双机热备在数据被误删时把库恢复到一小时前。
这篇内容面向的读者很明确:正在做数据库高可用选型的技术负责人、被要求"讲清楚为什么不能只上 HA"的 DBA、以及准备从单实例往集群架构迁移的实施人员。我会把三者的故障恢复路径拆开讲,把 Cache Fusion、redo 传输、心跳脑裂这些容易含糊过去的关键机制讲透,最后给出一张能直接拿去开会的选型对照表和几段踩过的坑。文中涉及的案例均做了脱敏处理,只保留技术逻辑。
1.1 从一次存储控制器故障说起,看三种方案的应急路径差异
假设有一套跑了三年的订单库,某天凌晨存储控制器的固件出问题,整台阵列离线二十分钟。这三种架构在同样的故障面前,表现完全不同。
双机热备架构下,主备两台服务器共用这台阵列。主节点发现数据库进程异常或心跳丢失,集群软件开始做资源组切换,虚拟 IP 漂移到备节点,备节点拉起数据库实例。整套动作通常在一到三分钟内完成。但问题在于,数据文件还躺在那台已经离线的阵列上,备节点拉起实例会直接报 I/O 错误,反复重试,永远起不来。也就是说,HA 的切换逻辑是完备的,但它的前提——共享存储可用——被打破了,方案本身没有任何应对手段。这时候能救场的,只有另一份独立的数据副本,也就是 DG 或者备份。
RAC 架构下,情况类似但更糟一点。所有节点都依赖同一份共享存储,阵列掉线意味着全部实例同时失去数据文件访问能力,节点会相继被踢出集群,或者在 I/O 超时后进入 hang 状态。RAC 的高可用特性在这里帮不上忙,因为它的冗余维度是"实例",不是"存储"。要缓解这个风险,得靠 ASM 的冗余模式(normal 或 high)、多路径软件、以及存储层自身的双控双活,但这些都属于存储侧的加固,已经超出了 RAC 的职责范围。
DG 架构下,备库有自己独立的存储,主库侧存储挂掉,备库完全不受影响。此时的操作是确认主库确实不可恢复,然后对备库执行 failover,把它提升为主库,前端连接切过去。RTO 取决于你多久做出判断、切换脚本多快跑完,通常十几分钟到半小时;RPO 取决于保护模式,同步模式下理论上不丢数据,异步模式下会丢掉最后一段尚未传输的 redo。
把这三条路径摆在一起看,结论就很清楚了:HA 和 RAC 都属于"同城同存储"架构,共享同一个数据存储的故障域;DG 属于"跨存储"架构,是唯一能在存储层故障中保住数据可用性的方案。而 RAC 相对 HA 的增量价值,不在故障切换速度上(HA 有时反而更快),而在于它让多个实例同时对外提供服务,把一台机器的计算能力扩展成了多台。
1.2 单实例数据库的"单点"到底分布在哪些部件上
要选型,先得把"单点"这个概念拆细。一套单实例 Oracle 数据库,可能出问题的部件至少有这么几层:
| 故障层 | 具体部件 | HA 能否覆盖 | RAC 能否覆盖 | DG 能否覆盖 |
|---|---|---|---|---|
| 服务器硬件 | 主板、CPU、内存、电源 | 能 | 能 | 间接(需切换) |
| 数据库实例 | 后台进程崩溃、ora-600 | 能(重启或接管) | 能(其他节点继续服务) | 间接(需切换) |
| 共享存储 | 阵列、控制器、光纤链路 | 不能 | 不能 | 能 |
| 数据逻辑错误 | 误 delete、误 drop | 不能 | 不能 | 能(延迟应用备库) |
| 机房级灾难 | 断电、水浸、火灾 | 不能 | 不能 | 能(异地备库) |
| 操作系统 | 内核 panic、文件系统损坏 | 部分能 | 部分能 | 间接 |
这张表最能说明问题:没有任何一个方案能覆盖所有层。HA 和 RAC 的覆盖面高度重叠,都在"节点级";DG 的覆盖面在"存储级和逻辑级";三者叠起来才接近完整。我在实际工作中见过的失败选型,绝大多数都是把某一层的方案当成了万能药。
还有一个常被忽略的点:即使不谈硬件故障,单实例数据库在计划内维护时的表现也是选型要素。给单实例打一个补丁,业务要停三十分钟;给 RAC 打补丁可以用滚动方式(rolling patch),一个节点一个节点来,业务几乎无感;给 DG 做 Switchover,通常是分钟级的中断或者配合应用重连做到接近无感。很多团队最后是被"每年几十次补丁窗口"逼着上 RAC 的,而不是被故障逼的。这一点在做成本测算的时候必须算进去,因为它是持续发生的成本,不是一次性风险。
2. 双机热备的真实工作方式:心跳、资源组与那个绕不开的脑裂
双机热备这个词在不同圈子里指代的东西略有差别,但在数据库语境下,它几乎总是指这样一套结构:两台服务器,一套共享存储(也可能是主备复制存储),一套集群管理软件负责监控和切换,前端通过一个虚拟 IP 访问数据库。这套结构的学名叫 High Availability Cluster,日常简称为 HA,所以很多文档里"HA"和"双机热备"是混用的。
这里必须提醒一点:严格意义上的高可用(High Availability)是一个目标,不是一种技术。RAC 也能叫高可用架构,DG 也能叫高可用架构。当你看到某份材料里写"我们采用 HA 方案"时,一定要追问一句"具体是哪种实现",否则后面所有的讨论都是空的。这篇文章里,我沿用行业习惯,把"基于集群软件的双机接管方案"称作 HA 或双机热备。
2.1 虚拟 IP 漂移与资源组接管,切换时到底发生了什么
很多人以为 HA 切换是"备用服务器启动数据库",这个描述太粗了。真实过程是一系列有严格顺序的资源操作,任何一步失败都会导致切换失败或者切换后服务不可用。
以常见的集群管理软件为例,一套数据库资源通常被组织成一个资源组,里面包含:文件系统挂载点、数据库监听、数据库实例、虚拟 IP(VIP),可能还有应用服务。切换时的动作序列大致是:
- 备节点检测到主节点心跳丢失,或者主节点上的资源监控脚本报告实例异常。
- 集群在备节点上执行资源的启动流程,顺序是先挂载文件系统,再启动 VIP,然后启动监听,最后启动数据库实例。
- 启动实例时通常需要做 crash recovery,因为主节点是非正常宕机,redo 需要回放,这个阶段可能持续几十秒到几分钟。
- 全部资源启动成功后,集群把状态标记为 online,并通知上层(如果有应用集群的话)。
关键在于第 2 步的顺序和依赖关系。如果文件系统没挂上就启动数据库,实例会因为找不到控制文件而失败;如果 VIP 没起来就启动监听,监听会绑定到物理 IP 上,客户端连过来照样不通。所以集群配置文件里的资源依赖关系必须写对,这不是可以随手改的地方。
我在一套两节点环境里踩过一次坑:运维同事为了"加快切换速度",把实例启动的等待超时从默认的 300 秒改成了 60 秒。结果在一次真实的节点宕机中,crash recovery 因为 undo 表空间较大跑了将近 90 秒,集群判定启动失败,开始反复重启实例,形成了循环。切换超时这个参数,永远要按"最坏情况下 redo 回放需要多久"来设,宁可设大,不要设小。一个粗略的估算方法是:用最近一次全库备份后的归档日志总量除以每秒的平均恢复速率,再乘 1.5 的安全系数。
另外,VIP 漂移这件事对应用侧也有要求。客户端连接池里缓存的还是旧 IP 的连接,切换后这些连接全部失效,应用必须有重连机制。有些团队为了省事,让应用连物理 IP 而不是 VIP,那 HA 就白做了——物理 IP 不会漂移,切换后应用根本感知不到新节点。HA 方案能否真正生效,一半取决于集群配置,一半取决于应用侧是否正确使用 VIP 和重连。
2.2 脑裂是双机热备最致命的坑,没有之一
脑裂(split-brain)指的是两个节点互相认为对方已经死掉,于是各自启动资源、各自挂载文件系统、各自启动数据库。如果共享存储被两边同时挂载并写入,数据文件的损坏几乎是必然的,而且这种损坏往往要等到几天后才被业务发现,到那时备份可能已经被覆盖。
这个问题的危险之处在于,触发条件看起来都很"合理":心跳网线被误拔、交换机做维护、机房网络抖动几秒钟、甚至系统负载过高导致心跳进程没被调度。任何一种情况都可能让节点误判。
主流方案提供的防护手段有三种:
- 心跳冗余:至少两条独立的心跳链路,走不同的物理网卡和不同的交换机。这是最基本的,单心跳的配置在生产环境基本等于没做防护。
- 仲裁设备(quorum):引入第三方的仲裁盘或仲裁节点,网络分割时由仲裁方决定哪一边"存活"。两节点集群如果没有仲裁,理论上无法在脑裂时做出正确判断。
- 隔离机制(fencing/STONITH):检测到冲突时,直接对其中一个节点执行断电或者强制重启,物理上确保它不会写共享存储。这个机制听起来粗暴,但它是最可靠的。
我个人倾向的配置原则是:心跳冗余 + 仲裁 + 隔离,三样至少要有两样,而且隔离机制必须实测过。很多方案在文档里写了 STONITH,但实际环境里因为缺少可控电源或者 IPMI 配置不通,从来没生效过。演练的时候一定要真的拔一次心跳线,看看系统会不会按预期隔离,这个测试做一次比看十份文档都管用。
还有一类隐蔽的问题值得一提:某些集群软件在检测到心跳丢失后,会先尝试"软隔离",也就是杀掉对方节点上的数据库进程,如果软隔离失败再走硬隔离。软隔离阶段如果共享存储的连接还在,有可能出现短暂的双方同时可写窗口。这个窗口有多长,取决于集群版本和配置,在关键业务系统里建议直接关掉软隔离,只保留硬隔离。
3. Oracle RAC 不是"加强版双机热备",Cache Fusion 才是它的立身之本
把 RAC 理解成"能同时跑两个实例的双机热备",是第二个高频误解。这个理解之所以流行,是因为从表面看确实很像:两台机器、一套共享存储、一套集群管理,故障了照样能切换。但如果只是这样,RAC 的价值就只剩下"切换时不需要重新挂载文件系统"这一点点优化,完全不值它带来的复杂度和授权成本。
RAC 真正的价值在另外两件事上:所有节点同时对外提供服务,以及实例之间的数据块可以直接在内存里传递。前者带来的是横向扩展和负载均衡,后者带来的是多实例并发访问同一份数据时的性能保障。这两件事共同构成了 RAC 的技术门槛,也构成了它的成本。
3.1 实例与数据文件分离,RAC 的架构长什么样
RAC 的基础是一句话:多个实例,共享一份数据文件。每个节点上跑一个独立的实例,有自己的 SGA(内存结构)和一整套后台进程,但它们操作的是同一批数据文件、同一份控制文件、同一份 redo 日志组。这个"共享"是靠集群文件系统或者 ASM 实现的,所有节点都能并发访问这些文件。
支撑这套架构的组件比单实例多了不少,必须心里有数:
- 集群件(Clusterware):负责节点成员管理、心跳检测、资源管理。它比普通 HA 用的集群软件复杂得多,因为它还要管理 VIP、SCAN、实例等多个资源类型。
- OCR 和 Voting Disk:OCR 存集群配置信息, Voting Disk 用来做节点存活仲裁。这两个东西必须是多副本的,通常建议放在独立的 ASM 磁盘组上,normal 冗余至少三份。
- 公共网络(Public Network):客户端连接走这个网。RAC 会把 VIP 和 SCAN IP 分配在这上面,客户端通过 SCAN 连过来,由 SCAN 监听把连接分发到某个节点。
- 私有网络(Private Network / Interconnect):节点之间的数据交换全走这里,Cache Fusion 的流量、集群心跳、GES 队列同步都在这个网段上。
- GCS 与 GES:全局缓存服务负责管理数据块在各节点 SGA 中的分布和传递,全局队列服务负责管理跨实例的锁(比如行锁、library cache lock)。这两个是 RAC 内部最核心的服务。
我特别想强调私有网络的重要性,因为它是 RAC 部署中最容易被"凑合"的部分。有些实施为了省钱,让私有网络和公共网络共用一块网卡,或者用一块千兆网卡跑 Cache Fusion。这在压力不大的测试环境可能看不出问题,但上了生产,一旦出现跨节点的大量数据块请求,私有网络瞬间打满,表现为"实例间通信延迟高、gc 相关等待事件飙升、业务响应时间断崖式下降"。生产环境的私有网络,我建议至少万兆起步、独立网卡、独立交换机、并且做 bonding 冗余,不要和公共网络混跑,也不要为了省事少配一条链路。
3.2 Cache Fusion 如何把"内存里的脏块"从一个节点搬到另一个节点
理解 Cache Fusion,是理解 RAC 性能特征的关键。
在单实例里,一个数据块被修改后留在 buffer cache 里,标记为脏块,等 DBWn 进程择机刷盘。整个过程都在一台机器内部完成,没有外部交互。到了 RAC 环境,节点 A 修改了数据块 X,这个块在节点 A 的内存里是脏的;此时节点 B 要读数据块 X。如果按单实例逻辑,节点 B 应该从磁盘读,但磁盘上的是旧数据,因为节点 A 还没刷盘。
Cache Fusion 的作用就是在这一刻:节点 A 通过私有网络,直接把内存里的数据块 X 传给节点 B,节点 B 拿到最新版本,不用去磁盘读,也不用等待节点 A 刷盘。这个传递是块级的、点对点的,延迟主要取决于私有网络的带宽和延迟。
这里有一个关键的性能特征:跨节点的数据块传输,代价远高于本地内存访问,但远低于磁盘 I/O。所以 RAC 应用的性能表现,高度依赖于数据访问的分布模式。如果两个节点访问的是完全不同的数据(比如按租户分片),跨节点流量几乎为零,性能接近线性扩展;如果两个节点频繁访问同一批数据块(比如对同一张热点表做更新),跨节点流量会很大,性能提升反而有限,甚至可能比单实例更慢。
这就带来了 RAC 应用设计的一条铁律:尽量减少跨实例的块争用。常见的做法包括按业务模块或租户分连接、避免多个实例并发更新同一批数据、把序列(sequence)的 cache 设大以减少字典争用、热点表考虑用反向键索引或者哈希分区。这些优化在单实例里完全不需要,在 RAC 里却是必修课。
还有一点:脏块在传输完成后,原节点的状态会变成"被共享",新节点也持有一份副本。如果新节点要修改这个块,需要先向 GCS 申请,GCS 通知原节点把块作废或者降级,这就是一次"块转移"。频繁的块转移会带来大量 gc buffer busy、gc cr block 2-way/3-way 之类的等待事件。看到这些等待事件,说明应用的数据访问模式不适合 RAC,或者私有网络撑不住,得从前端和网络两头查。
4. Data Guard 走的是另一条路:redo 传输与物理/逻辑备库的分野
如果说 RAC 和 HA 的分歧点是"要不要多实例并发",那 DG 跟它们的差别就不是量上的,是维度上的。DG 不看你有几个实例、也不管你服务器怎么切换,它只做一件事:把主库产生的 redo 持续不断地传到另一个完全独立的数据库上,在那台库上重放。
就这么一件事,却解决了 HA 和 RAC 都解决不了的问题:数据有了第二份独立物理副本。这份副本存在一个独立的数据库、独立的存储、独立的服务器、甚至独立的机房里。主库这边发生什么灾难,备库都不受影响。
代价也很直接:备库是"额外的一套",平时不能做写操作(物理备库在 mount 状态,或者配了 Active Data Guard 之后只能读),并且主备之间的数据同步会带来一定的性能开销和网络带宽消耗。
4.1 redo 的三种传输方式与三种保护模式,怎么配才不踩雷
DG 的数据同步链路,本质上是一组配置项的组合:redo 怎么传,乘以什么情况下允许主库停下来等。
传输方式主要有两种:
- LGWR 进程直接传输:主库的 LGWR 在写本地 redo 的同时,把 redo 发给备库。这种方式延迟最低,通常能达到秒级甚至亚秒级的数据同步。
- ARCH 进程传输:等主库归档完成后,由 ARCn 进程把归档日志传给备库。这种方式对主库性能影响最小,但备库会落后一段,落后量取决于归档频率。
LGWR 方式还分同步和异步:同步(SYNC)意味着主库的 LGWR 必须等到备库确认收到 redo,才向应用返回提交成功;异步(ASYNC)则是发出去就不管了,备库收没收到不影响主库提交。
再把保护模式叠加上去,就成了最常见的三档配置:
| 保护模式 | 传输方式 | 主库行为 | RPO | 适用场景 |
|---|---|---|---|---|
| 最大保护(Max Protection) | LGWR SYNC | 备库不可达时,主库挂起(hang),拒绝写入 | 0 | 绝对不能丢数据的核心交易库 |
| 最高可用(Max Availability) | LGWR SYNC | 备库不可达时,自动降级为异步,主库继续服务 | 0 或接近 0 | 绝大多数生产核心库的首选 |
| 最高性能(Max Performance) | LGWR ASYNC 或 ARCH | 完全不等待备库 | 秒级到分钟级 | 报表库、非核心业务、跨地域容灾 |
这里有个坑必须提醒:最大保护模式下,如果备库所在网络断开而你没有及时处理,主库会直接挂起,业务全线中断。我见过不止一次,运维为了满足"数据零丢失"的上线要求选了最大保护,结果某次专线抖动,主库写不进去,业务停了二十分钟才发现原因。相比之下,最高可用模式更务实:正常情况下同步、零丢失,备库不可达时自动降级、业务保住,等备库恢复后自动追平。除非业务方明确接受"宁可停业务也不丢一条数据",否则默认建议最高可用。
4.2 逻辑备库不是物理备库的降级版,它解决的是另一类需求
物理备库是通过块级重放保持与主库完全一致,连数据文件的物理结构都一样,好处是简单、可靠、开销小,坏处是备库和主库是"一根绳上的蚂蚱",主库上执行一个错误的 DDL,备库会原样执行一遍。所谓"防止逻辑错误"的能力,物理备库只能靠延迟应用来实现——让备库比主库落后一小时,发现误操作后,把备库停在错误发生之前的时间点。
逻辑备库(Logical Standby)则不同,它拿到的是 redo 里的 SQL 语句,由 LogMiner 解析成 SQL 后在备库上重新执行。好处是备库可以处于读写状态,可以在上面建额外的索引、额外的物化视图、做一些报表专用的结构;主库上某些表的变更甚至可以不过去。坏处是它支持的数据类型和操作有限制,某些 DDL 不支持、某些数据类型不支持,维护成本明显更高。
所以选逻辑备库的理由通常不是"想要更灵活",而是"我需要备库同时承担报表查询并且要能改表结构"或者"我需要过滤掉某些表的同步"。纯容灾需求选物理备库就够了,别为了灵活性付出不必要的复杂度。
真要解决"报表查询分流"这个需求,物理备库配上只读打开(Active Data Guard)通常更省事,主备结构完全一致,运维简单,报表 SQL 直接在备库跑,主库压力立减。这个方案现在已经是很多团队做读写分离的标准做法了。
5. 把业务指标和团队能力一起算进去:一张能直接拿去开会的选型表
前面几节讲的是机制,这一节讲怎么选。选型从来不是比技术先进程度,而是比"哪种方案的代价和业务需求匹配得最好"。
我通常会让业务方先回答三个问题:能容忍多长的停机时间(RTO)?能容忍丢多少数据(RPO)?每年愿意为高可用付多少钱?这三个问题答不出来,后面所有的技术讨论都是空转。
5.1 RTO、RPO、成本、复杂度四个维度的横向对照
| 维度 | 双机热备(HA) | Oracle RAC | Data Guard |
|---|---|---|---|
| 典型 RTO | 1-5 分钟 | 30 秒-2 分钟(实例故障) | Switchover 分钟级;Failover 取决于人工判断 |
| 典型 RPO | 0(共享存储) | 0(共享存储) | 同步模式 0;异步模式秒级到分钟级 |
| 存储故障保护 | 无 | 无(除非存储层自身双活) | 有 |
| 机房级灾难保护 | 无 | 无 | 有(异地备库) |
| 计算能力扩展 | 无(备机闲置) | 有,最多 8 节点左右 | 只读分流(ADG) |
| 硬件成本 | 2 台服务器 + 1 套共享存储 | 至少 2 台高配服务器 + 高性能共享存储 + 万兆私有网络 | 2 套完整环境(含存储) |
| 软件授权 | 数据库标准版即可 | 需企业版 + RAC 选件,成本显著上升 | 需企业版 + Active Data Guard 选件(如用只读) |
| 运维复杂度 | 低 | 高(集群、ASM、私有网络、跨实例调优) | 中(配置项多,但结构简单) |
| 应用改造要求 | 需使用 VIP + 重连机制 | 需优化数据访问分布,减少跨实例争用 | 切换后需改连接串或依赖 DNS/中间件 |
| 最适场景 | 单库、预算有限、停机容忍分钟级 | 高并发 OLTP、需要横向扩展、补丁窗口要求高 | 核心库容灾、异地多活、需要第二份数据副本 |
这张表最想传达的信息是:HA 和 RAC 在容灾能力上其实是一档的,差别在扩展性和维护窗口;DG 和它们是另一个维度的东西,不构成替代关系。所以真正的选型问题不是"三选一",而是"要不要上 RAC"以及"要不要加 DG"这两个独立决策。
5.2 现实中的组合拳:RAC + DG 与 HA + DG 各自适合谁
单打独斗的方案很少见,生产环境基本都是组合。两种主流组合的适用面差别很大。
HA + DG是预算敏感型项目的常见选择。两台服务器做双机热备,保证节点故障时能快速接管;再加一套 DG 到异地,保证存储故障和机房灾难时有数据。这套组合的优点是成本可控、技术栈简单、DBA 上手快;缺点是计算能力没有扩展,而且 HA 的切换在数据库 crash recovery 阶段可能比较慢。适合中等规模的业务库、内部管理系统、以及那些"并发不高但绝对不能丢"的场景。
RAC + DG是核心交易系统的标配。RAC 负责节点级高可用和计算扩展,滚动补丁让维护窗口几乎消失;DG 负责存储级和机房级容灾。这套组合的代价是复杂度成倍上升:集群、ASM、私有网络、跨实例调优、DG 同步监控,哪一环出问题都是生产事故。所以它对团队的要求很高,没有两三个熟悉 RAC 的 DBA 撑着,不建议轻易上。
还有一个容易被忽略的组合考量:如果已经上了 RAC,本地还需要 DG 吗?我的看法是,本地同城再放一套 DG 的意义不大,因为 RAC 和本地 DG 面对的是同一个城市级故障域。真正有意义的是把 DG 放到异地,形成"本地 RAC + 异地 DG"的两级结构。这个结构能同时覆盖节点故障、存储故障和城市级灾难,是目前比较完整的形态。
反过来,如果预算只能支撑一套方案,而业务对停机极其敏感、对数据丢失的容忍度是"可以丢几秒",那么优先上 DG 往往比上 RAC 更划算——因为 DG 能覆盖的故障类型更多,而实例级的小故障用重启加重连也能扛过去。这个取舍在很多团队里是被忽视的。
6. 部署阶段的实操要点:那些文档里不会写、但一定会遇到的问题
机制讲完,最后落到处。这一节挑两类最容易出问题的场景:RAC 的部署前检查和 DG 的搭建配置。这两块我踩过的坑最多,也最值得提前说。
6.1 RAC 上线前的检查清单,漏一项就够折腾一整天
RAC 部署失败的案例里,绝大多数不是卡在数据库安装那一步,而是卡在安装前的前置条件上。下面这些项,建议做成一个脚本,每次部署前跑一遍。
主机名与解析。每个节点的 /etc/hosts 必须包含:公共 IP、私有 IP、VIP、SCAN IP,四个名字都要能双向解析,而且 SCAN 建议配三个 IP 做负载。SCAN 的解析如果用 DNS,要确保 DNS 服务本身高可用,否则 DNS 挂了整个集群连不上。我一般建议小规模环境直接用 hosts 文件,简单可靠。
时间同步。节点之间的时间偏差必须控制在一分钟以内,最好在毫秒级。集群的心跳和 OCR 对时间敏感,时间漂移会导致节点被误判为故障。用 chrony 或 ntpd 都行,但一定要配好并且在所有节点上验证chronyc sources输出正常。
内核参数。共享内存相关的几个参数必须调:
# 示例:根据物理内存大小调整,具体值需按实际环境计算 kernel.shmmax = 68719476736 # 不小于 SGA 大小,通常设为物理内存的一半 kernel.shmall = 16777216 # shmmax / 页大小 kernel.shmmni = 4096 kernel.sem = 250 32000 100 128 fs.file-max = 6815744 net.ipv4.ip_local_port_range = 9000 65500 net.core.rmem_default = 262144 net.core.rmem_max = 4194304 net.core.wmem_default = 262144 net.core.wmem_max = 1048576这里最容易出问题的是 shmmax 和 shmall 的关系。shmall 是"总共可以分配的共享内存页数",如果只调了 shmmax 没调 shmall,实例启动时会报共享内存分配失败,而且报错信息很不直观。
私有网络的 MTU 一致性。如果打算用巨型帧(jumbo frame,MTU 9000)来提升私有网络吞吐,必须确保两端网卡、交换机端口、以及 VLAN 上所有设备的 MTU 都一致。任何一环还是 1500,就会出现大包被丢弃、小包正常通过的诡异现象——表现为 ping 通但 SSH 卡顿、集群心跳时有时无。这个问题排查起来极其耗时,因为现象跟网络断开的区别很小。我的建议是,除非网络团队明确支持并且全链路已经配置好,否则私有网络先用默认 MTU 1500,稳定之后再考虑优化。
多路径与设备名。如果存储是多路径接入,必须保证每个节点看到的设备名一致,或者用 ASM 的 ASMLIB/udev 规则做绑定。设备名漂移会导致 ASM 磁盘组在某个节点上无法挂载,进而导致实例启动失败。
私有网络的网卡绑定。两个节点的私有网卡建议做 active-backup 或者 LACP 绑定,不要单网卡。绑定之后要确认 bond 的 MTU 和交换机侧一致,并且确认切换时不会丢心跳。
6.2 DG 搭建时最常卡住的五个配置项
DG 的搭建流程文档里写得很清楚,但实际执行时总有几处会卡住,而且报错信息和真实原因的关联度很低。
第一,DB_UNIQUE_NAME 与 LOG_ARCHIVE_CONFIG。主备两边的LOG_ARCHIVE_CONFIG参数里必须同时列出两个库的 unique name,格式是'DG_CONFIG=(主库名,备库名)'。这一项漏了,redo 传过去备库不认,日志里只有一句含糊的 ORA-16191,非常误导。
第二,standby redo log 的组数和大小。常见错误是备库上根本没建 standby redo log,或者建了但组数不够。经验规则是:standby redo log 的组数 = 每个线程的 online redo 组数 + 1,再乘以线程数;大小不小于主库 online redo 的最大值。用 LGWR SYNC 传输时这一项不满足,同步会降级或者直接报错。
-- 在备库上添加 standby redo log 的示例 ALTER DATABASE ADD STANDBY LOGFILE THREAD 1 GROUP 11 ('/u01/app/oracle/oradata/standby/redo11.log') SIZE 512M, GROUP 12 ('/u01/app/oracle/oradata/standby/redo12.log') SIZE 512M, GROUP 13 ('/u01/app/oracle/oradata/standby/redo13.log') SIZE 512M;第三,密码文件。主备的 SYS 密码必须一致,并且密码文件要同步。用 broker 做切换的时候,密码不一致会导致连接失败,而报错只说是认证问题,不会告诉你是密码文件的问题。
第四,FAL_SERVER 和 FAL_CLIENT。这两个参数负责在归档日志出现缺口时自动补齐。配置了之后,备库发现缺日志会自动向主库(或另一个备库)请求。很多搭建教程里没写这两个参数,结果一次网络抖动导致日志没传完整,备库就此停滞,要人工介入才能恢复。
第五,时区和字符集。两边必须完全一致。字符集不一致会导致备库 apply 时报转换错误,时区不一致会影响基于时间的查询和某些系统视图。这两项在创建备库之前就要确认,建完再改基本等于重建。
搭建完成后,别急着宣布完工。至少要做三次演练:一次正常的 Switchover,验证角色转换和回切;一次模拟主库宕机的 Failover,验证应急流程;一次网络中断,验证保护模式下的降级和恢复行为。演练的价值不在于"证明方案能用",而在于把切换脚本里那些没写全的步骤暴露出来。我见过的每一次真实切换,多多少少都和演练时不一样,提前跑过几遍,至少心里有底。
最后说一点个人的体会。做了这些年数据库,越来越觉得高可用这件事的核心不在技术选型,而在故障域的划分是否清晰。RAC 再强也扛不住存储挂掉,HA 再快也救不了误删的表,DG 能保数据但切换要人做决定。真正的靠谱做法,是先把业务能接受的 RTO 和 RPO 问清楚,再回头对照每个方案能覆盖哪一层故障,把没覆盖的那几层用别的方案补上,然后老老实实做演练。方案好不好,不看架构图画得多漂亮,看的是真出事那天,值班的人能不能在十分钟内找到那份已经验证过三遍的操作手册。