当前位置: 首页 > news >正文

开发转兼职DBA(七):不是SQL的锅——从操作系统层面排查数据库问题

开发转兼职DBA(七):不是SQL的锅——从操作系统层面排查数据库问题

数据库慢了,第一反应是查SQL、查执行计划。但有时候SQL没问题,索引也在走,数据库配置也没改——问题在操作系统层。这篇讲两个真实案例:Tomcat权限污染导致系统"假死",和磁盘IO瓶颈拖慢整个数据库。


文章目录

  • 开发转兼职DBA(七):不是SQL的锅——从操作系统层面排查数据库问题
    • 案例一:系统"挂了",结果不是数据库的问题
      • 现象
      • 排查过程
      • 修复
      • 为什么会这样
      • 教训
    • 案例2:数据库慢了,结果是磁盘的问题
      • 现象
      • 跳出数据库,看操作系统
      • 解决:加大内存
      • 为什么加大内存能解决磁盘问题
      • 为什么加大内存能解决磁盘问题
        • 两层缓存
        • 这个案例里发生了什么
        • 另一种思路:不扩内存,把SGA加大?
      • 一句话总结
    • 排查思路:逐层往下追
    • 几个常用命令速查
      • CPU相关
      • 内存相关
      • 磁盘IO相关
      • 网络相关
      • 文件权限相关
    • 系列终章总结

案例一:系统"挂了",结果不是数据库的问题

现象

某天上午,用户打电话来:系统打不开了。

登录检查:

  • 数据库正常,监听正常,SQL能跑
  • 应用服务器ping得通
  • 但浏览器访问页面,一直转圈,最终超时

第一反应:数据库是不是锁了?查了一下,没有阻塞会话,没有异常等待。数据库好好的。

那问题在哪?

排查过程

第一步:看应用日志。

tail-200f/opt/tomcat/logs/catalina.out

报错:

java.io.FileNotFoundException: /opt/tomcat/logs/localhost.2024-01-15.log (Permission denied) java.io.IOException: Unable to create directory /opt/tomcat/work/Catalina/localhost/_

写不进去日志文件。

第二步:查文件权限。

ls-la/opt/tomcat/logs/
-rw-r----- 1 root root 12345 Jan 15 09:00 localhost.2024-01-15.log drwxr-x--- 2 root root 4096 Jan 15 09:00 Catalina/

所有者全是root。

第三步:查Tomcat是用什么用户启动的。

psaux|greptomcat
tomcat 12345 2.3 15.4 2345678 654321 ? Sl 09:00 1:23 /usr/bin/java -jar ...

现在是用tomcat用户跑的。但日志文件属于root

第四步:问了一圈,真相大白。

前一天晚上,有人(用root账号)手动重启了Tomcat:

su- /opt/tomcat/bin/startup.sh

Tomcat以root身份启动。运行过程中创建的日志文件、临时文件、编译后的JSP class文件——全部属于root。

第二天早上,自动化的systemd服务以tomcat用户重启了Tomcat。新进程想写日志文件——没有权限。想编译JSP——work目录属于root,写不进去。应用看起来"挂了",但数据库完全正常。

修复

# 停掉Tomcatsystemctl stop tomcat# 把所有文件归属改回tomcat用户chown-Rtomcat:tomcat /opt/tomcat/# 重启systemctl start tomcat

一行chown,问题解决。

为什么会这样

Linux的权限模型很简单:

-rw-r----- 1 tomcat tomcat 12345 Jan 15 09:00 catalina.out │├──┤├──┤├──┤ ├──┤ ├──┤ │ │ │ │ │ └── 组名 │ │ │ │ └── 所有者 │ │ │ └── 其他用户的权限(--- = 无权限) │ │ └── 同组用户的权限(r-- = 只读) │ └── 所有者的权限(rw- = 读写) └── 文件类型(- = 普通文件,d = 目录)

进程创建新文件时,文件的所有者是进程的运行用户。root启动的进程创建的文件属于root。之后tomcat用户想去写这些文件——权限不够。

教训

  1. 永远不要用root启动应用服务——不仅权限会乱,安全风险也大
  2. 用systemd管理服务,在unit文件里指定用户:
[Service] User=tomcat Group=tomcat
  1. 排查"系统挂了"不要只盯着数据库——应用层的问题比数据库层更常见

案例2:数据库慢了,结果是磁盘的问题

现象

某大库,Oracle数据库整体变慢。不是某一条SQL慢,是所有SQL都慢。

第一反应:是不是锁表了?查v$session,没有阻塞。查AWR报告,发现db file sequential read的等待时间比平时高了几倍。

db file sequential read是单块读等待——Oracle从数据文件读一个块到缓冲区的等待。这个等待变长,要么是读的块变多了,要么是每次读的时间变长了。

