该问题本质是应用层日志系统在高并发压测时因文件句柄管理不当引发的线程级死锁,常见于Log4j2、Logback等框架配合异步日志与文件滚动策略使用时,多线程争抢同一日志文件写入句柄或滚动锁导致阻塞链形成。

这个问题本质不是数据库死锁,而是应用层日志系统在高并发压测时因文件句柄管理不当引发的线程级死锁。常见于 Log4j2、Logback 等框架配合异步日志 + 文件滚动策略使用时,多个线程争抢同一日志文件的写入句柄或滚动锁,导致阻塞链形成。
确认是否为日志句柄级死锁
先排除误判:数据库死锁报错通常含 “Deadlock found when trying to get lock” 或 “ERROR 1213”;而日志句柄问题表现为:
- 应用线程堆栈中大量线程卡在 FileOutputStream.write()、RollingFileManager.rollover() 或 LockSupport.park() 等 I/O 或同步块内
- 系统级观察:lsof -p
| wc -l 显示打开文件数逼近 ulimit 限制(如 65535),且大量句柄指向同一日志文件(如 app.log、app.log.2026-05-28) - 日志输出突然中断,但应用 HTTP 接口仍可响应(说明主线程未卡死,是日志子线程或异步队列阻塞)
关键修复动作(立即生效)
无需重启服务,优先调整运行时行为:
-
降低日志异步队列容量:将 Log4j2 的
AsyncLoggerConfig.RingBufferSize从默认 262144 调至 8192~32768,避免堆积触发锁竞争 -
禁用日志滚动期间的阻塞等待:在
RollingFileAppender中设置filePermissions="rw-r--r--"并添加suppressExceptions="true",防止滚动失败卡住主线程 -
临时关闭非核心日志:通过 JMX 或 runtime API 动态将
DEBUG/INFO级别日志设为OFF,只保留WARN/ERROR,大幅减少 I/O 压力
配置层根治方案
压测后必须落地的长期配置优化:
-
改用无锁日志轮转:启用
TimeBasedTriggeringPolicy+DefaultRolloverStrategy,并设置max="30"和fileIndex="nomax",避免归档重命名时的文件句柄争抢 -
分离日志路径与磁盘:将 access.log 单独挂载到高速 SSD 分区(如
/var/log/app/access),与业务日志物理隔离,消除 IO 队列竞争 -
替换为内存缓冲型日志:接入
log4j2-kafka-appender或logback-syslog4j,将访问日志直发消息队列或远程 syslog,完全绕过本地文件句柄
系统层兜底保障
防患于未然的 OS 级配置:
- 调高单进程文件句柄上限:
echo "* soft nofile 65536" >> /etc/security/limits.conf - 启用内核级日志缓存:
sysctl -w fs.inotify.max_user_watches=524288(尤其对频繁滚动的场景) - 禁用 journalctl 对应用日志的双写捕获:
systemctl edit rsyslog中添加Environment=RSYSLOG_ForwardToJournald=off

















