当前位置: 首页 > news >正文

YashanDB的errno 24, error message Too many open files故障分析

我们的文章会在微信公众号IT民工的龙马人生和博客网站 ( www.htz.pw )同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢!

由于博客中有大量代码,通过页面浏览效果更佳。


故障摘要

  • 简单描述故障:数据库在凌晨定时任务(如统计信息采集)期间出现 errno 24(Too many open files),无法打开 system 数据文件,触发多节点 FATAL,实例进入异常只读状态。
  • 根本原因:进程打开文件描述符数量超过操作系统或进程级限制(ulimit/RLIMIT_NOFILE),导致后续 open 失败,数据库无法继续写数据文件而异常只读。
  • 处理方案:确认并调高相关进程的 nofile 限制、排查占用文件句柄的进程或配置后,通过 alter database convert to normal; 将库从异常只读恢复为正常模式(无需重启实例),修改进程或者内核的文件句柄限制。

故障详情

故障现象

数据库在 02:00:00 左右集中报错:统计信息采集失败、多个会话报 FATAL,错误信息均为打开 /data/HTZZ/db-1-1/dbfiles/system 失败,errno 24,提示 "Too many open files"。随后 MMON 创建快照失败,报数据库因异常被设置为只读(YAS-06023 / YAS-04015)。

典型日志如下:

2026-02-25 02:00:00.070 3810739 [WARN][errno=02182]: [STATS] an error occurred while collecting database/schema statistics : failed to gather statistics, reason: failed to open file /data/HTZZ/db-1-1/dbfiles/system, errno 24, error message "Too many open files"
2026-02-25 02:00:00.071 3810741 [FATAL][errno=00313]: [DATABASE] Fatal error occurred when load block for write, database need shutdown abort. error massage: failed to open file /data/HTZZ/db-1-1/dbfiles/system, errno 24, error message "Too many open files"
2026-02-25 02:00:00.073 3810737 [FATAL][errno=00313]: [DATABASE] Fatal error occurred when load block for write, database need shutdown abort. error massage: failed to open file /data/HTZZ/db-1-1/dbfiles/system, errno 24, error message "Too many open files"
...
2026-02-25 02:42:13.047 3826270 [ERROR][errno=06023]: [MMON] create snapshot failed : database is set to read-only because of database abnormal
YAS-04015 at line 1
YAS-06023 database is set to read-only because of database abnormal

原因分析

  • errno 24(EMFILE):表示当前进程已达到“可打开文件数”上限(由 RLIMIT_NOFILE 或系统 limits 决定),再调用 open() 会失败。若按文档设置了 limits 仍报错,需确认是否对数据库进程生效(如 systemd 或 limits.conf 是否生效、是否需重启进程或重新登录会话)。
  • 为何在 02:00 爆发:多与定时任务(统计信息采集、归档、快照等)同时打开大量对象或临时文件有关,叠加日常连接与数据文件,总 fd 数突破限制。
  • 只读与 YAS-06023:数据库检测到无法正常写系统文件后,为保护数据一致性会进入异常只读状态,需在排除根因后显式执行恢复命令。

排查时需区分两类统计方式:

  • /proc/PID/fd:只统计进程 fd 表里的文件描述符个数,与 ulimit/EMFILE 直接相关。
  • lsof:除 fd 外还会列出 cwd、rtd、txt、mem 等,同一进程下 lsof 行数 ≥ /proc/PID/fd 条目数;判断是否逼近 nofile 限制应以 fd 数为准。

解决方案

1. 确认进程与系统级文件句柄情况

在故障发生后,可以通过下面方式来查看进程和系统打开的文件数,但是通常故障发生后,数据库异常,对应的打开文件的资源也逐一的释放了,所以就算我们通过如下的方式获得的资源信息是小于故障时刻的。

查看系统整体与按进程统计的打开文件数:

cat /proc/sys/fs/file-nr
lsof -n | awk 'NR>1 {print $2, $1}' | sort | uniq -c | sort -nr | head -n 10
ls -d /proc/[0-9]* 2>/dev/null | while read pid_dir; do echo "$(ls -1 $pid_dir/fd 2>/dev/null | wc -l) $(basename $pid_dir)"; done | sort -nr | head -n 10

查看数据库进程的当前 limit:

prlimit --pid 1096865
pgrep yasdb | xargs -I {} sh -c 'echo "PID: {}"; prlimit --pid {} '

2. 将数据库从异常只读恢复为正常模式(无需重启)

在确认句柄未超过限制后,执行:

alter database convert to normal;

3. 使用脚本持续监控“打开文件数”

可使用如下统计脚本(保存后执行 sh /tmp/open_files_stats.sh):