查执行计划,跟以前一样,没变。查索引,没变。查数据量,没有暴增。

不是SQL的问题。

跳出数据库,看操作系统

第一步:看CPU。

top
%Cpu(s): 3.0 us, 1.5 sy, 0.0 ni, 55.0 id, 38.0 wa, 0.0 hi, 2.5 si

关键指标:

  • us(user):用户态CPU占用,3%——不高
  • sy(system):内核态CPU占用,1.5%——不高
  • id(idle):空闲,55%——一半多的CPU时间在空闲
  • wa(iowait):38%——严重不正常

wa(iowait)是CPU在等磁盘IO完成的时间占比。正常应该接近0。38%意味着CPU超过三分之一的时间在等磁盘。idle只有55%,不是CPU忙,是CPU在等磁盘

第二步:看磁盘IO。

iostat-x15
Device rrqm/s wrqm/s r/s w/s rMB/s wMB/s await %util sda 0.0 12.0 350.0 200.0 2.8 1.6 35.8 98.5

关键指标:

  • r/s, w/s:每秒读写次数,加起来550次/秒——很多
  • await:平均每次IO等待时间,35.8ms——很高
  • %util:磁盘利用率,98.5%——磁盘已经饱和

35ms的await意味着什么?Oracle一次单块读需要35ms。一个查询需要读100个块,光等磁盘就要3.5秒。而且这不是个别慢查询的问题——550次/秒的IO请求量,磁盘98.5%利用率,磁盘已经被打满了。

第三步:看内存。

free-g
total used free shared buff/cache available Mem: 128 95 2 1 31 30 Swap: 8 1 7

128G总内存,Oracle的SGA占了约100G,剩余留给操作系统的只有不到30G。其中buff/cache约31G——看起来不小,但这是一个大库,数据文件几百GB甚至上TB,31G的page cache远远覆盖不了热点数据。

128G总内存 ├── Oracle SGA: ~100G(Buffer Cache + Shared Pool + 其他) ├── 进程+系统: ~2G ├── page cache: ~31G └── 空闲: ~2G

根因找到了:大库,数据量大,128G内存里Oracle自己吃了100G,留给操作系统page cache的只有30G,缓存不住热点数据,大量读请求直接打到磁盘,磁盘被打满。

解决:加大内存

服务器内存从128G扩到264G。Oracle的SGA配置没动,还是100多G。多出来的136G全部留给操作系统。

扩完之后,top里看到最明显的变化:

  • idle从55%升到85%+——CPU空闲时间多了
  • wa从38%降到5%以下——CPU不用等磁盘了

free -g的变化:

total used free shared buff/cache available Mem: 264 97 80 1 87 164

page cache从31G涨到87G。Oracle的Buffer Cache没变,但操作系统层多出了56G的缓存。Oracle的Buffer Cache没命中的数据块,现在大概率在page cache里能找到——不用访问磁盘了。

iostat的变化:磁盘%util从98%降到30%左右,await从35ms降到个位数。

数据库整体性能恢复。不是改了任何SQL、加了任何索引、调了任何数据库参数——就是加了内存。

为什么加大内存能解决磁盘问题

为什么加大内存能解决磁盘问题

这个问题的本质不是"磁盘太慢",是内存不够大,缓存不住热点数据

但"缓存"这个词太笼统。内存分配给数据库之后,实际上是两层缓存在配合工作,很多人只看到了一层。

两层缓存
Oracle读一个数据块 ↓ 第一层:Oracle自己的 Buffer Cache(SGA里配的 db_cache_size) ↓ 命中 → 直接返回 ↓ 未命中 第二层:Linux的 page cache(操作系统的 buff/cache) ↓ 命中 → 从内存读,不需要访问磁盘 ↓ 未命中 第三层:磁盘 → 真正的物理IO(慢)

第一层:Oracle的Buffer Cache。这是Oracle自己管理的内存区域,配多大由sga_targetdb_cache_size决定。Oracle把频繁访问的数据块缓存在这里,内部用LRU算法管理淘汰。Buffer Cache命中率就是第五篇讲的那个指标——低于90%说明不够用。

第二层:Linux的page cache。Oracle的数据文件(.dbf)也是操作系统管理的文件。Oracle向操作系统发起read调用时,Linux先查page cache——如果这个数据块最近被读过,还在内存里,直接返回,不访问磁盘。

这两层缓存的关系:

  • Oracle的Buffer Cache是Oracle内部申请的内存,通过sga_target分配。这部分内存被Oracle进程锁定,Linux不会回收。
  • Linux的page cache是操作系统自动管理的空闲内存。Linux会把"没人用的内存"全部拿来缓存磁盘数据。应用不用的内存越多,page cache越大,磁盘IO越少。

