1. 故障定位先别急着翻日志先圈定现象边界做GBase 8c数据库运维这些年我遇到最多的故障根子都出在操作系统层。数据库进程跑在操作系统上CPU怎么分配、内存够不够、磁盘写得动写不动、网络稳不稳全都受系统层控制。业务侧看到的“连接失败”“实例重启”“主备切换”数据库日志里往往只有一句笼统的FATAL或ERROR真正的诱因早被淹没在系统日志里了。所以拿到故障单我的习惯是先别急着翻数据库日志先把现象边界圈出来再决定从哪里下手。1.1 为什么数据库故障一半以上能在OS层找到线索GBase 8c在单个节点上不是只有一个孤零零的进程它有一组核心进程在协作进程之间要使用共享内存、信号量、文件锁这些OS级别的资源同时还要依赖本地文件系统持续写入WAL日志和数据文件。这意味着OS任何一个资源分配异常最终都会传导到数据库行为上。举个例子磁盘写延迟正常是2毫秒某天突然涨到200毫秒数据库的checkpoint和WAL刷盘一下就被拖慢事务提交耗时跟着成倍上涨。业务侧不会说“你的磁盘慢”只会说“数据库卡了”。再比如内核网络监听队列溢出数据库监听端口来不及accept新连接应用侧看到的就是“Connection reset”或者“Connection refused”。这些现象只看数据库日志会得到一堆“could not send data to client”“terminating connection due to administrator command”这种间接结果很容易被带偏。所以我一直把证据分成三类数据库日志、OS日志、OS资源指标。三份证据必须落在同一个时间点上不能各看各的。数据库日志里有checkpoint报错那就去dmesg和sar里找同一时间段的磁盘I/O记录对上才算定位完成。光凭一句报错下结论后面大概率要返工。1.2 先回答四个问题再动手故障发生时最怕的就是看到报错就猜原因。我现在要求自己先回答四个问题答案写在纸上再开始查。第一故障从什么时间开始持续了多久这个时间要精确到秒因为数据库日志和OS日志都是按时间顺序记录的只有先把时间段锁定才能在两套日志里拉出同一段内容做交叉比对。很多DBA上来就看最后一屏日志这是不对的故障可能是半小时前就开始的滚动日志早把关键信息冲掉了。第二最近有没有变更数据库参数调整、应用发布、扩缩容、内核补丁、硬件维护、网络割接全都算。别看这个问题老生常谈真实案例里至少有一半问题是因为某个“小变更”没同步记录。比如有人顺手改了一个内核参数或者底层存储做了固件升级结果数据库第二天开始抖动。变更信息不完整定位方向就容易完全跑偏。第三影响范围是什么是单节点故障还是整个集群异常只影响某个业务库还是全部库这个信息能把排查范围缩小一大半。整个集群都出现连接超时第一怀疑对象是网络层和公共依赖只有单个节点出问题重点看这个节点的CPU、内存、磁盘和系统日志。第四应用侧报什么错不要只盯着数据库日志。应用报“timeout”和报“connection refused”对应的OS层原因完全不同。前者多半是资源紧张或网络延迟后者很可能是监听队列溢出或端口根本没起来。应用侧的报错文本措辞差异很大值得原样记录下来。这四件事理清楚我才会打开终端按固定命令流去采集现场信息。后面几个章节就是我在GBase 8c维护工作中反复使用、确认有效的定位方法和命令组合。2. 四大OS资源的定位方法与实战经验2.1 CPU先分清用户态、内核态、等待和stealCPU排查最容易犯的错就是只看整机load。load高可能来自CPU繁忙也可能来自磁盘等待、内存换页甚至是不可中断进程堆积。光看一个数字根本区分不了是哪一种。所以第一步是跑top按1键看每个核的使用率同时看us、sy、wa、st、hi、si这几项。us高说明是业务程序在跑要尽快定位到具体进程和SQLsy高说明系统调用频繁可能是锁竞争、网络包处理、文件系统操作wa高说明CPU在等磁盘瓶颈其实在存储st高说明这台机器是虚拟机宿主机上的其它虚拟机正在争抢CPU资源。st这个指标在物理机上很罕见但云上环境非常常见而且最容易误导人。有一次客户报告某个节点查询变慢我上去看到整机load有30第一反应是SQL问题。细看top才发现st已经占到40%也就是说有40%的CPU时间根本不在我们这台机器上执行而是被宿主机调度走了。数据库自身消耗的CPU并不高单纯调SQL没有意义。最后协调平台方把实例迁移到资源充足的宿主机故障立刻消失。这件事之后我只要看到云上数据库性能问题都会先看st再聊SQL。定位到确实是用户态CPU高后下一步要找到具体线程。推荐用pidstat -t -p 数据库主进程pid 1把进程内各线程的CPU使用率打出来然后去GBase 8c的会话视图里把os线程id和会话id对应起来。这个对应关系一旦建立是哪个会话在烧CPU就一目了然。复杂查询调优、vacuum(清理)任务调度都离不开这一步。我自己的经验是遇到CPU问题先在进程树里“捞”线程能省下大量在SQL日志里大海捞针的时间。2.2 内存free看到的只是表象free -h输出里buff/cache经常显示占用几十个G新同学看到就会紧张以为内存不够了。其实page cache是内核用来缓存文件和目录数据的内存不足时可以自动回收。真正要关注的是available列这才是应用实际可用的内存。如果available长期很低swap又被大量使用就要小心了。数据库场景最担心的内存问题是OOM Killer误伤进程。GBase 8c这类数据库进程内存占用大在系统内存紧张时很容易被内核选中杀掉。判断方法很直接先执行dmesg -T | grep -i oom看内核日志里有没有“Out of memory: Kill process”。如果有往上翻几行能看到当时是谁触发了OOM、谁被杀了、各进程当时的内存占用是多少。这里有个常见误解OOM杀的并不一定是内存占用最大的进程而是内核根据oom_score算出来的“性价比牺牲品”。所以有时候你会看到某个不起眼的进程被杀真正的元凶却是旁边那个疯狂吃内存的进程。解决思路不是单纯调大内存而是要控制整体内存水位。我规划GBase 8c内存时会预留20%到30%的OS空闲内存给page cache和系统进程shared_buffers这类数据库参数再大也不能把整机内存吃干。同时保留适量swap我习惯设成物理内存0.5到1倍用于缓解瞬时峰值而不是指望它扛长期压力。还有一类内存问题不太好查内存缓慢泄漏。具体表现是系统可用内存一天比一天少但单个进程看不出明显异常。排查时要靠监控趋势把free -h和ps aux输出按小时记录连续观察几天。定位到具体进程后再看这个进程是数据库内部的还是外部程序。数据库内部的多和连接数、缓存命中率变化有关外部的要单独处理那个程序的资源占用。这类问题不常见但一旦出现就非常隐蔽没有趋势数据很难抓住。2.3 磁盘和文件系统空间只是最低要求磁盘故障要分三层看空间、inode、I/O能力。很多监控只看磁盘使用率结果数据目录所在分区还剩不少GB数据库却写不进去了一查是inode耗尽。所以df -h和df -i必须一起看两个都正常才算空间充足。I/O能力判断要靠iostat -x 1的输出。我重点看await、util、svctm这三项。await是I/O请求从发出到完成的时间它高说明请求在队列里等着util接近100%只能说明设备一直在工作不代表“慢”svctm是实际服务时间。如果await高但svctm不高说明请求在排队瓶颈可能是请求数量太多或队列深度不合理不是磁盘物理速度不够。GBase 8c对磁盘的依赖核心在WAL日志和数据文件。数据目录、WAL目录、归档目录、日志目录我建议分开规划放在不同磁盘或至少不同分区。不为别的就为故障时能隔离。归档目录是最容易爆的开启归档但归档失败时日志会持续堆积直到分区写满数据库可能直接hang住或者备节点被踢出集群。我碰到过一次非常典型的归档目录占用100%数据库日志里刷满了“could not archive log file”但业务侧表现却是“节点失联”排查了好几个小时才发现是磁盘满。这件事之后我把磁盘监控指标扩展到了每个挂载点不再只盯根分区和数据分区。文件系统层面Linux环境给数据库用XFS通常比ext4稳尤其是并发写和多线程场景。挂载参数我建议至少加上noatime减少读操作对atime的更新其它优化项不要照搬网上的文章必须结合具体存储和内核版本做验证。还有一点要记住文件系统变成只读时错误提示已经非常明确但很多人的第一反应是重启机器这个动作非常危险。应该先确认根因通常和I/O错误、坏道、底层存储链路抖动有关再决定修复文件系统还是更换存储。2.4 网络连接被拒不一定是数据库不背锅GBase 8c的主备节点和分布式节点之间靠网络通信做心跳和日志同步。网络层故障在数据库日志里通常表现为“connection timeout”“connection reset”“heartbeat failed”这类模糊信息。不要一看到这些就怀疑数据库配置网络本身也是头号嫌疑对象。排查网络问题我习惯先跑ss -lntp查看监听端口状态重点看Recv-Q和Send-Q。Recv-Q如果持续不为0并且一直有大值说明连接已经到了内核但数据库进程还没来得及accept也就是监听队列溢出。GBase 8c监听队列容量由内核参数net.core.somaxconn和程序listen设置共同决定常见做法是把somaxconn调大。还有一个更隐蔽的坑如果tcp_abort_on_overflow开启内核会直接发RST包应用侧表现为“Connection reset”但数据库日志干净得很。再往前走一步要看TCP层的丢包和重传。用netstat -s能看到总的TCP重传次数配合ss -s看当前socket状态。如果重传持续增长通常意味着链路不稳可能是交换机丢包、带宽打满甚至网卡故障。分布式场景下还要检查节点间ping延迟和吞吐不能只看连通性。延迟抖动对日志同步的影响比丢包更隐蔽因为它看起来像“数据库处理慢了”。网络参数调整要克制不能把所有队列调大就完事。somaxconn调太大连接堆积在内核队列里不可见问题反而更难排查。我一般只在看到确凿的队列溢出证据时才调整每次只改一个参数改完立即用ss观察效果。这里也提一句分布式集群内部节点通信建议单独规划网络至少不要和业务流量混在一个拥塞链路上不然高峰期互相抢带宽数据库日志同步迟滞会引发一连串误判。3. 故障现场的一整套命令流建议直接背下来3.1 入场先抓三样时间、上下文、内核日志故障发生时最重要的事情是固定现场而不是急着分析。我实际操作时会按下面的顺序把信息先存下来整个过程用不了两分钟后面定位全靠这份底稿。date %F_%T # 记录当前时间确认和数据库日志时间一致 uptime # 负载全貌 top -H -b -n 1 top_H.txt # 进程和线程级CPU快照 free -h # 内存全貌 df -hT # 各挂载点空间 df -i # 各挂载点inode iostat -x 1 3 # 连续3秒的磁盘I/O详情 sar -q -u -r -b -n DEV -n TCP 1 3 # 系统历史统计 dmesg -T | tail -200 # 内核日志尾部 tail -200 /var/log/messages # 系统日志尾部这套命令流的顺序有讲究。先跑进程级快照是因为故障现场稍纵即逝CPU最高的那个线程可能几秒后就消失了再跑连续采样是为了看动态趋势最后看内核日志和系统日志是因为日志内容量大要先了解上下文再读不然很容易被无关条目淹没。我见过不少人到现场直接tail /var/log/messages翻了几百行全是无关记录反而错过了和数据库日志交叉验证的黄金机会。如果故障已经过去了现场快照没抓到怎么办只能靠sysstat的历史数据。sar服务如果一直开着可以回看故障时段内的CPU、内存、磁盘、网络指标虽然看不到进程级细节但定位到资源维度完全够用。这也是我坚持长期部署sysstat的原因平时看起来没什么存在感关键时候能救命。3.2 数据库侧日志怎么和OS侧交叉验证GBase 8c的运行日志通常在数据目录下的log或pg_log子目录里面记录启动参数、运行状态、错误信息还有慢查询和备份恢复日志。故障定位时我最常用的是运行日志重点关注FATAL、PANIC、ERROR以及“could not”这类带系统调用的描述。拿到数据库日志后第一步不是分析内容而是把时间对齐。OS日志用系统时间数据库日志用本地时间两边必须确认时区一致。我曾经处理过一个案例数据库日志显示14:00:05发生PANICOS日志同一时刻是14:00:04的磁盘I/O error偏差1秒这是因为两边日志精度不同不算问题但如果时区不一致再聪明的分析都没用。所以部署阶段就应该配好NTP统一时区。时间对齐后第二步是找交集。比如数据库日志里出现“could not write to file”我在同时间段的内核日志里又看到SCSI层的“I/O error”两条线索一拼根因基本清楚了。反过来如果数据库日志报连接中断但网络监控和内核日志都没异常那就要把注意力拉回到数据库自身比如连接池、会话数限制、慢事务阻塞。还有一类现场必须重视进程级崩溃。如果GBase 8c核心进程被信号杀掉OS可能留下coredump文件。建议提前在系统配置里设置好core文件路径比如kernel.core_pattern指向固定目录崩溃时自动落盘。没有coredump的情况下也要去/var/log/messages里找信号来源。“signal 9”通常对应OOM“signal 11”对应段错误这两个信号能把问题大的方向区分出来。3.3 内核参数的快速核对清单数据库部署文档里通常会给出内核参数建议但很多环境是后来改造的参数已经被改得和初始状态完全不同。我建议把下面这张表存下来遇到疑难故障就逐项核对。参数常见参考值影响vm.swappiness10左右不建议直接改0控制匿名内存和page cache回收偏好影响内存抖动vm.overcommit_memory0或部署文档指定值影响大内存请求是否被接受设置不当会触发OOM或启动失败vm.max_map_count65530或更大影响进程可映射内存区域数数据库线程并发多时易踩fs.file-max按连接预算放大限制系统总文件句柄数太小会报too many open filesnet.core.somaxconn1024或更大影响监听队列容量太小导致连接被拒绝net.ipv4.tcp_syncookies1缓解SYN Flood对正常业务影响小kernel.sem按部署文档影响信号量数量太小会导致启动新进程失败kernel.shmmax足够大影响共享内存段上限与shared_buffers配置相关这里要强调的不是“抄参数”而是“排查时先看这些参数现在是什么”。我遇到过一些环境的参数和官方要求完全相反数据库还能跑只是跑得很难受。比如kernel.sem设置过小数据库在会话量上来后会启动新进程失败日志里不断出现“could not create semaphores”。这类问题表面看像权限错误实际是内核参数不对只看应用层永远找不到根。另外不要忽略用户资源限制。在gbase用户下执行ulimit -a重点看open files、max user processes和stack size。进程数限制太小连接池扩容时fork子进程会失败文件句柄限制太小高并发下会出现大量“too many open files”。修改这类限制要同时改/etc/security/limits.conf和systemd服务配置只改一边不换另一边重启后照样踩坑。还有一个常见优化项是透明大页THP。很多数据库在THP开启时会出现性能抖动因为内存页合并和分配行为不稳定建议在内核启动参数里加上transparent_hugepagenever并确认生效。这个操作一般在系统初始化时就要做掉免得业务上线后再折腾。4. 三个高频故障现场复盘从现象到根因4.1 场景一归档日志写满磁盘集群节点反复被踢这个场景我印象很深。某系统是一主两备部署某天半夜开始主备节点频繁切换业务断断续续数据库日志里不断出现备节点连接超时、心跳失败的记录。现场同学第一反应是网络问题查了链路却一切正常。我上去后没有立刻查网络先跑df -hT一眼看到某个挂载点使用率100%。再查数据库日志发现归档进程从几个小时前就开始报“could not archive log file”WAL日志越积越多最终把磁盘彻底写满。磁盘满之后主节点无法继续接收备节点的同步请求连接超时被误判为节点故障触发了剔除和切换。处理分三步走第一步清理过期归档恢复可用空间第二步确认主备数据一致性把集群状态恢复正常第三步把归档目录迁移到独立磁盘给磁盘使用率加监控阈值超过85%就告警。事后复盘最值得记下来的教训是不能只监控根分区和数据目录归档目录这种容易被遗忘的挂载点恰恰是故障高发区。磁盘空间告警必须覆盖所有挂载点而且要按数据文件、日志、归档、临时文件分别设置预警阈值。4.2 场景二内存配置不当引发OOM误杀另一个高频场景是实例频繁重启。某个节点运行一段时间后业务查询突然大面积报错数据库日志里有“server process was terminated by signal 9”随后整个实例自动重启。单看这句话很多人会怀疑数据库进程崩溃但我翻了dmesg之后看到了“Out of memory: Kill process”确认是OOM误杀。为什么会被误杀当时的系统里除了数据库还跑着几个批处理任务内存占用经常冲高。数据库的shared_buffers配置非常大几乎把物理内存的可调空间都吃掉了swap又只有几个G内存一紧张内核只能选择杀掉某个进程来释放空间。数据库进程因为内存占用大在OOM打分里排在最前面就成了牺牲品。调整方案不是简单加内存而是三方面同时着手一是把数据库内存参数调到合理水位给OS和page cache留出余量二是限制批处理任务的内存占用并且错峰执行三是保留适量swap防止瞬时峰值触发内核干预。如果核心库确实很重要还可以调整进程的oom_score_adj让内核在万不得已时优先杀别的进程。但这是把双刃剑要配合监控否则可能掩盖真实内存压力。这个案例给所有DBA的提醒是数据库内存参数不是越大越好OS必须有自己的呼吸空间。我现在习惯在部署前先画一张内存预算表把数据库固定开销、连接并发开销、OS预留、page cache目标占用全部列出来再反推shared_buffers等参数的值而不是随手填个“物理内存的70%”。4.3 场景三文件系统异常变成只读数据库写入全部失败还有一种故障最直接也最容易让人慌神文件系统变成只读。某次应用反馈节点数据写不进去了数据库日志里刷出“ERROR: read-only file system”。我先执行mount看到对应磁盘状态已经是ro说明内核检测到I/O错误后自动把文件系统切成了只读保护模式。只读状态下数据库还能对外提供读服务但任何写入操作都会失败这比直接宕机更隐蔽。现场第一反应不是重启而是先把系统日志和内核日志完整保存下来因为一旦重启有些错误痕迹可能被冲掉。接着查看/var/log/messages发现文件系统底层报了一串I/O错误初步判断是存储链路抖动或磁盘坏道。协调底层存储修复后再尝试把文件系统重新挂载为rw同时做一致性检查。这类问题要避免一个危险操作在文件系统处于只读状态时强制重启主机。如果底层有硬件损坏重启后文件系统可能无法完整挂载反而扩大故障范围。正确流程是先隔离业务再备份日志再修复底层硬件最后恢复文件系统。另外只读状态要第一时间告警不能等业务反馈才发现。文件系统的“只读”和“空间满”是两种完全不同的问题前者通常对应硬件或链路故障后者是容量管理监控上要分别覆盖。5. 避坑清单和长期监控5.1 我踩过的几个坑做OS层故障定位这些年我也掉过不少坑有些至今想起都觉得不该犯。第一个坑是只看数据库日志、不看系统日志。有一次数据库会话大量中断我翻了一晚上慢查询和锁等待第二天偶然打开/var/log/messages才发现网络重传已经持续了好久。从那以后我给自己立了个规矩数据库日志里只要出现“connection”“could not”“terminated”这类字眼必须同时看系统日志。第二个坑是故障发生时没有保留现场。早期处理过一次磁盘满的故障第一反应是赶紧清理空间恢复业务结果把确凿的现场证据都删了后面做根因复盘时啥都拿不出来。现在我的习惯是先跑前面那套命令流把输出重定向到带时间戳的文件哪怕和业务恢复没关系也要留着。第三个坑是误把iowait当成CPU瓶颈。服务器load高我就去加CPU结果没过多久又打回原形。后来用iostat仔细看发现磁盘util长期100%SQL慢的根因在存储。给CPU加资源只会让应用更快地堆积到磁盘队列上问题反而更难解。这类误判在监控数据不完整的时候特别容易发生所以现在我把top和iostat配合着看先分清瓶颈在哪一层。第四个坑是对内核参数直接照抄网上的“优化大全”。有一次我把vm.swappiness直接改成0结果数据库在某些内存压力场景下表现更差。后来在测试环境仔细对比才发现我们那套存储和业务模型并不适合这个设置。现在碰到任何内核参数我都会先在测试环境压一遍确认没有副作用再上生产。5.2 值得长期部署的监控与应急准备经验再多也比不上一套可靠的监控。我给生产环境长期部署的几样东西这里一并分享。首先是sysstat也就是sar的源码包。它的价值不只是实时看指标而是“事后回放”。把定时任务配置好保留至少7天历史数据故障过去之后依然可以回看每一分钟的资源指标。对数据库这样的长期服务来说这份历史数据是定位疑难杂症的底牌。其次是日志时间同步。OS日志、数据库日志、应用日志、网络设备日志最好都统一到NTP源时区保持一致。很多时间轴对齐问题其实部署阶段就能避开而不是故障时靠人肉换算。第三是黄金指标监控清单我通常会盯这几个维度维度核心指标CPU使用率、load、steal、用户态/内核态占比内存可用内存、swap使用率、OOM事件、page cache回收情况磁盘每个挂载点空间、inode、await、util、只读状态网络TCP重传、连接队列溢出、连接数、丢包率进程数据库主进程是否存在、核心线程状态、进程数限制第四是应急脚本。我会准备一个一键采集脚本把前面第3节那套命令流的输出打包成一个带时间戳的压缩包同时把数据库日志最近2小时的错误行抽出来。故障发生时只要执行一条命令所有现场信息就自动留档。这套脚本平时不起眼真正救场时比任何分析工具都管用。最后是coredump配置。提前设置好内核的core_pattern确保数据库进程崩溃时能留下完整的内存映像同时检查ulimit -c是否为unlimited。有了coredump很多“进程神秘消失”的问题才有机会定位到具体代码路径而不是靠猜。做了这么多年数据库相关工作我最深的体会是故障定位不靠灵光一现靠的是规范流程和长期积累。OS层问题对数据库影响巨大可它恰恰是数据库运维里最容易被标准化处理的部分。把命令流、日志对齐、参数核对、监控指标变成肌肉记忆遇到故障时就能多一分从容。我自己的习惯是每次处理完一类OS故障都会把现场关键输出和解决方案归档到本地再用一段话写清时间线。回头再看这种记录比任何复盘文档都管用下次遇到同类问题直接按关键词翻出来能少走很多弯路。
阅读完成 · 觉得有帮助?