cat > /tmp/open_files_stats.sh  << 'HTZ'
#!/bin/sh
#
# Count total open files on Linux and list top 10 processes by open file count.
# Usage: ./open_files_stats.sh or sh open_files_stats.sh
#set -eecho "=========================================="
echo "  Linux Open Files Statistics"
echo "=========================================="
echo ""# 0. /proc/sys/fs/file-nr (allocated, free, max)
if [ -r /proc/sys/fs/file-nr ]; thenread -r allocated free_handles max_handles < /proc/sys/fs/file-nrecho "[/proc/sys/fs/file-nr]"echo "  allocated file handles:  $allocated"echo "  free file handles:       $free_handles"echo "  max file handles:        $max_handles"echo ""
fi# 1. Count total open file descriptors (POSIX: no associative arrays)
total=0
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXITfor fd_dir in /proc/[0-9]*/fd; do[ -d "$fd_dir" ] || continuepid=$(basename "$(dirname "$fd_dir")")count=$(find "$fd_dir" -mindepth 1 -maxdepth 1 2>/dev/null | wc -l)total=$((total + count))echo "$count $pid" >> "$tmpfile"
done 2>/dev/nullecho "[Total open files]: $total"
echo ""# 2. Get process name (prefer cmdline, else comm)
get_proc_name() {pid=$1name=""if [ -r "/proc/$pid/cmdline" ]; thenname=$(tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null | head -c 80)name=${name%% *}[ -z "$name" ] && name=$(cat "/proc/$pid/comm" 2>/dev/null) || trueelsename=$(cat "/proc/$pid/comm" 2>/dev/null) || name="[exited]"finame=$(basename "$name" 2>/dev/null)echo "$name"
}# 3. Sort by open file count and take top 10
echo "[Top 10 processes by open file count]"
echo "------------------------------------------"
printf "  %-8s  %-10s  %s\n" "PID" "Open FDs" "Process/Command"
echo "------------------------------------------"sort -rn "$tmpfile" | head -10 | while read count pid; doname=$(get_proc_name "$pid")printf "  %-8s  %-10s  %s\n" "$pid" "$count" "$name"
doneecho "------------------------------------------"
echo ""
echo "Note: file-nr shows kernel-allocated handles; total open files is the sum of"
echo "      fd counts under /proc/<pid>/fd; process names from cmdline/comm (permissions apply)."
echo ""
HTZ

示例输出:

  Linux Open Files Statistics
==========================================[/proc/sys/fs/file-nr]allocated file handles:  2240free file handles:       0max file handles:        3140017[Total open files]: 764[Top 10 processes by open file count]
------------------------------------------PID       Open FDs    Process/Command
------------------------------------------1         72          systemd811526    53          yasdb823637    48          yasdb1096865   45          yasdb702       40          systemd-journald913       33          NetworkManager1190      24          rsyslogd2121      22          systemd1263      22          systemd1095394   21          yasom
------------------------------------------

4.修改资源限制

4.1 动态修改进程nofile资源

Linux提供prlimit命令来动态修改资源限制,操作如下:

pgrep yasdb | xargs -I {} sh -c 'echo "PID: {}"; prlimit --pid {} |grep "NOFILE" ;sudo prlimit --pid {} --nofile=3140004:3140004 ;prlimit --pid {} |grep "NOFILE"'
PID: 512968
NOFILE     max number of open files             1048576   1048576 files
NOFILE     max number of open files             3140004   3140004 files

prlimit 修改的是进程级限制,但它不能超过内核级定义的单个进程允许打开的最大文件数,内核级需要通过sysctl来修改,如下:

sudo sysctl -w fs.nr_open=3140004

4.2 永久修改

永久修改内核nr_open

sudo tee -a /etc/sysctl.d/yashandb.conf <<'HTZ'
fs.nr_open=3140004
HTZ

永久修改用户的限制

sudo tee -a /etc/security/limits.conf <<'HTZ'
yashan soft nofile  3140004
yashan hard nofile  3140004
HTZ

重新登录用户和新启动的进程才会生效。

5. 排查时需区分 /proc/PID/fd 与 lsof

  • /proc/PID/fd:仅表示该进程 fd 表中的条目数,与 EMFILE 直接对应。
  • lsof:还会列出 cwd、rtd、txt、mem 等,不都在 fd 表里,故 lsof 行数 ≥ 该进程 fd 数;判断是否触及 nofile 限制应以 fd 数为准。

故障模拟

为便于在测试环境验证现象与监控手段,可用脚本复现 errno 24(Too many open files)。以下为相关步骤与输出。

目的:在受控环境下让单进程持续占用大量文件描述符,观察系统与统计脚本的结果,并与真实故障时的“打开文件数”现象对照。