内存就这么多,给了Oracle就不能给Linux的page cache,反过来也一样。所以内存规划的核心问题是:Oracle的Buffer Cache和操作系统的page cache,怎么分?

这个案例里发生了什么

128G总内存。Oracle的SGA配了约100G(其中Buffer Cache是大头),操作系统和进程占用几G,剩下的约30G被Linux自动用作page cache。

128G总内存 ├── Oracle SGA: ~100G(Buffer Cache占大部分) ├── 进程+系统: ~3G └── page cache: ~31G

大库的数据文件几百GB,100G的Buffer Cache加上31G的page cache,总共约130G的缓存。看似很多,但一个跑了几年的大库,热点数据分散在大量表和索引上,130G的缓存覆盖不了。大量读请求穿透两层缓存直接打到磁盘。

扩到264G之后:

264G总内存 ├── Oracle SGA: ~100G(没变) ├── 进程+系统: ~3G ├── page cache: ~87G(大涨) └── 空闲: ~80G(Linux会逐渐回收用作page cache)

Oracle的SGA没变,Buffer Cache还是100G左右。但操作系统的page cache从31G涨到87G。Oracle的Buffer Cache没命中的数据块,在page cache里命中的概率大大增加——很多请求不用访问磁盘了。

磁盘压力骤降。iowait从38%降到5%以下,idle从55%升到85%+。

另一种思路:不扩内存,把SGA加大?

既然缓存不够,把Oracle的Buffer Cache从100G加大到150G,是不是也能解决?

能缓解,但有个问题:Oracle的Buffer Cache只缓存Oracle自己的数据块。操作系统上的其他进程(RMAN备份、日志收集、文件传输)的磁盘IO不受Oracle Buffer Cache保护,还是直接打磁盘。如果这些操作也在产生大量IO,Oracle加大Buffer Cache的作用有限。

而操作系统的page cache是全局的——所有进程的文件IO都受益。Oracle的数据块、备份程序读的文件、日志文件的写入,都能利用page cache减少磁盘IO。

所以实际经验中:不要把所有内存都分配给Oracle的SGA,要留足够的内存给操作系统做page cache。一般建议Oracle SGA占总内存的40%~60%,剩下的留给操作系统。

这个案例里,128G内存给Oracle 100G,SGA占了78%——比例偏高了,留给操作系统的太少。扩到264G后,SGA只占38%,操作系统拿到160G,page cache充足,磁盘压力自然下来了。

这不是数据库调优能解决的问题。执行计划再优化,索引再加,该从磁盘读的块还是要读。瓶颈在磁盘IO,解法在内存规划和两层缓存的配合。

一句话总结

数据库慢了,不一定是SQL的问题。先看iostat和free,确认瓶颈在哪一层。


排查思路:逐层往下追

两个案例放在一起,抽象出一个通用的排查链路:

用户说"系统慢了/挂了" ↓ 第一层:应用层 ├── 应用日志有没有报错?(Tomcat权限问题就是在这一层发现的) ├── 接口响应时间是多少? └── 是所有功能都慢,还是某个功能慢? ↓ 第二层:数据库层 ├── 有没有锁阻塞?(v$session的blocking_session) ├── 有没有慢SQL?(v$sql按elapsed_time排序) ├── 等待事件是什么?(v$system_event) └── 执行计划有没有变化? ↓ 第三层:操作系统层 ├── CPU够不够?(top的us/sy/wa) ├── 内存够不够?(free的available和buff/cache) ├── 磁盘IO是不是瓶颈?(iostat的await和%util) └── 网络通不通?(ping/telnet/netstat) ↓ 第四层:基础设施层 ├── 磁盘是不是快满了?(df -h) ├── 存储后端有没有问题?(SAN/NAS/云盘) └── 有没有硬件故障?(dmesg | grep error)

大部分开发者到第二层就停了——查SQL、加索引、调参数。但如果问题在第三层、第四层,在数据库里怎么折腾都没用。


几个常用命令速查

CPU相关

# 整体CPU使用情况top# 每个CPU核心的使用情况mpstat-PALL15# 查哪个进程吃CPUpsaux--sort=-%cpu|head-20

重点关注:%wa(iowait)持续>5%说明磁盘是瓶颈。

内存相关

# 内存使用概况free-m# 进程内存排序psaux--sort=-%mem|head-20# 详细内存映射cat/proc/meminfo

重点关注:available(真正可用的内存,包含可回收的缓存)、Swap used(swap用了多少,持续增长说明物理内存不够)。

磁盘IO相关

