作者:任仲禹,爱可生数据库工程师,OBCE/OceanBase 2025 年度布道师。
爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
本文约 2900 字,预计阅读需要 10 分钟。
1. 背景
某日 19 点左右,收到客户业务反馈:某 OceanBase 业务集群不可用,OCP 中该集群显示为异常状态,需要紧急处理。
环境信息
环境为 3 节点 1-1-1 架构,421 版本集群:
-
OBServer1:88.88.88.32 -
OBServer2:88.88.88.33 -
OBServer3:88.88.88.34
2. 应急处理过程
为保证尽快恢复业务,略过根因分析,尝试重启集群。故障期间 OCP 平台中该集群处于异常状态:
-
节点 32 OBServer1 正常运行 -
节点 33 OBServer1 异常 -
节点 34 OBServer1 异常
2.1 启动 OBServer2
分析 OBServer2 时,发现节点 33 的 OBServer 进程已经不存在,所以选择直接启动。
-
进程不存在的原因搁置,后文进行分析
# 切换到 admin 用户
su - admin
# 查出 OBServer 进程号
ps -ef | grep OBServer
# 启动 OBServer 进程
cd /home/admin/oceanbase/
/home/admin/oceanbase/bin/OBServer
# 查出 OBServer 进程
ps -ef | grep OBServer
2.2 启动 OBServer3
处理 OBServer3 时,发现节点 34 的 OBServer 进程已经不存在,所以选择直接启动。
-
进程不存在的原因搁置,后文进行分析
# 切换到 admin 用户
su - admin
# 查出 OBServer 进程号
ps -ef | grep OBServer
# 启动 OBServer 进程
cd /home/admin/oceanbase/
/home/admin/oceanbase/bin/OBServer
# 查出 OBServer 进程
ps -ef | grep OBServer
2.3 检查集群是否正常
OBServer 节点全部启动后,通过元数据视图进行状态检查。
# 登录集群的 sys 租户
obclient -h88.88.88.32 -P2883 -uroot@sys#obdemo -pxxxxxxxxxxx -A
# 执行如下命令
obclient> select gmt_modified,svr_ip,status,stop_time,start_service_time from __all_server;
+----------------------------+-------------+--------+-----------+--------------------+
| gmt_modified | svr_ip | status | stop_time | start_service_time |
+----------------------------+-------------+--------+-----------+--------------------+
| 2025-05-07 07:39:56.581325 | 88.88.88.32 | active | 0 | 1746574794656340 |
| 2025-05-07 07:41:29.154517 | 88.88.88.33 | active | 0 | 1746574886937797 |
| 2025-05-07 07:40:08.583806 | 88.88.88.34 | active | 0 | 1746574807774666 |
+----------------------------+-------------+--------+-----------+--------------------+
3 rows in set (0.01 sec)
确保如下字段的值全部满足必要条件,即表示 OBServer 节点为正常运行。
-
Status:节点状态;必须为 active -
Stop_time:节点停止时间;必须为 0 -
Start_service_time:节点服务运行时间:必须不为 0
3. 故障原因排查
3.1 节点 33 故障原因分析
3.1.1 检查 OBServer 日志
登录到节点 33,进入 /home/admin/oceanbase/log/ 目录,查询 OBServer.log 日志进行分析。
得到的线索如下:
-
07:37 分手工处置之前,2025年4月21日12:23:00 该 33 节点已停止打印日志,此时数据库进程已经不在。 -
日志有显示: clog disk may be hang or something error has happen!即数据库写 redo 日志所在的磁盘出现 hang 的情况或其它错误。
3.1.2 检查 OS-message 日志
根据线索,继续排查操作系统的 message 日志。
部分关键 message 日志如下:
Apr 21 12:23:00 ob2 systemd[1]: [114136712.106915] Stopped Journal Service.
Apr 21 12:23:00 ob2 systemd[1]: [114136712.108908] Starting Journal Service...
Apr 21 12:23:00 ob2 systemd[1]: [114136802.198799] Failed to start Journal Service.
Apr 2112:23:00 ob2 systemd[1]: [114136811.949713] Stopped Network Service.
Apr 2112:23:00 ob2 systemd[1]: [114136811.951961] Starting Network Service...
Apr 2112:23:00 ob2 systemd[1]: [114136902.197882] systemd-networkd.service: start operation timed out. Terminating.
Apr 2112:23:00 ob2 systemd[1]: [114136902.197882] systemd-networkd.service: start operation timed out. Terminating.
Apr 2112:23:00 ob2 kernel: [114137610.805255] smartpqi 0000:5e:00.0: resetof scsi 15:1:0:0: SUCCESS
Apr 2112:23:00 ob2 kernel: [114137610.846264] sd 15:1:0:0: timing out command, waited 180s
Apr 2112:23:00 ob2 kernel: [114137610.846273] sd 15:1:0:0: [sda] tag#889 FAILED Result: hostbyte=DID_IMM_RETRY driverbyte=DRIVER_OK
Apr 2112:23:00 ob2 kernel: [114137610.846276] sd 15:1:0:0: [sda] tag#889 CDB: Read(16) 88 00 00 00 00 00 00 54 de c0 00 00 00 08 00 00
Apr 2112:23:00 ob2 kernel: [114137610.846279] print_req_error: 96 callbacks suppressed
Apr 2112:23:00 ob2 kernel: [114137610.846281] print_req_error: I/O error, dev sda, sector 5562048
Apr 2112:23:00 ob2 kernel: [114137610.846286] Read-erroron swap-device (253:1:3052224)
Apr 2112:23:00 ob2 kernel: [114137610.846444] print_req_error: I/O error, dev sda, sector 632512664
Apr 2112:23:00 ob2 kernel: [114137610.846499] print_req_error: I/O error, dev sda, sector 805802552
Apr 2112:23:00 ob2 kernel: [114137610.846523] sd 15:1:0:0: timing out command, waited 180s
Apr 2112:23:00 ob2 kernel: [114137610.846499] print_req_error: I/O error, dev sda, sector 805802552
Apr 2112:23:00 ob2 kernel: [114137610.846523] sd 15:1:0:0: timing out command, waited 180s
Apr 2112:23:00 ob2 kernel: [114137610.846594] print_req_error: I/O error, dev sda, sector 632524944
Apr 2112:23:00 ob2 kernel: [114137610.846613] sd 15:1:0:0: timing out command, waited 180s
得到的线索如下:
1. 节点 33 的操作系统日志,在 12:23:00 确实有涉及 os-kernel 的信息输出。
Apr 21 12:23:00 ob2 kernel: [114136267.896464] smartpqi 0000:5e:00.0: resetting scsi 15:1:0:0
-
根据日志信息,smartpqi 驱动在尝试重置 SCSI 总线时触发了磁盘离线问题,可能涉及驱动程序与硬件的兼容性或稳定性问题。
Apr 21 12:23:00 ob2 kernel: [114137610.846444] print_req_error: I/O error, dev sda, sector 632512664
Apr 21 12:23:00 ob2 kernel: [114137610.846499] print_req_error: I/O error, dev sda, sector 805802552
-
根据信息,操作系统在访问磁盘时发生了 输入/输出(I/O)错误,具体是设备 /dev/sda 的xx 扇区 无法正常读写;猜测是磁盘坏块/不可用导致。
2. 磁盘 I/O 错误期间,部分系统重要服务也被异常停止,例如日志服务和网络服务。
Apr 21 12:23:00 ob2 systemd[1]: [114136712.106915] Stopped Journal Service.
Apr 21 12:23:00 ob2 systemd[1]: [114136712.108908] Starting Journal Service...
Apr 21 12:23:00 ob2 systemd[1]: [114136802.198799] Failed to start Journal Service.
Apr 21 12:23:00 ob2 systemd[1]: [114136811.949713] Stopped Network Service.
Apr 21 12:23:00 ob2 systemd[1]: [114136811.951961] Starting Network Service...
3. 将问题报与硬件工程师沟通
-
经确认,当时该节点数据盘确实坏了 1 块,后续已经修好。
3.2 节点34故障原因分析
3.2.1 检查 OBServer 日志
登录到节点 34,进入 /home/admin/oceanbase/log/ 目录,查询 OBServer.log 日志进行分析。
得到线索如下:
-
07:39 分手工处置之前,2025年5月07日04:55:42 该 34 节点已停止打印日志,此时数据库进程不在。
3.2.2 检查 OS-message 日志
继续排查操作系统的 message 日志。
能看到,在5月7日04:55:42秒左右,节点 34 的 OBServer 进程被 Linux-OOM-killer 杀死。
-
查看节点34 的物理内存大小。
为什么 OOM-Killer 要杀死 OBServer 进程?
OOM Killer 是 Linux 内核设计的一种机制,在内存不足时选择一个进程杀死,释放内存来满足内存分配的的需求,避免系统 crash。
通常我们要求 OBServer 服务器为 OceanBase 数据库独占,不建议同时在 OBServer 服务器上部署其他应用。当 OBServer 其他应用共享服务器时,如果操作系统发生 OOM(Out-of-Memory),因为 OBServer 进程使用的内存较多,操作系统很可能杀掉 OBServer 进程来释放内存。
3.2.3 当时 Linux 的可用内存多少?OBServer 用了多少?
1. message 日志有打印,OBServer 进程当前用的实际物理内存是 187603704 kB,即 178G。
-
rss 表示进程的 驻留集大小,即当前实际驻留在物理内存(RAM)中的内存量,单位为 KB。
May 7 04:55:42 ob3 kernel: [115486651.142354] Killed process 3391142 (OBServer) total-vm:216443928kB, anon-rss:187603704kB, file-rss:87512kB, shmem-rss:0kB
2. OBServer 用的内存符合预期吗?
该集群配置的内存使用上限参数是 80; 80% * 251(机器总内存)= 200G。
-
实际 OBServer 使用 178G 符合预期的(没超过 OB 的最大可用内存上限 200G)。
根据监控也能看到,当时 Linux 实际 used 内存为 188G 左右。
3. 当时 Linux 可用内存多少?
根据 message 日志与 tsar 监控,故障时节点 34 的操作系统实际使用 188.2G 内存,有 16470296 个缓存页可用。
-
16470296 * 4kb(Linux 默认页大小) = 62.8G -
根据监控:62.8G 缓冲 + 188G USED = 251 【Linux 总内存】 根据 Linux 操作系统原理,缓冲内存也是实际可用内存。但是为何还是发生了 OOM-killer?
3.2.4 简单浅析 Linux OOM Kill 触发机制与缓存页的联系
1. 缓存页的可回收性
Page Cache 缓存页本质上是可回收的,但回收过程需要时间(如同步脏数据到磁盘)。当系统物理内存不足时,内核优先触发缓存回收,但遇到以下场景会导致回收效率不足:
-
脏页比例过高:需写入磁盘后才能释放内存,若 I/O 性能瓶颈导致回收延迟,无法满足进程的实时内存需求。 -
缓存页被锁定:某些场景(如文件内存映射 mmap)会锁定部分缓存页,导致无法立即回收。
2. OOM 触发条件
OOM Killer 的激活取决于实时可用物理内存量而非缓存总量:
-
当进程申请内存时,若物理内存不足且缓存回收速度无法跟上需求,直接触发 OOM。 -
即使缓存总量大,若短时间内进程集中申请大量物理内存(如 malloc + memset 强制分配),可能绕过缓存回收直接耗尽内存。
4. 结论
4.1 集群异常导致业务不可用的原因
根据故障处理时间线:
-
4月21日12点23分节点 33 就已 crash 了,有生成 coredump 文件。 -
5月7日04点55分节点 34 crash,此时不满足多数派,集群不可用。 -
5月7日早上7点左右应急处理:手工启动 33、34,重启 32。
原因可以明晰:
-
7号04点55分,集群中 3 节点,连续挂了 2 个,不满足 OB 多数派,集群异常符合预期。
4.2 两个节点陆续宕机的原因
4.2.1 节点 33 宕机原因
根据上文分析,该 33 节点的 OBServer 进程宕机是因为数据磁盘坏块导致,经过与硬件工程师确认,故障时刻,数据盘确实坏了 1 块。
4.2.2 节点 34 宕机原因
根据上文分析,该 34 节点 OB 宕机原因是所在 Linux 物理机实际物理内存耗尽,导致物理内存不足且缓存【脏页】回收速度无法跟上需求,直接触发 OOM。
如何规避该问题?
内存的缓存页实际存储的是操作系统的 “脏数据”,我们可以调整脏数据的占比在当前场景来规避该问题。
# 添加到 /etc/sysctl.conf
vm.dirty_background_ratio = 10 # 设置脏页占比阈值至 10% ; 即内存可以填充“脏数据”的百分比。这些“脏数据”在稍后是会写入磁盘的,pdflush/flush/kdmflush这些后台进程会稍后清理脏数据。举一个例子,我有250G内存,那么有 25G 的内存可以待着内存里,超过 25 G的话就会有后来进程来清理它。
vm.dirty_ratio = 20 # 设置脏数据的硬限制为 20%,内存里的脏数据百分比不能超过这个值。如果脏数据超过这个数量,新的IO请求将会被阻挡,直到脏数据被写进磁盘。这是保证内存中不会存在过量脏数据的保护机制。
sysctl -p
本文关键字:#OceanBase #OOM #
✨ Github:https://github.com/actiontech/sqle
📚 文档:https://actiontech.github.io/sqle-docs/
💻 官网:https://opensource.actionsky.com/sqle/
👥 微信群:请添加小助手加入 ActionOpenSource
🔗 商业支持:https://www.actionsky.com/sqle