环境与前提:Linux 环境,当前进程 RLIMIT_NOFILE 足够大(如 1048576),以便先成功打开指定数量文件并保持不关闭。

步骤 1:创建并运行“占满 fd”的 Python 脚本

脚本如下(可保存为 /tmp/open_files.py/tmp/too_many_open_files.py):

cat > /tmp/open_files.py << 'HTZ'
#!/usr/bin/env python3
"""
Simulate Linux errno 24 (EMFILE) "Too many open files"Usage:python3 too_many_open_files.py           Open until failure, then print errno 24python3 too_many_open_files.py -n N      Open exactly N files and hold; run another program to trigger the error
"""import os
import sys
import errno
import argparsedef main():parser = argparse.ArgumentParser(description="Simulate errno 24 Too many open files")parser.add_argument("-n", "--num-files", type=int, default=None,help="Open exactly N files and hold (so another program can hit the limit)")parser.add_argument("-t", "--hold-seconds", type=int, default=None,help="With -n: hold for SECONDS then exit (no stdin); for scripting")args = parser.parse_args()print("=== Simulate Too many open files (errno 24) ===\n")try:import resourcesoft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)print(f"Process RLIMIT_NOFILE: soft={soft}, hard={hard}\n")except Exception as e:print(f"getrlimit: {e}\n")opened = []if args.num_files is not None:# Mode: open exactly N and holdn = args.num_filesif n <= 0:print("Invalid -n value (must be positive)")sys.exit(1)print(f"Opening {n} file(s) and holding...\n")try:for _ in range(n):fd = os.open("/dev/null", os.O_RDONLY)opened.append(fd)except OSError as e:print(f">>> Error opening file <<<")print(f"errno: {e.errno}")print(f'error message: "{e.strerror}"')for fd in opened:try:os.close(fd)except OSError:passsys.exit(1)print(f"Opened {len(opened)} file(s) and holding.")if args.hold_seconds is not None:print(f"Holding for {args.hold_seconds} seconds then exiting...")import timetime.sleep(args.hold_seconds)else:print("Run your other program in another terminal to trigger 'Too many open files'.")input("Press Enter to release and exit...")for fd in opened:try:os.close(fd)except OSError:passprint("Released and exited.")return# Mode: open until failureprint("Opening until failure...\n")try:while True:fd = os.open("/dev/null", os.O_RDONLY)opened.append(fd)except OSError as e:if e.errno == errno.EMFILE:print(">>> Triggered error <<<")print(f"errno: {e.errno}")print(f'error message: "{e.strerror}"')print(f"Successfully opened count: {len(opened)}")else:print(f"OSError: errno={e.errno}, {e.strerror}")finally:for fd in opened:try:os.close(fd)except OSError:passif __name__ == "__main__":main()
HTZ

运行示例:打开 100000 个文件并保持 10000 秒。

[yashan@htz01 ~]$ python3 /tmp/too_many_open_files.py -n 100000 -t 10000
=== Simulate Too many open files (errno 24) ===Process RLIMIT_NOFILE: soft=1048576, hard=1048576Opening 100000 file(s) and holding...Opened 100000 file(s) and holding.
Holding for 10000 seconds then exiting...

步骤 2:在另一终端用统计脚本观察该进程占用

执行 sh ./open_files_stats.sh 后的输出:

==========================================Linux Open Files Statistics
==========================================[/proc/sys/fs/file-nr]allocated file handles:  101888free file handles:       0max file handles:        3140004[lsof -n] (all processes when root)open file entries:       122396[Top 10 processes by lsof -n]
------------------------------------------PID       Open FDs    Process/Command
------------------------------------------3655806   100031      python3512968    14006       yasdb512927    3102        yascs1071      530         tuned1322      448         polkitd511726    360         yasagent500629    350         multipath1059      333         NetworkMa1412      312         gssproxy511774    180         yasom
------------------------------------------[Total open files]: 100660[Top 10 processes by open file count]
------------------------------------------PID       Open FDs    Process/Command
------------------------------------------3655806   100003      python3512968    93          yasdb1         61          systemd512927    53          yascs1059      33          NetworkManager512716    32          ycsrootagent704       30          systemd-journald1869      22          systemd184703    22          systemd1035      21          systemd-logind
------------------------------------------Note: file-nr = kernel-allocated handles; lsof -n (as root) is closest to that.Total open files below = sum of /proc/<pid>/fd (only processes you can see).

可见 python3 进程(PID 3655806)在 lsof 下约 100031 条、在 /proc 统计下约 100003 个 fd,与“打开 10 万并持有”一致;lsof 略多出的是 cwd、rtd、txt、mem 等非 fd 表项。

步骤 3:查看该进程打开项(排除 /dev/null)