# 磁盘IO统计(每秒刷新,共5次)iostat-x15# 查看哪个进程在疯狂读写磁盘iotop# 磁盘空间df-h# 目录大小du-sh/opt/*

重点关注:%util持续>80%说明磁盘饱和,await持续>10ms(SSD>1ms)说明IO延迟高。

网络相关

# 查看网络连接状态netstat-tlnp# 测试数据库端口是否通telnet192.168.1.1001521# 查看网络流量sar-nDEV15# DNS解析nslookupdbserver

文件权限相关

# 查看权限ls-la/opt/tomcat/logs/# 递归修改所有者chown-Rtomcat:tomcat /opt/tomcat/# 修改权限chmod755/opt/tomcat/bin/startup.sh# 查看某用户对文件的权限su- tomcat-c"test -w /opt/tomcat/logs/catalina.out && echo 'writable' || echo 'not writable'"

系列终章总结

七篇文章,一条线:

(一)只会写SQL ↓ 查询慢了 (二)学会看执行计划,加索引 ↓ 索引也救不了 (三)数据库起不来了,逼着理解WAL和redo ↓ undo也坏了 (四)逼着理解MVCC和undo ↓ 不能老出事才救 (五)学参数、内存、监控、备份 ↓ 换了个项目,数据库换成了MySQL (六)发现原理都一样 ↓ 数据库没问题,系统还是慢 (七)跳出数据库,从操作系统层排查

从一个只会写SELECT * FROM的开发者,到能看执行计划优化SQL,到理解WAL和MVCC的底层原理,到能配参数做监控写备份脚本,到跨数据库触类旁通,到能跳出数据库从操作系统层面定位问题。

这就是"开发转兼职DBA"的完整路径。不是因为我想学,是因为小团队没有专职DBA,出了事就是我扛。每次事故逼出一段认知,每段认知沉淀成经验,最终串成一条完整的知识链。


标签:#DBA #运维 #Linux #iostat #top #free #权限 #磁盘IO #page cache #故障排查

http://www.jsqmd.com/news/908203/

相关文章:

  • 达秘助力起量!28天狂揽50万GMV!中式锻打菜刀爆红TikTok美区,户外厨具赛道迎来新风口
  • Go语言项目结构:标准布局与最佳实践
  • 时序逻辑任务下的控制系统能量弹性:量化扰动应对成本
  • Upload-Labs Pass-01 ~ Pass-05 通關記錄:前端校驗、MIME、特殊後綴、.htaccess、大小寫繞過
  • 搞定7nm DRC收敛:一份来自Innovus和ICC2实战的避坑清单(附脚本)
  • 告别乱码!实测三款主流Java反编译工具(JD-GUI、Luyten、Jadx)的导出源码对比
  • 海宁市城镇有机更新专项规划(2024-2035年)
  • 规划师必备:用ArcGIS Pro二次开发5分钟搞定用地合规性检查(避坑指南)
  • MLIR与CGRA编译优化技术解析
  • PS 满屏斜着的透明水印如何制作?两大实操方案,快速做出全屏斜向水印
  • Cloudflare AI Labyrinth:用数字迷宫反制AI爬虫,保护原创内容
  • 用STM32CubeIDE搞定TB6612驱动GB37-520电机:从引脚配置到PWM频率计算全流程
  • AI时代职场竞争力重塑:从工具使用者到AI策展人的思维与实战
  • VUE2_TO_VITE_VUE3
  • 面试官:对话 Agent 上下文窗口不够用怎么办?
  • 从关键词到自然语言_AI搜索时代的搜索意图发生了哪些变化
  • 倾斜摄影测量全流程解析:从采集原理、CC建模到模型修复与土方计算
  • PS如何提高照片清晰度?3个方法零基础也能快速搞定高清修图
  • fselect:用类SQL语句查找文件
  • AI 告诉你代码安全,它在骗你!
  • Android init启动过程
  • 不只是VMware:开启AMD-V后,你的Win10/Win11还能玩转这些虚拟化工具
  • GPT5.5对Gemini3.5对DeepSeekV4编程能力横评
  • 别再死记硬背build.gradle了!用Groovy闭包和DSL思维,5分钟看懂Gradle配置的本质
  • 帆软报表FineReport连接Elasticsearch实战:从插件安装到SQL查询的保姆级避坑指南
  • 推荐几个博客
  • 用STM32F103 DIY一个JTAG边界扫描测试仪(附源码和避坑指南)
  • 别再只用洞洞板了!用嘉立创EDA+370电机,低成本搞定POV旋转LED全套硬件
  • AI与机器学习驱动的智能运营:从数据到决策的自动化闭环
  • 别再只盯着5G了!聊聊IMS:这个藏在通话、视频背后的‘老’技术,为啥现在又火了?