最近一个客户从 Oracle 迁移到 PostgreSQL 系的国产数据库后,CPU一直接近100%,但是再仔细分析,发现%system CPU占到60%左右,当然这是一种不正常的现象。之前我写过《如何在 Linux 上诊断高%Sys CPU》(https://www.anbob.com/archives/6730.html),使用pidstat确认%sys cpu进程大部分为 PostgreSQL 进程,pstack查看发现call,PostgreSQL 的线程大部分时间都在调用newfstatat()
,这不是正常现象,并且通常意味着数据库运行中存在频繁的文件状态检查(stat)操作,严重时可能导致性能瓶颈。
什么是 newfstatat()?
ENMOTECH
newfstatat()是 Linux 的系统调用,用于检查文件状态,类似stat()
、lstat()
等。PostgreSQL 在以下场景中可能频繁调用newfstatat()
:
判断WAL文件是否存在或过期;
检查relation文件(如表、索引文件);
目录扫描(如
pg_tblspc、
pg_wal、
pg_stat_tmp
等);检查文件是否存在或大小变化(特别是在归档、WAL回放、恢复或重启点期间;
后台进程(如checkpointer、walwriter、autovacuum、archiver)可能周期性遍历文件。
特别是在WAL写入或checkpoint过程中,PostgreSQL 会频繁检查pg_wal
目录中的文件状态。频繁调用它通常表示 PostgreSQL 正在不断访问某些文件或目录的元信息.
如何分析排查?
ENMOTECH
sys% CPU高存在2种情况,常见是系统级配置,还有一种是局部会话级。当看到CPU高,部分人是想着赶紧优化SQL,但是进数据库发现活动用户进程并非多高并发,其次一些较差的应用使用DB总有优化不完的TOP SQL。如果没有成本可以让你的DBA或乙方厂家在优化SQL上面折腾,或找应用厂家自查,但都效果微乎其微。首先应该定位CPU使用类型与触发点。
vmstat或top查看sys/user CPU占比;
明确范围,使用pidstat查找进程,pstack调用堆栈,strace跟踪函数的调用位置;
perf做系统级负载分析。
如本案例分析到是newfsatat()函数,能猜到是FS文件系统相关,再查看文件系统负载。
使用iostat查看负载,发现数据盘繁忙近100%,根据之前的负载压测基线数据,大概可以判断是否达到了硬件磁盘的IOPS或吞吐量上限,使用iotop找I/O高的进程。
优化调整
ENMOTECH
通过上面的排查,发现是I/O方面问题,并且主要进程为checkpoint进程,下面可以在系统级做一些调优,PostgreSQL 本地文件文件确实较多,但inode使用不到10%,之前我记录过《Linux最佳实践for Postgresql/openGauss》(https://www.anbob.com/archives/6970.html)在文件系统级有提到,调整文件系统的noatime nodirtime禁用访问时间,可以在线调整,我们调整完后%SYS CPU有明显降低,但是I/O繁忙率依旧比较高。
Checkpoint是 PostgreSQL 中重要的后台进程,负责将共享缓冲区中的脏页写入磁盘,并确保事务日志(WAL)的一致性。优化参数主要有:
checkpoint_timeout增加此值可减少checkpoint频率;
max_wal_size控制两次checkpoint之间允许的WAL最大大小;
min_wal_size WAL文件回收时的最小保留大小,应与max_wal_size配合调整;
checkpoint_completion_target控制在checkpoint_timeout内完成checkpoint的目标比例。
监控Checkpoint性能
SELECT * FROM pg_stat_bgwriter;SELECT checkpoints_timed, checkpoints_req, 100.0 * checkpoints_req / (checkpoints_timed + checkpoints_req) AS req_checkpoint_ratio, buffers_checkpoint, buffers_cleanFROM pg_stat_bgwriter;
关注以下指标:
checkpoints_timed – 定时触发的checkpoint
checkpoints_req – 因WAL增长触发的checkpoint
buffers_checkpoint – checkpoint写入的缓冲区数量
buffers_clean- 后台写入器清理的缓冲区数量
req_checkpoint_ratio<10%(请求式checkpoint占比低),checkpoint应由超时触发(
checkpoints_timed),而非WAL写满触发,尤其是>30%应该增加WAL
优化策略
减少checkpoint频率:增加checkpoint_timeout和max_wal_size
平滑checkpoint I/O:提高checkpoint_completion_target
平衡恢复时间:确保max_wal_size不会导致恢复时间过长
监控调整:根据pg_stat_bgwriter结果持续优化
-- 增加checkpoint间隔(默认5min,可增至15-30min)ALTER SYSTEM SET checkpoint_timeout = '30min';-- 允许更多WAL积累(默认1GB,根据磁盘空间调整)ALTER SYSTEM SET max_wal_size = '8GB';-- 使checkpoint写入更分散(默认0.5,建议0.7-0.9)ALTER SYSTEM SET checkpoint_completion_target = 0.9;
查看 WAL 文件统计
COUNT(*) AS total_wal_files, SUM(size) / 1024 / 1024 AS total_size_mb, (SELECT setting FROM pg_settings WHERE name = 'max_wal_size') AS max_wal_sizeFROM pg_ls_waldir();
检查 WAL 生成速率
SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0') / 1024 / 1024 AS total_wal_mb, (SELECT (sum(blks_hit)+sum(blks_read)) FROM pg_stat_database) AS total_io, (SELECT extract(epoch from now() - pg_postmaster_start_time()) / 3600 AS hours_up);
优化高 WAL 生成的查询
SELECT query, wal_bytes FROM pg_stat_statements ORDER BY wal_bytes DESC LIMIT 10;
如果太多WAL文件/频繁checkpoint,提高max_wal_size,降低checkpoint_timeout,提高checkpoint_completion_targetj.我们把max_wal_size从20G调到400G后,明显磁盘的使用率降了下来。
检查relation文件
除了WAL清理外,还有可能是检查relation文件是否存在时触发newfsatat,下面测试:
-- session 1select pg_backend_pid();-- session 2 strace -p xxx -o s2.o-- session 1select big query....
创建了一个大分区表,确认有多个relation文件,然后在执行此表的关联查询,另一个会话使用strace跟踪,后面查看newfsatat函数。发现newfsatat调用次数与表数据的文件个数并不成正比,而当join时有调用newfsatat.
# awk -F"(" '{print $1}' s2.o|sort|uniq -c|sort -nk 1 1 fallocate 1 ftruncate 1 mmap 1 munmap 2 epoll_pwait 2 kill 2 newfstatat 2 rt_sigprocmask 3 recvfrom 3 --- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=2024915, si_uid=1000} --- 3 --- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=2024916, si_uid=1000} --- 3 unlinkat 4 --- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=394857, si_uid=1000} --- 10 rt_sigreturn 521 brk 628 sendto 761 pread64 763 pwrite64 17138 openat 17139 close 18042 lseek# grep newfstatat s2.onewfstatat(AT_FDCWD, "base/pgsql_tmp/pgsql_tmp1981697.8", {st_mode=S_IFREG|0600, st_size=2211840, ...}, 0) = 0newfstatat(AT_FDCWD, "base/pgsql_tmp/pgsql_tmp1981697.7", {st_mode=S_IFREG|0600, st_size=1810432, ...}, 0) = 0
NOTE:使用newfstatat函数检查的是查询过程中的pgsql_tmp临时文件,接下来可以考虑优化SQL减少temp或调DB参数。
总结
ENMOTECH
出现newfstatat()
占用大量调用栈,不是bug,而是 PostgreSQL 或操作系统层面在频繁获取文件元信息。但它很可能是以下问题的表现症状:
WAL写入压力大
Checkpoint频繁
归档问题
文件系统性能差
查询产生大量的中间temp文件
数据驱动,成就未来,云和恩墨,不负所托!
云和恩墨创立于2011年,是业界领先的“智能的数据技术提供商”。公司以“数据驱动,成就未来”为使命,致力于将创新的数据技术产品和解决方案带给全球的企业和组织,帮助客户构建安全、高效、敏捷且经济的数据环境,持续增强客户在数据洞察和决策上的竞争优势,实现数据驱动的业务创新和升级发展。
自成立以来,云和恩墨专注于数据技术领域,根据不断变化的市场需求,创新研发了系列软件产品,涵盖数据库、数据库存储、数据库管理和数据智能等领域。这些产品已经在集团型、大中型、高成长型客户以及行业云场景中得到广泛应用,证明了我们的技术和商业竞争力,展现了公司在数据技术端到端解决方案方面的优势。