lsof -p 3655806|grep -v /dev/null

输出节选:

COMMAND     PID   USER   FD   TYPE DEVICE SIZE/OFF      NODE NAME
python3 3655806 yashan  cwd    DIR  252,0      141 201714279 /home/yashan
python3 3655806 yashan  rtd    DIR  252,0      236       128 /
python3 3655806 yashan  txt    REG  252,0    69208 134562495 /usr/libexec/platform-python3.6
python3 3655806 yashan  mem    REG  252,0    69464  67147505 /usr/lib64/python3.6/lib-dynload/resource.cpython-36m-aarch64-linux-gnu.so
...
python3 3655806 yashan    0u   CHR  136,0      0t0         3 /dev/pts/0
python3 3655806 yashan    1u   CHR  136,0      0t0         3 /dev/pts/0
python3 3655806 yashan    2u   CHR  136,0      0t0         3 /dev/pts/0

除大量 /dev/null 的 fd 外,lsof 还会显示 cwd、rtd、txt、mem 等,与上文 /proc/PID/fd 与 lsof 的区别一致;判断是否逼近 nofile 限制应以 /proc/PID/fd 或脚本中的“Open FDs”为准。


建议及总结

  • limits 配置:确保数据库及依赖进程(yasdb、yascs、yasagent 等)的 nofile(soft/hard)在 systemd 或 /etc/security/limits.conf 中正确设置并生效(需重启进程或重新登录会话后生效)。
  • 定时任务:统计信息、归档、备份等集中打开大量对象的任务尽量错峰,并监控进程 fd 数,避免与业务高峰叠加后触及限制。
  • 监控与复现:可定期执行 open_files_stats.sh 或类似脚本,观察 Top 进程的 fd 数;需要复现 errno 24 时,可使用上文故障模拟中的 Python 脚本(如 too_many_open_files.py -n N)在测试环境模拟。

本次故障由进程打开文件数超限引发,表现为 errno 24 与数据库异常只读;通过核对 limits、排查高 fd 进程并执行 alter database convert to normal; 即可恢复,无需重启实例。故障模拟表明,用脚本在测试环境复现“大量打开文件”并配合统计脚本,可有效验证监控与理解 /proc 与 lsof 的差异。


推广文案(约 100 字)

数据库凌晨报错 errno 24「Too many open files」,实例进入异常只读?本文从故障日志入手,说明根本原因、如何用 /proc 与 lsof 排查打开文件数,并给出恢复命令与统计脚本;同时包含故障模拟步骤,用 Python 脚本复现 errno 24,便于在测试环境验证。适合 DBA 和运维在遇到 EMFILE、数据库只读时快速定位与恢复。欢迎收藏,在公众号与博客同步更新。

http://www.jsqmd.com/news/425530/

相关文章:

  • 16个高效AI论文写作网站,技巧全解析
  • 深度学习篇---多模态
  • 毕业论文必备:16个AI写作平台及使用攻略
  • 毕业论文神器:16个AI写作工具使用指南
  • 欧拉函数 总结
  • 16大AI论文助手盘点,附详细技巧分享
  • AI Agent在智能浴缸中的水疗模式个性化
  • PowerShell 批量下载 SharePoint Online 文档
  • 论文写作利器:16个AI网站推荐与技巧
  • 16款AI论文写作网站推荐,附操作指南
  • 16个AI工具助力毕业论文,附实用方法
  • K8S负载均衡原理详解 - 智慧园区
  • 提示系统从崩溃到稳定:架构师的30天服务治理改造记
  • 北京GEO服务商怎么挑?2026年AI获客实战指南 - 品牌2025
  • Java编译报码8273代码解决的思路
  • 北京GEO服务商哪家强?2026年AI获客能力全景透视 - 品牌2025
  • 基于springboot框架的交通事故档案管理平台的设计与实现_o63l5u1o
  • 基于springboot框架的大学生健康管理系统_35l867i9
  • Dora视觉集成系统
  • 2026年琼海人气海鲜店推荐,抢先体验最值得的琼海海鲜大餐排行榜
  • Gaia 与 ARE:赋能社区的智能体评测
  • 提示工程架构师:解决prompt效率低下的5个创新实践技巧
  • HuggingFace
  • 讲讲 Redis 集群为什么只有 0 号数据库?
  • LLMs之Agent之Code:everything-claude-code的简介、安装和使用方法、案例应用之详细攻略
  • 6.6 Dify低代码平台搭建LLM应用完整实战教程
  • LCT 相关
  • HDFS的缺点与不适用场景
  • 北京豆包推广公司:如何选择合规、专业的GEO服务商? - 品牌2025
  • 你的 try-catch 没有在处理错误,它在藏错误