数据库变慢先别急查SQL,先看操作系统:GBase 8c故障排查实战
半夜两点手机震动值班电话接起对面一句话让我睡意全消核心库写入超时应用开始报错了。我条件反射地打开数据库日志查慢SQL、查锁等待、查主备复制状态忙了一个多小时数据库侧指标全都在正常范围可业务就是持续报错。直到我把目光从数据库挪到操作系统的设备层才发现真正的堵点根本不是SQL也不是锁——磁盘设备层的I/O队列彻底堵死了所有写入请求都在长时间排队。这段经历后来被我反复讲给团队里的新同事。尤其是维护GBase 8c这类数据库很多同学一遇故障就本能地往SQL执行计划、锁冲突、复制延迟上想却忽略了最底层的一条铁律数据库本身是跑在操作系统上的应用程序OS层面的CPU、内存、磁盘、网络、句柄任何一环出问题最终都会用数据库变慢连接超时节点不可用这种表象来表达。这篇文章想把先看操作系统再看数据库的定位思路从头到尾讲透包括GBase 8c与OS资源的耦合关系、高发故障的识别信号、一次完整排查案例、数据库侧证据链配合方法以及我日常踩坑之后攒出来的巡检工具清单。适合正在维护GBase 8c的运维和DBA同学也适合所有被数据库突然变慢折磨过的人。1. 为什么数据库卡死要先查操作系统先搞清楚因果链1.1 数据库故障往往只是OS故障的翻译结果很多人对数据库的印象是一套独立系统出问题就翻SQL、翻执行计划、翻锁等待这个方向没有错但不完整。GBase 8c无论单机、主备还是分布式部署最终都是若干个进程跑在Linux上靠操作系统调度CPU、分配内存、读写磁盘、收发网络包。OS相当于整栋大楼的水电燃气总管数据库只是楼里的一个租户。家里没水可能是自己家水管堵了也可能是整栋楼的总管坏了但用户感知到的永远是我家没水。我在实际维护里见过太多次数据库慢的投诉最后根因却五花八门有CPU被其他业务进程占满的有内存不足触发OOM把数据库关键进程杀掉的有磁盘控制器写缓存策略被改成写透导致每次刷盘都卡顿的甚至遇到过网卡固件问题引发主备复制持续延迟的。这些问题的共同特征是数据库侧只会显示为普遍性变慢、写入超时、会话堆积SQL本身没问题锁也没问题真正的问题发生在数据库看不见的OS层。所以我的第一个建议是收到数据库故障告警前两分钟先看一眼操作系统形成一个初步判断再深入数据库侧。这两分钟不会耽误事反而能帮你少走一个多小时的弯路。1.2 GBase 8c进程与OS资源的耦合关系要快速定位脑子里得有一张进程到资源的映射表。GBase 8c的核心进程大致包括主进程负责监听和启停控制、工作进程每个连接会话对应一个承载SQL解析、执行、结果返回、日志与复制相关进程WAL writer、checkpointer、主备之间的sender/receiver如果部署分布式形态还要考虑CN节点与DN节点之间大量内部通信会话。不同资源瓶颈会牵动不同进程表现也完全不同OS资源受影响的主要DB行为数据库侧常见表现CPUSQL解析与执行、排序、哈希连接、备机日志回放整体吞吐下降单条SQL变慢但看不出一致规律内存shared_buffers命中率、会话私有内存、排序哈希内存性能抖动、swap升高、严重时进程被OOM Killer清理磁盘I/O数据文件读写、WAL日志刷盘、checkpoint、备份恢复事务提交延迟、日志刷盘卡顿、checkpoint耗时飙升网络应用连接、主备复制、分布式节点间通信连接超时、复制延迟增长、分布式查询明显变慢文件句柄连接建立、日志写入、临时文件创建连接数上不去、报too many open files类型错误这张表就是定位的总纲。看到数据库表现先在OS层找出对应的资源指标如果OS资源是健康的再回来深挖数据库侧这比盲目翻SQL高效得多。2. 操作系统四类高频故障识别信号与定位手势2.1 CPU压力从load average到数据库慢查询的传导链条CPU问题是最容易被误判为数据库慢的。有一次某客户报批量作业执行时间从半小时拉长到三个小时数据库侧看所有SQL的执行计划都很好但就是整体变慢。一查uptimeload average已经飙到CPU核数的四五倍再top一看有一个跟数据库无关的数据采集进程占了三百多核——其实是cron任务里一个脚本出现了死循环。CPU排查我的固定手势是三板斧。第一板斧是uptime看load averageload持续超过核数就说明有排队的计算任务。第二板斧是top按CPU排序看用户态us、内核态sy、IO等待wa、空闲id四类占比。wa高说明CPU在等IO问题往往出在磁盘而不是CPU本身sy高说明系统调用频繁或内核锁竞争常见于进程频繁切换和网络小包风暴us高才是真正的计算密集。第三板斧是mpstat -P ALL看单核有时候整体空闲但某个核打满会形成木桶效应拖累整个数据库的吞吐。从CPU到数据库的传导链条也很清晰CPU被占满之后GBase 8c的工作进程得不到调度SQL执行时间变长新连接建立变慢连接池开始堆积最终应用侧报获取连接超时。这时候在数据库里查pg_stat_activity能看到一堆active状态的会话但每条SQL单独看都不算慢——它们不是在执行而是在等CPU。这个signal非常关键它提醒你别在SQL优化上浪费时间。2.2 内存与swapfree不是唯一指标别漏了页缓存与NUMA内存类故障比CPU隐蔽。free -h看到free列几乎为0很多新手就开始紧张其实这大概率是好事——操作系统把空闲内存用作页缓存buff/cache来加速文件读取这部分内存会在需要时自动释放。真正要看的是available列这个指标才表示在不触发swap的前提下还能分配多少内存。更危险的信号是swap使用率持续上升。一旦进程的内存页被换到swap分区数据库工作进程的响应时间会陡增几个数量级。我记得有一次GBase 8c节点频繁出现会话空闲但整体响应极慢排查下来发现是某次参数调整把shared_buffers和其他进程的内存需求叠加超过了物理内存总量系统开始大量swap。处理办法是调小数据库内存参数并把部分非核心进程迁移到其他节点swap逐渐回落到零后系统恢复。另外两个容易漏的点。第一个是OOM Killer内存耗尽时内核会挑选进程杀掉数据库关键进程如果被杀表现就是数据库突然连接不上或进程消失。dmesg | grep -i oom可以直接看到内核的杀进程记录这是第一现场证据。第二个是NUMA架构多路服务器上内存访问有远近之分如果数据库进程绑定的CPU访问了远端内存性能会明显受损。维护GBase 8c这类内存大户建议在确认业务场景后评估是否关闭NUMA或做亲和性绑核避免内存看着够性能就是不行的诡异问题。2.3 磁盘I/O从iostat的await到数据库WAL刷盘的连锁反应磁盘是数据库故障的重灾区也是我最先检查的OS资源。定位磁盘问题的标准命令是iostat -x 1重点看三列util设备利用率、awaitIO请求平均等待时间含排队时间、svctm实际服务时间。一个常被误解的点是util达到100%不代表磁盘坏了只说明IO请求已经排队更关键的是await如果await从正常时的几毫秒飙升到几百毫秒说明IO路径上出现了严重拥塞。磁盘瓶颈传导到GBase 8c有两条主要路径。第一条是事务提交路径GBase 8c沿用WAL机制事务提交前必须先刷WAL日志WAL落盘慢直接拉长commit时间表现就是应用侧写入越来越慢。第二条是checkpoint路径checkpoint要把脏页刷到数据文件如果数据盘忙checkpoint耗时变长又可能引发后续的IO尖峰形成恶性循环。排查时一个关键动作是建立目录到磁盘设备的映射数据目录、WAL日志目录、数据库日志目录各落在哪块盘上。很多故障的根因就是数据盘和WAL盘共用或日志盘混在系统盘上某类IO一多就把整条链路堵死。我处理过一个案例应用跑批时数据库日志疯狂输出日志盘和WAL盘共用结果WAL刷盘被日志写入拖垮最终表现为数据库整体冻结。把日志目录挪到独立盘之后问题彻底消失。2.4 网络与句柄连接数上不去、主备复制延迟的隐藏杀手网络类故障在分布式部署里尤其致命。GBase 8c的主备复制和CN/DN通信都依赖网络网络抖动不会像磁盘故障那样直接卡住进程而是表现为复制延迟增长、分布式查询超时、节点间连接频繁断开重连。看网络先看整体ss -s看连接统计sar -n DEV 1看网卡吞吐与错误包sar -n ETCP 1看重传率。重传率异常升高一般是链路不稳定、网卡驱动问题或者交换机端口拥塞。文件句柄问题则是另一种看不见的墙。GBase 8c是多进程模型每个连接对应一个后端进程每个进程要打开文件、socket、日志句柄。如果系统级/proc/sys/fs/file-nr接近上限或者进程级ulimit -n太小会出现外部连接数明明没到数据库上限但新连接就是建不上的怪象。这种问题在数据库侧看连接数并不到max_connections很容易误判为连接池故障。排查手势很简单lsof -p 数据库主进程pid | wc -l统计句柄数再对比ulimit限制基本一眼定位。3. 一次真实的操作系统故障排查全程从误判到根因3.1 故障现象与第一轮误判查SQL、查锁、查复制都没问题这一节用一个我处理过的典型案例把整个排查链路串起来。某生产系统是GBase 8c主备架构某天下午应用团队反馈写入超时率超过30%同时部分查询也明显变慢。我接手后的第一反应和大多数人一样先看数据库侧。第一步查pg_stat_activity发现active会话数量不少但没有任何一条SQL的执行时间异常长全部集中在几百毫秒到一两秒之间——这个量级不该引发超时。第二步查锁pg_locks里没有长时间的锁等待排除锁冲突。第三步查主备复制pg_stat_replication显示主备延迟正常。数据库侧的三角排查全部无果我开始迷茫甚至怀疑是不是应用侧连接池配置出错。事后复盘这一小时最大的教训是当数据库侧所有指标都正常、但整体表现异常时应该立刻跳出数据库。这个所有指标正常本身就是一种信号它说明问题不在SQL执行和锁机制而在于SQL根本跑不动——数据库只是把OS层的痛苦忠实地传递给了应用。3.2 操作系统层证据采集top、vmstat、iostat的现场快照换了个方向之后我先跑了top第一屏就发现问题waIO等待占比超过50%load average还在缓慢爬升。接着vmstat 1 10看动态变化r列运行队列很高b列不可中断睡眠通常是在等IO也持续有值wa比例高企这基本坐实了IO瓶颈。然后iostat -x 1看到具体的设备情况一块数据盘util持续100%await从正常时期的5毫秒左右飙到200毫秒以上svctm相对正常反而没那么高——这说明请求大量排队不是磁盘本身慢了而是IO路径堵住了。为了进一步确认是哪个进程在产生IO用pidstat -d 1按进程统计IO发现GBase 8c的WAL writer和checkpointer进程名列前茅。到这一步证据链已经清晰数据库写入请求正常但磁盘设备层排队严重导致WAL刷盘和checkpoint全部变慢最终传导为事务提交超时。问题范围从数据库收窄到了磁盘IO路径。3.3 根因定位磁盘控制器写缓存策略变更导致fsync卡顿范围锁定在磁盘IO路径之后我联系了基础设施团队查看存储侧配置。基础设施团队反馈了一个关键变更当天中午为配合一次机房维护存储设备的控制器写缓存策略被人为从写回调整为写透。写回模式是数据先写入控制器缓存就返回成功由控制器后台批量落盘写透模式则要求数据必须真实落到物理磁盘后才算写入完成。这个变更对普通文件读写影响有限但对数据库这种依赖fsync保证数据落盘的负载来说是灾难性的。GBase 8c每次事务提交都要fsync一次WAL原来fsync命中的是控制器缓存微秒级返回改成写透之后fsync必须等物理盘真正写完机械盘甚至部分低端SSD的单次写延迟会放大几十倍。数据库侧看到的现象就是事务提交时间从几毫秒变成几百毫秒整体写入能力断崖式下跌。我通过strace跟踪数据库写进程看到fdatasync系统调用后面跟着数百毫秒的等待再加上存储变更记录根因完全闭合。这里想强调的是数据库进程对fsync的依赖程度远超普通应用任何影响写缓存的存储配置变更都应该先评估数据库影响范围再做。3.4 修复动作与验证曲线改配置、调参数、看回归根因找到后修复很快。基础设施团队把存储控制器写缓存策略改回写回模式该存储设备自带电池保护写回模式安全性有保障我同时做了两个数据库侧的动作一是将checkpoint相关参数适当调大比如提高checkpoint_timeout和checkpoint_completion_target让checkpoint的IO压力在时间轴上铺得更平二是把数据库日志目录单独挪到另一块负载较轻的磁盘避免日志刷写与WAL刷盘抢IO。修复后我按时间线观察了三组指标iostat里那块盘的await从200毫秒回落到5毫秒左右util降到30%以下数据库日志中事务提交耗时恢复正常应用侧写入超时率归零。整个故障从定位到修复花了半天但其中超过一半时间都耗在第一轮数据库内部排查的误判上。如果一开始就遵循先OS后DB的顺序至少能节省一个小时。4. GBase 8c侧的证据链日志、视图与等待事件的配合4.1 等待事件是数据库给OS问题留下的目击证词数据库侧并非完全没有线索只是需要知道怎么看。GBase 8c的pg_stat_activity视图里有一个非常关键的字段wait_event。它记录了会话当前正在等待什么相当于给每个会话装了一个你在等什么的记录仪。我在定位OS问题时第一个SQL永远是这个select pid, state, wait_event_type, wait_event, query from pg_stat_activity where state not in (idle, idle in transaction) order by backend_start;这行查询的价值在于如果大量活跃会话的wait_event_type是IO或Sync等待事件集中在DataFileWrite、WALWrite这类IO类事件上那么OS层磁盘有问题的概率极高。如果wait_event都集中在CPU或Scheduler相关则是CPU资源紧张的表现。等待事件把OS资源的罪状翻译成了数据库的语言等于是数据库替OS作的目击证词。我记得在前面的案例里执行这个查询时能看到大量会话都在等WALSync和DataFileWrite这在正常的系统里不会出现。那一刻我心里已经有底了数据库没有病病在OS的IO路径上。4.2 主备复制视图网络与备机回放状态一查便知网络类问题要配合pg_stat_replication来看。这个视图里几个与延迟相关的字段——write_lag、flush_lag、replay_lag分别表示主库写入到WAL、备机收到WAL、备机回放入库三条链路的延迟。如果flush_lag和replay_lag同时增长背后往往不是数据库参数问题而是网络带宽受限或备机所在主机的磁盘IO跟不上回放速度。分布式形态下还要加一层检查——CN到DN之间的内部连接状态。有时候查询慢是某一条CN到DN的网络链路抖动导致CN在等DN的返回结果。这个可以从两个方向验证数据库侧看CN上会话的等待事件和分布式执行计划OS侧用ping和sar -n ETCP看节点间网络质量。数据库侧证据和OS侧证据对上才算完成闭环。4.3 时间轴关联法把OS快照与数据库日志对齐定位OS类故障我最推崇的方法是时间轴关联把OS监控数据、系统日志、数据库日志、应用报错时间点放到同一条时间线上对齐谁先发生、谁在跟随一目了然。具体做法不复杂先把数据库日志和系统日志里每个可定位时间点的记录捞出来再用OS监控平台拉出同一时间段的CPU、内存、磁盘、网络快照按时间排序比对。比如数据库日志里出现大量connection timed out系统日志同一时间出现网卡link down记录OS监控显示该时间点TCP重传率飙升三段证据指向同一个时间窗口根因不言而喻。我在日常运维里会把这一步固化成标准的故障复盘模板现象、时间窗口、数据库侧证据、OS侧证据、结论、验证结果。有了这个模板新人也知道该怎么有条理地排查而不是东一榔头西一棒子。5. 故障定位工具体系与日常巡检建议5.1 一套够用的OS层工具箱工欲善其事必先利其器。为了防止故障发生时才发现命令缺失我建议在任何跑GBase 8c的服务器上提前装好一套基础工具箱。不需要花哨够用就行。工具用途什么时候用top / htop实时看CPU、内存、负载第一反应必看vmstat看运行队列、阻塞进程、CPU与IO概览判断是CPU问题还是IO问题iostat -x看磁盘设备util、await、svctm磁盘IO定位pidstat -d按进程统计IO与CPU找出谁在消耗资源sar历史数据回溯CPU/内存/磁盘/网络故障发生后复盘ss连接数、网络状态统计连接超时排查dmesg内核日志OOM、硬件错误进程消失、内存耗尽lsof查看进程打开的文件与句柄句柄耗尽排查strace跟踪系统调用耗时确认是哪个调用被拖慢fio主动制造磁盘压力做验证复现IO问题这里重点提醒sysstat提供sar/iostat一定要装并且开启历史数据采集。故障发生后再想办法补监控基本来不及sar的历史记录常常是复盘时唯一能倚仗的OS证据。云环境如果自带监控平台更好但一定要确认历史指标能回溯到至少一个月前否则问题发生时就抓瞎了。5.2 指标基线没有基线等于没有定位很多人拿到OS指标不知道怎么看核心原因是缺基线。我常打一个比方指标是相对值不是绝对值。磁盘await是5毫秒还是50毫秒要看这台机器平时的水平CPU利用率40%算不算高要看业务高峰期的常态。没有基线任何一个指标看起来都可能有问题也会被轻易放过。我的做法是给每套GBase 8c环境建立一套OS指标基线表记录正常运行一周以上的指标范围指标采集方式建议关注阈值load averageuptime长期超过CPU核数70%需关注CPU wa占比top / vmstat持续超过25%警惕IO磁盘awaitiostat -x超过基线2倍持续10分钟需排查磁盘utiliostat -x持续超过80%做容量规划内存availablefree低于总内存10%预警swap使用量free持续非零需排查文件句柄cat /proc/sys/fs/file-nr占用超过上限80%关注TCP重传率sar -n ETCP超过0.1%关注每周做一次几分钟的巡检脚本检查发现超过阈值的项就提前处理总比半夜被故障叫醒强。这属于典型的花10分钟避免2小时救火。5.3 从救火到防火变更管理与容量预留最后一个建议来自经验的沉淀很多OS层故障不是自然发生的而是变更引发的。前面案例里存储控制器写缓存策略被修改就是一个典型例子。所以我会在团队里反复强调两条第一任何OS级变更都要有记录、有审批、有回滚方案变更前执行一次数据库性能基线检查第二数据库告警一旦触发监控平台要自动联动拉取OS快照把故障发生前后5分钟的OS指标和数据库日志打包存档。这样做的好处一方面是为复盘留下完整证据另一方面也逼迫团队在变更前多想一步。我自己吃过太多变更不告知故障靠猜的亏。有些变更当时看完全无害比如调整内核参数、升级网卡驱动、改存储策略但对数据库这种对资源性能和持久性极度敏感的系统可能就是压垮负载的那根稻草。把变更管理做好是防火的第一道防线。GBase 8c的故障定位说到底考验的不是对某一条SQL的优化能力而是对整个系统链路的理解。数据库是OS之上的应用OS资源是数据库的物理基础。这个顺序想清楚了定位故障的思路就顺了。干得久了你会发现很多所谓数据库疑难杂症其实只是操作系统在替你背锅之前你先让数据库背了锅。以后遇到数据库慢我的建议始终是同一个先花两分钟看一眼操作系统再看数据库。很多时候答案根本不在数据库里。