/dev/shm 被塞满导致 Redis/PostgreSQL 崩溃的业务场景与限制方法

10次阅读

/dev/shm 空间耗尽会导致 redis 和 postgresql 崩溃,因其 RDB 快照、并行查询、WAL 共享内存等依赖该 tmpfs 文件系统;默认 64MB 容易被占满,引发 ENOSPC 错误,造成服务拒绝连接或退出。

/dev/shm 被塞满导致 Redis/PostgreSQL 崩溃的业务场景与限制方法

为什么 /dev/shm 满了会让 redis 和 PostgreSQL 崩溃

Redis 默认用 fork() 做 RDB 快照,PostgreSQL 的并行查询、WAL 共享内存、甚至某些客户端连接(如使用 unix_socket_directories 时的 socket 文件)都可能依赖 /dev/shm。它本质是基于 tmpfs 的内存文件系统,默认大小通常只有 64MB(取决于内核版本和发行版),而 fork 时子进程会复制父进程的虚拟内存页(写时复制),但某些共享内存段(如 PostgreSQL 的 shared_memory_type = mmap 或 Redis 的 AOF rewrite 过程中临时缓冲)会直接在 /dev/shm 创建文件——一旦空间耗尽,open()shm_open() 返回 ENOSPC,服务就卡在初始化或 checkpoint 阶段,表现为拒绝新连接、主从同步中断、甚至进程直接退出。

检查 /dev/shm 是否真成瓶颈

别只看 df -h /dev/shm,那只是 tmpfs 总大小;更要确认实际被哪些进程占用了:

  • 运行 ls -l /dev/shm/,重点关注以 redisPostgreSQLpg_sem.shmem. 开头的文件
  • find /dev/shm -type f -size +1M -ls 找大文件
  • 查 Redis 日志是否含 Failed to open the temp file for AOF rewriteCannot allocate memory
  • 查 PostgreSQL 日志是否含 could not resize shared memory segmentout of memory(注意:这不一定是物理内存不足,可能是 shm 限额)

临时扩容与永久配置 /dev/shm 大小

临时改法立竿见影但重启失效;永久改法需配合挂载参数,且必须避开 systemd-tmpfiles 的覆盖逻辑:

  • 临时扩容(立刻生效):sudo mount -o remount,size=2G /dev/shm
  • 永久生效(推荐):编辑 /etc/fstab,把原 tmpfs /dev/shm tmpfs defaults 0 0 改成 tmpfs /dev/shm tmpfs defaults,size=2G 0 0,然后 sudo mount -o remount /dev/shm
  • 避免 systemd 干扰:确保 /usr/lib/tmpfiles.d/tmp.conf/etc/tmpfiles.d/*.conf 中没有对 /dev/shmdZ 类型定义(它们会重置权限和大小)

更治本:让 Redis 和 PostgreSQL 少用 /dev/shm

扩容只是兜底,关键要减少对它的依赖:

  • Redis:设 stop-writes-on-bgsave-Error no 只是掩盖问题;真正有效的是关掉 AOF(如果业务允许),或把 appendfilename 改到普通磁盘路径(AOF 文件本身不走 shm,但 rewrite 临时文件会);升级到 7.0+ 后可用 replica-announce-ip + replica-announce-port 避免某些 shm 通信路径
  • PostgreSQL:将 shared_memory_type 从默认的 mmap 改为 sysv(需重启),它用 System V IPC 而非 /dev/shm;同时调低 max_connectionswork_mem,减少共享内存总需求
  • 通用:禁用不需要的扩展(如 pg_stat_statements 在高并发下会频繁写 shm)、定期清理僵尸 socket 文件(find /dev/shm -name "PostgreSQL.*" -mmin +60 -delete

最常被忽略的是:容器环境里 /dev/shm 默认只有 64MB 且不会继承宿主机配置,docker run --shm-size=2gkubernetessecurityContext.shmSize 必须显式设置。

text=ZqhQzanResources