CnOps 智能运维与可观测社区
首页开源项目实践文章视频课程常见问答开发者工具STAROps 专题
导航菜单
首页开源项目实践文章视频课程常见问答开发者工具STAROps 专题

CnOps 智能运维与可观测社区



愿景

CnOps 智能运维与可观测社区是一个以"智能运维与可观测"为核心的开放、包容、分享的技术社区,旨在聚集运维专家、开发者和爱好者,共同探讨、学习和分享可观测最佳实践与最新技术,与众多技术社区合作互动,共同探讨交叉领域的技术挑战,推动可观测领域的创新与进步。

内容社区

  • 实践文章
  • 视频课程
  • 开源项目
  • 常见问答
  • 开发者工具

友情链接

  • Prometheus
  • Grafana Lab
  • OpenTelemetry
  • LoongCollector

关注我们

阿里云云原生公众号阿里云云原生
阿里云可观测公众号阿里云可观测

Copyright © 2026 CnOps 社区. All rights reserved.

首页STAROps 专题云数据库故障诊断最佳实践

云数据库故障诊断最佳实践

#场景能力#场景能力

STAROps | 2026-09-03

本文覆盖托管云数据库的连接、性能、锁与并发、存储与日志四类故障,面向三类读者:应用开发者用它定位连接池、SQL、事务和发布变更引发的问题;数据库运维与 DBA 用它完成证据保全、根因分析、止损、恢复和容量治理;SRE 与云平台工程师用它关联网络、计算、存储、可观测性和高可用事件。

前言

1. 命令标记、占位符和权限

  • [只读]:不改变数据库业务状态,但仍可能产生负载。

  • [变更]:修改参数、对象或运行状态;需审批、备份/快照、回滚方案和维护窗口。

  • [高风险]:可能中断会话、阻塞业务、扩大复制延迟或占用大量磁盘/IO;生产执行需 DBA 与业务负责人双重确认。

  • 统一占位符:<HOST>、<PORT>、<DB>、<SCHEMA>、<TABLE>、<COLUMN>、<VALUE>、<PID>、<USER>、<SLOT>、<CHANNEL>、<PATH>。替换时不要把尖括号保留给 Shell 重定向解析。

  • Agent 提示词模板额外使用 <INSTANCE>(实例)、<ENGINE_VER>(引擎与小版本)、<WINDOW>(故障时间窗)、<IMPACT>(影响面)、<ERRTEXT>(报错原文)、<CHANGES>(近期变更);另有 <REGION>(地域)、<WORKSPACE>(Agent 工作空间)、<SLS_PROJECT>(日志服务 Project)、<UMODEL_OBJ>(拓扑对象名)。

  • 客户端命令使用交互式密码提示或权限为 0600 的凭据文件/密钥管理服务;禁止把密码写入命令行、脚本、工单或聊天记录。

  • MySQL 常需 PROCESS、REPLICATION CLIENT、Performance Schema 视图权限;PostgreSQL 的多数 pg_stat_* 监控视图与 pg_ls_waldir() 等函数可由 pg_monitor 覆盖,但 pg_monitor 并非万能——例如 pg_hba_file_rules 在 PostgreSQL 14 默认仅超级用户可读,pg_monitor 不足;终止其他会话通常需 pg_signal_backend 或更高权限。云数据库可能不授予超级用户,并限制 SUPER、文件系统和部分函数,此时以控制台等价功能为准。

安全连接示例:

# [只读] MySQL:-p 仅触发交互提示,不在命令行提供密码
mysql --connect-timeout=5 -h <HOST> -P <PORT> -u <USER> -p <DB>
 
# [只读] PostgreSQL:-W 触发交互提示;也可使用受保护的密码文件
psql -W "host=<HOST> port=<PORT> dbname=<DB> user=<USER> connect_timeout=5 sslmode=prefer"

2. 模板怎么用:执行顺序与两段分工

每个场景在排查步骤之后紧跟一份可整段复制的提示词模板,执行顺序固定为技能路由→ 前置检查→ A 段 → B 段。

  • 技能路由:按场景加载对应技能,避免凭记忆操作。

  • 前置检查:第 0.0 项先判断实例是否已被 Workspace EntityStore 纳管,据此选择取数路由;再验证监控接入、日志投递、权限与保留期。任一项不满足,先报数据缺口,不要带着缺失数据源继续推断。

  • A段是 Agent 自取的证据:指标、日志、链路、事件与 OpenAPI。

  • B段是必须由 DBA 在实例上执行只读语句后粘贴的输出——执行计划、pg_stat_*、performance_schema 等都属于 B 段,Agent 无权限执行,只能索要。

  • 顺序不能倒:先做完 A 段再按结论决定 B 段索要哪些,避免一次性向 DBA 索要全部输出。

3. 模板怎么用:数据面与取数双路由

模板按阿里云 STAROps 的数据面书写:可读云监控 2.0(含托管 Prometheus 与 ARMS APM 指标)、SLS 日志、ARMS 链路、系统事件与告警、UModel 拓扑,并可调用云平台 OpenAPI 查询实例状态与配置;不具备直连实例执行 SQL 的能力。

  • 实例已纳管走 CMS2_WORKSPACE 路由,starops observe 系列命令优先。

  • 未纳管或该路由无数据走 ALIYUN_RDS 兜底:DescribeDBInstancePerformance 取指标,DescribeSlowLogs 与 DescribeSlowLogRecords 取慢日志,DescribeErrorLogs 取错误日志,DescribeEvents 取事件。

  • 无论走哪一路,都要在证据表标注路由选择与降级原因。

  • 两套路由的时间格式互不通用:前者用 RFC3339 或相对表达式,后者用 UTC ISO 8601。混用不会报错,会静默返回空列表——这是最容易误判成"没有数据"的坑。

  • 换用其他云平台时,按同类产品替换数据源与命令即可;手册正文其余部分保持厂商中立。

4. 模板怎么用:证据、基线与结论的硬规矩

  • 占位符必须全部替换后再提交。未替换的占位符会让 Agent 用假设补齐上下文,得到看似合理但无证据的结论。

  • 基线用默认配方:同一指标近 7 天同一时段的 P50 与 P95,跨周对比取同一星期几;涉及变更时另取变更前 24 小时与变更后等长窗口对比。保留期不覆盖基线窗口时降级为环比或对照实例,并在结论中标注降级。禁止用固定阈值替代基线。

  • 每条证据标注数据源与状态(已验证 / 不可得 / 未验证),每条判定回写基线窗口;无权限或数据源缺失的项列入数据缺口,不参与置信度加成。

  • 仅有指标侧证据时,置信度不得高于中,并说明缺哪一路证据。

  • 指标名、实体 ID 与时间格式一律不许凭命名规律推测;契约里没列出的,确认不到就按不可得处理。

  • 委派 CallInvestigationAgent 之后必须审查返回结果:结果为空或未实质回应故障范围时,由主 Agent 继续用技能与工具完成调查,不因委派失败而结束任务。

  • Agent的输出是诊断草稿,不是执行许可。标 [变更] 与 [高风险] 的动作仍须按手册的故障分级和证据保全、统一升级与回滚检查单的流程审批与留证。收到结论后按同样标准复核,不要直接采信。

5. 模版怎么用:具体流程

  • 第一步(只做一次):把下一节的通用契约整段写入 STAROps 数字员工的系统提示词,或做成一个自定义 skill 常驻加载。

  • 第二步:发生故障时从手册找到对应场景,复制该场景的 Agent 诊断提示词发给数字员工。已配置契约时,模板中与契约重复的通用段可以不发,只发场景专属部分;不确定发多少就整段发,不会冲突。

  • 第三步:替换占位符后提交,按上一节的硬规矩要求 Agent 产出。

  • 第四步:收到结论后,再决定是否进入处置流程。

# 云数据库故障诊断 Agent 通用契约
# 配置一次即可:写入数字员工的系统提示词或自定义 skill,之后每次诊断都自动生效。
# 场景专属内容(专属输入、指标 Key、采集条目、候选原因)由手册中该场景的 Agent 诊断提示
  词给出,本契约不重复。
 
角色:STAROps 运维诊断 Agent(数字员工)。可用数据面:云监控 2.0(含托管 Prometheus 与
ARMS APM 指标)、SLS 日志、ARMS 链路与调用栈、系统事件与告警、UModel 拓扑,以及云平台
OpenAPI 查询实例状态与配置。
能力边界:不具备直连数据库实例执行 SQL 的能力。凡需在实例内执行的语句,一律列入 B 段向
DBA 索要原始输出,禁止假装已执行或用经验值代替。
 
【输入】提交前替换全部占位符,缺失项写未知,不要留空
实例:<INSTANCE>;引擎与小版本:<ENGINE_VER>;地域:<REGION>
STAROps 上下文:workspace=<WORKSPACE>;SLS Project=<SLS_PROJECT>
UModel 对象:<UMODEL_OBJ>(该实例在 UModel 中的对象名,用于界定影响面)
故障窗口:<WINDOW>(注明时区);是否仍在持续:是 / 否
影响面:<IMPACT>(受影响业务、失败比例、是否可复现)
报错原文:<ERRTEXT>(原样粘贴,不要改写或截断)
近期变更:<CHANGES>(发版、参数、网络与证书、扩缩容、备份任务,含时间点)
场景专属输入项以手册中该场景的 Agent 诊断提示词为准。
 
【STAROps 技能路由】按场景加载对应技能,避免凭记忆操作
- 指标与日志查询:builtin.starops.observe(含 metric-route、log-route、alert-event、
  change-event、acm-basic 参考)
- RDS 专项巡检:builtin.rds.rds-inspection(含 command-contracts、datasource-routing、
  rds-inspection-execution.yaml 参考)
- APM 链路分析:builtin.cms2.apm.apm-analysis(指标漏斗)或 builtin.cms2.apm.trace-analysis
  (慢 Trace 诊断)
- SLS 日志查询:builtin.starops.sls-query 或 builtin.sls.sls-sql-generation
- UModel 拓扑:builtin.starops.umodel-operation
- 根因调查:若用户明确要求根因定位或快速/深度调查,调用 CallInvestigationAgent
  (diagnosis_mode: fast/deep),region_id 取实例所在地域 <REGION>。
 
【前置检查】第 0 步。任何一项不满足,先输出数据缺口与补齐动作,不要带着缺失的数据源继续推断
0.0 实例是否已在 CMS 2.0 Workspace EntityStore 中纳管。用 starops observe entity
  search --entity-domain acs --entity-type acs.rds.instance --filter <INSTANCE> 确认;
  命中则走 CMS2_WORKSPACE 路由(starops observe 优先取指标与日志),未命中则走
  ALIYUN_RDS 路由(aliyun rds OpenAPI 兜底),并在证据表标注路由选择与降级原因。
0.1 实例是否已接入云监控 2.0 或托管 Prometheus,且故障窗口与基线窗口内都有数据点。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 验证有数据点;
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 验证有数据点。
0.2 该场景需要的日志(见手册中该场景的 Agent 诊断提示词)是否已开启并投递到 SLS
  <SLS_PROJECT>;未投递则改走 OpenAPI 兜底接口,并说明该路径在粒度与保留期上的限制。SLS
  日志寻址见下方数据源映射通则。
0.3 调用该实例的应用是否接入 ARMS APM;未接入时应用侧证据只能由提交人提供。
0.4 UModel 中是否存在 <UMODEL_OBJ> 对象及其依赖关系;缺失则影响面判断降级为人工确认。
  用 starops observe entity search --entity-domain acs --entity-type acs.rds.instance
  --filter <INSTANCE> 确认实体存在,再用 entity topo / entity neighbor 查依赖。
0.5 当前身份是否具备读取指标、日志与实例 OpenAPI 的权限;缺权限直接列出所缺项,
  不要用经验值填补。
0.6 指标与日志保留期是否覆盖基线窗口(默认近 7 天);不覆盖则走基线降级路径并标注。
0.7 手册中该场景 Agent 诊断提示词里的场景专属前置检查项,逐条确认。
 
【取数参数对照】把语义映射成平台具体值。禁止凭经验猜指标名、实体 ID 与时间格式;
确认不到就标注不可得并降级
实体 ID:输入里的实例名可能只是别名,OpenAPI 必须用 DBInstanceId。先用
  DescribeDBInstances 按地域列出实例,用连接地址或备注反查 DBInstanceId,再调其他接口。
  四套标识互不通用,逐一确认后写进证据表:
  OpenAPI 用 DBInstanceId;SLS 用 <SLS_PROJECT> 加该实例对应的 Logstore;
  ARMS 用应用名或 PID;UModel 用 <UMODEL_OBJ>。
取数双路由(优先 CMS2_WORKSPACE,兜底 ALIYUN_RDS):
  CMS2_WORKSPACE 路由(实例已纳管时优先):
    指标:starops observe entity metric-data --entity-id <DBInstanceId>
      --entity-domain acs --entity-type acs.rds.instance
      --metric <MetricKey> --time-range <RFC3339_START>/<RFC3339_END>
    或:starops observe metric_set query --workspace <WORKSPACE>
      --promql '<PromQL>' --time-range <RFC3339_START>/<RFC3339_END>
    日志:starops observe entity log-data --entity-id <DBInstanceId>
      --entity-domain acs --entity-type acs.rds.instance
      --logstore <LOGSTORE> --query '<SPL或SQL>' --time-range <RFC3339_START>/<RFC3339_END>
    告警:starops observe alerts query --entity-id <DBInstanceId> --time-range <...>
    变更:starops observe changes query --entity-id <DBInstanceId> --time-range <...>
    系统事件:starops observe acm_basic event system-query --region <REGION>
      --resource-id <DBInstanceId> --time-range <...>
    UModel 拓扑:starops observe entity search → entity topo → entity neighbor
      → entity stores
  ALIYUN_RDS 路由(实例未纳管或 CMS2 无数据时兜底):
    aliyun rds DescribeDBInstances --RegionId <REGION>
    aliyun rds DescribeDBInstancePerformance --DBInstanceId <DBInstanceId>
      --Key <MetricKey> --StartTime <UTC_START> --EndTime <UTC_END>
    aliyun rds DescribeDBInstanceAttribute --DBInstanceId <DBInstanceId>
    aliyun rds DescribeParameters --DBInstanceId <DBInstanceId>
    aliyun rds DescribeSlowLogs / DescribeSlowLogRecords / DescribeErrorLogs
    aliyun rds DescribeEvents --ResourceGroupId <RG>
指标 Key(PostgreSQL):命名与 MySQL 不同,且随内核版本与增强监控开关变化。先从控制台
  监控项或托管 Prometheus 的指标列表确认实际名称,不得套用 MySQL_* 命名;确认不到就标注
  该指标不可得。
本场景该取哪些指标 Key,以手册中该场景的 Agent 诊断提示词为准;未列出的指标不要凭命名规律推测。
 
【时间格式对照】两套时间格式互不通用,混用会静默返回空列表
  starops observe 系列(CMS2_WORKSPACE 路由):
    --time-range 用 RFC3339 或相对表达式,例 2026-09-02T15:00:00Z/2026-09-02T16:00:00Z
    或 last_1h / now-15m~now
  aliyun rds 系列(ALIYUN_RDS 路由):
    DescribeDBInstancePerformance 与 DescribeSlowLogRecords 用 yyyy-MM-ddTHH:mmZ,
    例 2026-09-02T15:00Z;后者查询跨度不超过 30 天,起始不得早于当前时间前 30 天。
    DescribeSlowLogs 只接受日期 yyyy-MM-ddZ,例 2026-09-02Z;跨度不超过 31 天,
    起止为同一天时默认从 08:00 起算 24 小时。
  <WINDOW> 若填的是本地时间,先换算成 UTC 再传参,并在证据表里同时写出本地时间与 UTC。
 
【数据源映射通则】每条证据必须标注取自哪一路,并标状态:已验证 / 不可得(写清缺哪一路与
  补齐动作)/ 未验证。不可得的项不参与置信度加成,也不得用推测填空。
  本场景各路数据源分别取什么值,见对应场景的 Agent 诊断提示词。
SLS 日志寻址(三级优先,逐级降级):
  ① Workspace LogSet storage(starops observe entity stores --entity-id <DBInstanceId>
    列出该实例已纳管的 LogSet,再用 log_set query 查询)
  ② CloudLens for RDS 采集策略(starops observe entity log-data 直接按实体查日志,
    无需手动定位 Logstore)
  ③ 默认投递位置:Project=aliyun-product-data-<aliuid>-<region>,
    Logstore=slow_error_log(慢日志+错误日志合并投递)或 rds_audit_log(审计日志)
  以上三级均取不到时改用 OpenAPI 兜底(DescribeSlowLogs/DescribeSlowLogRecords/
  DescribeErrorLogs),并说明该路径在粒度与保留期上的限制。
ARMS APM 取数(PromQL,MetricStore=metricstore-apm-metrics-detail):
  - DB 出站指标:arms_db_requests_count / arms_db_requests_duration_sum
    (用 sum_over_time_lorc(...[1m]) 聚合,禁用 rate())
    按 dest_endpoint label 过滤到目标实例
  - 异常层指标:arms_exception_requests_count
    按 excepName label 区分超时类(SocketTimeoutException)与拒绝类
    (ConnectionRefusedException / ConnectException)
  - Trace 检索:starops cms2 trace search --app-name <APP>
    --filters 'spanName=connect,statusCode=2'(注意 statusCode 为数字 2,非字符串;
    spanName 用 connect 而非 rpc)
  - OTel 接入应用只有业务指标(arms_requests_*),无 JVM/系统指标
  ARMS 未接入或未开启 DB 埋点时这一路不可得:在证据表标状态为不可得,
  把应用侧结论降级为待应用侧确认,不得用指标或日志反推应用内部行为。
降级兜底:客户端网络探测依赖有人能在应用所在容器或子网内执行;无人可执行时标注不可得,
  链路结论降级为未验证——平台侧策略正确不等于端到端可达。
降级兜底:B 段证据依赖 DBA 在故障持续期间配合;拿不到就把相关候选原因标为证据不足,
  不要用指标侧现象替代。
 
【基线口径】默认配方,换口径必须写明理由
指标基线:同一指标近 7 天同一时段的 P50 与 P95;跨周对比取同一星期几,避免用全天均值
  掩盖峰值。
变更基线:以变更时间点为界,取变更前 24 小时与变更后等长窗口对比。
指标保留期未覆盖基线窗口或数据有缺口时,降级为环比(前 1 小时)或对照实例,并在结论里
  标注基线降级。
禁止用固定阈值(如 CPU 80%、缓存命中率 99%)替代基线判断。
 
【采集步骤】先做 A 段自取,再按 A 段结论决定 B 段索要哪些,避免一次性索要全部输出。
A 段是 Agent 用指标、日志、链路、事件与 OpenAPI 自取的证据;B 段是必须由 DBA 在实例上执
  行只读语句后粘贴的原始输出。
A 段与 B 段的具体条目见手册中该场景的 Agent 诊断提示词;B 段语句 Agent 无权限执行,只能
  索要输出。
 
【判定纪律】候选原因清单见手册中该场景的 Agent 诊断提示词,逐条写命中 / 排除 / 证据不足
  并给出依据。
每条判定写清依据的数据源、基线窗口与偏离幅度;仅有指标侧证据时置信度不得高于中,
并说明缺哪一路证据。
划清责任边界:数据库侧、客户端与应用侧、网络与托管平台侧。
 
【CallInvestigationAgent 指引】
  若用户明确要求根因定位、快速调查或深度调查,直接调用 CallInvestigationAgent:
    description: "<场景名>根因调查"
    instruction: 以 <last_user_message/> 开头,保留用户原始输入
    region_id: <REGION>(实例所在业务地域,非控制面地域)
    diagnosis_mode: fast(默认)或 deep(用户明确要求深度时)
    skill: quick-investigation 或 rca
  调用后审查返回结果:若结果为空、不可用或未实质性回应故障范围,主 Agent 继续用
  上述技能与工具完成调查,不因委派失败而结束任务。
 
【输出格式】
1) 结论:一句话根因 + 置信度(高 / 中 / 低)
2) 证据表:编号 | 证据 | 数据源(云监控2.0 / SLS / ARMS / 事件 / OpenAPI / UModel /
  DBA 粘贴)| 状态(已验证 / 不可得 / 未验证)| 取值 | 采集时间(本地时间与 UTC 各写一次)
3) 基线回写:实际使用的基线窗口与数据源、保留期是否覆盖该窗口、是否降级及原因
4) 根因候选:按可能性排序,逐条写保留或排除的依据
5) 处置建议:分 [只读验证] / [变更] / [高风险] 三档,每档给出前置条件与回滚方式
6) 数据缺口:因无权限或数据源缺失而未验证的项,逐条写清需要谁补什么

一、连接类故障

1. 连接超时/拒绝

1.1 现象描述

应用出现 connect timeout、Connection refused、Can't connect to MySQL server、could not connect to server。超时多指报文未返回或握手未完成;拒绝通常表示目标可达但端口未监听、代理主动拒绝或连接被策略重置,两者不能混为一谈。可能全部节点失败,也可能只有部分可用区、部分副本或部分容器网段失败。

1.2 常见原因

端点或端口配置错误;实例停止、重启、故障切换或代理异常;安全组/防火墙/ACL/路由拒绝;私网 DNS 解析漂移;数据库未监听目标接口;客户端连接超时小于网络实际抖动;连接风暴导致握手排队;应用误用外网/旧集群/只读端点。

1.3 排查步骤

1.3.1 网络连通性检查(ping/telnet/nc)

  • 目的:确认从真实应用来源到数据库端点的三层/四层链路是否可达,并区分“完全不通”“间歇不通”“仅 ICMP 被禁”。

  • OS/云控制台方法 [只读]:在与应用相同的网络命名空间(同容器、同 ECS/VM、同子网跳板)执行探测;云控制台侧查看实例网络类型、连接地址、可用区和网络诊断/连通性检查工具。

# [只读] 依次验证 DNS、ICMP、TCP;ICMP 失败不能作为 TCP 不通的证据
getent hosts <HOST>
ping -c 4 <HOST>
telnet <HOST> <PORT>
nc -vz -w 5 <HOST> <PORT>
  • MySQL 8.0 [只读]:TCP 通后用最小查询验证数据库侧确实接受连接。
mysqladmin --connect-timeout=5 -h <HOST> -P <PORT> -u <USER> -p ping
  • PostgreSQL 14+ [只读]
pg_isready -h <HOST> -p <PORT> -d <DB> -t 5
  • 关键判断字段:DNS 解析结果是否落在预期网段;ping 的丢包率与 RTT 是否与历史基线一致;nc/telnet 是超时(多为策略丢弃)还是立即 refused(多为无监听或代理拒绝);mysqladmin ping 返回 mysqld is alive、pg_isready 返回 accepting connections。

  • 下一步:解析地址不对→修正服务发现与私网 DNS;TCP 超时→“端口可达性检查”与“网络连通性故障”场景;立即 refused→“实例运行状态确认”;TCP 与 ping 均正常但客户端仍失败→“连接参数核对(host/port/协议)”。

1.3.2 端口可达性检查

  • 目的:确认目标端口在数据库侧真实监听,且中间设备没有把端口改写、拦截或映射到其他后端。

  • OS/云控制台方法 [只读]:核对控制台的“连接地址/端口”“读写分离端点”“代理端口”;确认安全组/ACL 放通的是同一端口;在应用主机检查本地出口是否被策略限制。

# [只读] 观察 TCP 握手过程与实际连接的四元组
nc -vz -w 5 <HOST> <PORT>
ss -tn state established "( dport = :<PORT> )"
  • MySQL 8.0 [只读]
SHOW VARIABLES WHERE Variable_name IN ('port','mysqlx_port','bind_address','skip_networking');
SELECT @@port AS server_port, @@hostname AS server_host;
  • PostgreSQL 14+ [只读]
SELECT name, setting, source, pending_restart
FROM pg_settings
WHERE name IN ('port','listen_addresses','unix_socket_directories');
SELECT inet_server_addr() AS server_addr, inet_server_port() AS server_port;
  • 关键判断字段:port/mysqlx_port 与客户端使用端口一致;bind_address/listen_addresses 覆盖客户端到达的接口;skip_networking 未开启;pending_restart 为 false,否则参数已改但未生效;inet_server_port() 应等于客户端连接端口,不等则说明中间有端口映射或代理。

  • 注意:inet_server_addr()、inet_server_port()(以及后文的 inet_client_addr())在 Unix domain socket 连接下返回 NULL,不要把 NULL 误判为端口映射或代理;判断端口映射必须在 TCP 连接上进行。

  • 下一步:端口不匹配→修正应用配置或代理映射([变更],走审批与最小开放范围);监听地址不含目标接口→按变更流程调整并确认是否需重启;端口正确仍不可达→“网络连通性故障”场景的“安全组规则检查”。

1.3.3 实例运行状态确认

  • 目的:排除实例已停止、正在重启、正在故障切换、被平台限制为只读/保护状态或处于维护窗口这类“非网络”原因。

  • 云控制台方法 [只读]:查看实例状态、事件与运维日志、最近的重启/切换/规格变更/备份任务、可用区与主备角色。

# [只读] 从控制台或云 CLI 读取实例状态与事件(各平台命令不同,此处示意等价操作)
# 关注:运行状态、可用区、主备角色、最近事件时间线、维护计划
  • MySQL 8.0 [只读]
SELECT @@hostname, @@port, @@read_only, @@super_read_only,
       @@innodb_read_only, VERSION() AS version;
SHOW GLOBAL STATUS LIKE 'Uptime';
  • PostgreSQL 14+ [只读]
SELECT pg_is_in_recovery() AS in_recovery,
       pg_postmaster_start_time() AS started_at,
       current_setting('transaction_read_only') AS read_only,
       version();
-- current_setting('transaction_read_only') 只反映当前会话/事务;实例级只读要看上面的 pg_is_in_recovery() 与下面的 default_transaction_read_only
SHOW default_transaction_read_only;
  • 关键判断字段:Uptime/pg_postmaster_start_time() 是否指向刚刚发生的重启;read_only/super_read_only/pg_is_in_recovery() 是否与期望角色一致(写请求连到只读节点会表现为“连上但报错”);PostgreSQL 侧以 pg_is_in_recovery()=true(备库)与 default_transaction_read_only=on(实例/库/角色级被置为只读)为准,current_setting('transaction_read_only') 只是当前会话取值,可被会话自行覆盖;控制台事件时间是否与故障窗口重合。

  • 下一步:实例异常或维护中→按平台流程等待或提工单,不反复重启;角色不符→修正读写端点;实例健康→“连接参数核对(host/port/协议)”与“认证失败”场景、“SSL/TLS 故障”场景。

1.3.4 连接参数核对(host/port/协议)

  • 目的:确认应用实际使用的连接串、驱动、协议与超时设置和目标实例匹配,避免“配置正确但生效的是另一份配置”。

  • OS/云控制台方法 [只读]:导出应用运行时真实生效的连接配置(环境变量、配置中心、Secret 引用),与控制台端点逐字比对;确认未混用内网/外网端点、旧集群端点或读写分离端点。

# [只读] 只打印非敏感字段,禁止输出密码
env | grep -Ei '^(DB_|MYSQL_|PG)' | grep -vEi 'PASS|PWD|SECRET|TOKEN'
  • MySQL 8.0 [只读]
SELECT CONNECTION_ID() AS conn_id, USER() AS client_claim,
       CURRENT_USER() AS matched_account, DATABASE() AS current_db,
       @@hostname AS server_host, @@port AS server_port;
SHOW VARIABLES WHERE Variable_name IN ('connect_timeout','wait_timeout','interactive_timeout','max_allowed_packet');
  • PostgreSQL 14+ [只读]
SELECT current_database() AS current_db, current_user, session_user,
       inet_client_addr() AS client_addr, inet_server_addr() AS server_addr,
       inet_server_port() AS server_port;
SELECT name, setting FROM pg_settings
WHERE name IN ('tcp_keepalives_idle','tcp_keepalives_interval','statement_timeout','idle_in_transaction_session_timeout');
  • 关键判断字段:连接到的服务器主机/端口/数据库与目标一致;USER() 与 CURRENT_USER() 是否被匹配到意外账户;客户端 connect_timeout 是否短于链路真实抖动;协议层(TCP/Unix socket、MySQL X 协议、sslmode)与服务端要求一致。

  • 下一步:连接串错误→按发布流程修正配置([变更]);超时过小→按分层超时策略调整;账号/来源不符→“认证失败”场景;协议或加密不符→“SSL/TLS 故障”场景。

1.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"连接超时/拒绝"场景(连接类故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
客户端来源:<HOST>(应用节点所在子网或容器网段);目标端点与端口:<PORT>
 
【前置检查补充】
0.2 需确认的日志:实例错误日志。
0.7 白名单与安全组是否可通过 OpenAPI 读取,客户端真实出口 IP 是否已确认。
  用 aliyun rds DescribeDBInstanceIPArrayList 读取 IP 白名单(原模板未指定此接口,补齐)。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_Sessions —— 当前活跃连接数与当前总连接数(个)
  MySQL_ThreadStatus —— 活跃线程数与线程连接数(个)
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:实例可用性、连接数与连接数使用率、活跃会话数。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):实例错误日志中的连接被拒绝、超过连接上限、认证失败记录。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:实例重启、主备切换、维护变更、平台网络事件。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:实例运行状态与规格、端点清单(主/只读/代理)、白名单与安全组、只读属性。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
白名单与安全组读取(OpenAPI):
    aliyun rds DescribeDBInstanceIPArrayList --DBInstanceId <DBInstanceId>
    --RegionId <REGION>  -- 读取 IP 白名单(原模板未指定此接口,补齐)
    安全组需走 ECS/NACL 相关 OpenAPI,不在 RDS 接口范围内。
需 DBA 执行并粘贴:实例 Uptime 与只读角色、服务端实际监听端口、当前连接数与上限。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 取故障窗口内实例可用性与连接数曲线,与基线比对,判断是否触达连接上限。
  2) 查事件与告警,确认窗口内是否发生重启、主备切换、维护或平台网络事件。
  3) 查实例错误日志,按被拒绝 / 超过上限 / 认证失败分类计数,并取首末时间点。
  4) 调 OpenAPI 核对实例状态、端点清单与白名单(DescribeDBInstanceIPArrayList),
     确认客户端出口 IP 是否放通、是否误连只读或代理端点。
  5) 从 ARMS 取建连失败样本,区分超时(无响应)与 refused(立即拒绝)及其客户端分布。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 实例状态与只读角色(判断是否连到只读节点或刚重启):
   MySQL:SELECT @@hostname, @@port, @@read_only, @@super_read_only;
          SHOW GLOBAL STATUS LIKE 'Uptime';
   PostgreSQL:SELECT pg_is_in_recovery(), pg_postmaster_start_time(),
               current_setting('default_transaction_read_only');
2) 连接上限与当前占用(排除连接已满):
   MySQL:SELECT @@max_connections; SHOW GLOBAL STATUS LIKE 'Threads_connected';
   PostgreSQL:SHOW max_connections; SELECT count(*) FROM pg_stat_activity;
 
【候选原因与判定补充】
- 端点或端口配置错误,或应用误用外网、旧集群、只读端点
- 实例停止、重启、故障切换,或托管代理异常
- 安全组、防火墙、ACL 或路由拒绝
- 私网 DNS 解析漂移,解析到旧地址
- 数据库未监听目标接口
- 客户端连接超时小于网络实际抖动
- 连接风暴导致握手排队
超时与 refused 必须分开判:前者多为策略丢弃或链路不通,后者多为端口未监听、
  代理拒绝或实例未就绪。
  指标与事件均正常且只有部分客户端 refused 时,转认证与加密方向排查。

1.5 解决方案

先恢复正确端点、网络路径和实例状态;应用使用带抖动的指数退避、有限重试与连接池,避免故障时重试风暴。监听地址、安全组、路由和数据库重启均为 [变更],需最小开放范围、审批和回滚;不要为“快速验证”向公网全开放端口。若实例处于平台维护或切换中,等待平台流程完成并保留事件证据,不叠加人工重启。

1.6 验证与预防

从至少一个真实应用节点执行 TCP、TLS、认证和 SELECT 1 四层验证;观察一个业务高峰窗口内连接成功率、建立时延和错误码是否恢复基线。维护端点清单、私网 DNS 监控、合成连接探测和故障切换演练;把连接串纳入配置审计,禁止硬编码端点。

2. 连接数耗尽

2.1 现象描述

MySQL 报 Too many connections;PostgreSQL 普通用户把 max_connections 占满时最常见的是 FATAL: sorry, too many clients already,而 FATAL: remaining connection slots are reserved for non-replication superuser connections 只在仅剩 superuser_reserved_connections 余量时出现,两条报文对应的余量状态不同,不要混用。应用连接池等待、请求排队,新连接失败而已有连接可能仍工作。管理员往往也难以登录,需要预留的运维连接通道。

2.2 常见原因

连接未关闭;每请求新建连接;连接池总上限大于数据库容量;慢查询或锁等待占住会话;发布扩容后应用副本数增加但单池大小未缩减;健康检查连接过多;代理与数据库连接复用失配;空闲事务长期占用连接。

2.3 排查步骤

2.3.1 当前连接数与上限查看

  • 目的:确认当前连接量、历史峰值与上限的关系,判断是真的到顶、平台代理层到顶,还是错误信息误导。

  • MySQL 8.0 [只读]

SHOW VARIABLES WHERE Variable_name IN ('max_connections','max_user_connections','thread_cache_size');
SHOW GLOBAL STATUS WHERE Variable_name IN
('Threads_connected','Threads_running','Max_used_connections','Connections','Aborted_connects');
  • PostgreSQL 14+ [只读]
SELECT name, setting, pending_restart FROM pg_settings
WHERE name IN ('max_connections','superuser_reserved_connections','max_worker_processes');
SELECT count(*) AS total,
       count(*) FILTER (WHERE state = 'active') AS active,
       count(*) FILTER (WHERE state = 'idle') AS idle
FROM pg_stat_activity;
  • 关键判断字段:Threads_connected 与 max_connections 的差距、Max_used_connections 反映的历史峰值、Threads_running 反映的真实并发;PostgreSQL 的总连接数是否已吃掉 superuser_reserved_connections 余量;托管代理的连接上限须与后端上限分别核对。

  • 下一步:接近上限且活跃占多→“空闲连接与活跃连接区分”与“慢查询”场景(性能类故障);接近上限但多为空闲→“空闲连接与活跃连接区分”、“应用侧连接池配置检查”;未接近上限却报错→检查代理层、单账号 max_user_connections 和平台限制。

2.3.2 连接来源与分布分析

  • 目的:定位是哪一个服务、实例、版本或作业在制造连接,避免误伤正常业务。

  • MySQL 8.0 [只读]

SELECT user, SUBSTRING_INDEX(host,':',1) AS client_ip, db, command, COUNT(*) AS sessions
FROM information_schema.processlist
GROUP BY user, client_ip, db, command
ORDER BY sessions DESC LIMIT 50;
  • PostgreSQL 14+ [只读]
SELECT datname, usename, client_addr, application_name, state, COUNT(*) AS sessions
FROM pg_stat_activity
GROUP BY datname, usename, client_addr, application_name, state
ORDER BY sessions DESC LIMIT 50;
  • 关键判断字段:单一 IP/账号/application_name 是否占据异常份额;是否有未登记的批任务、数据同步、BI 工具或健康检查;NAT 后所有来源可能表现为同一地址,此时以 application_name、账号和数据库区分。

  • 下一步:来源明确且异常→联系所有者限流或下线(应用侧优先);来源为健康检查/探针→降低探测频率并复用连接;来源均正常但总量过大→“应用侧连接池配置检查”并做容量评估。

2.3.3 空闲连接与活跃连接区分

  • 目的:判断连接被“占着不用”(泄漏、空闲事务、池过大)还是“用着不放”(慢 SQL、锁等待),两者的处置完全不同。

  • MySQL 8.0 [只读]

SELECT PROCESSLIST_ID, PROCESSLIST_USER, PROCESSLIST_HOST, PROCESSLIST_DB,
       PROCESSLIST_COMMAND, PROCESSLIST_TIME, PROCESSLIST_STATE
FROM performance_schema.threads
WHERE TYPE = 'FOREGROUND'
ORDER BY PROCESSLIST_TIME DESC LIMIT 50;
SELECT trx_state, COUNT(*) FROM information_schema.innodb_trx GROUP BY trx_state;
  • PostgreSQL 14+ [只读]
SELECT state, COUNT(*) AS sessions,
       max(now()-state_change) AS max_in_state,
       max(now()-xact_start) AS max_xact_age
FROM pg_stat_activity
WHERE pid <> pg_backend_pid()
GROUP BY state ORDER BY sessions DESC;
  • 关键判断字段:MySQL Sleep 且 PROCESSLIST_TIME 很大的会话数量、是否存在 trx_state='RUNNING' 却无当前 SQL 的空闲事务;PostgreSQL idle、idle in transaction、idle in transaction (aborted)、active 各自占比与在该状态的停留时长。

  • 下一步:idle in transaction 或 MySQL 空闲事务占主导→“长事务”场景(锁与并发故障);active 占主导且等待锁→“锁等待”场景(锁与并发故障);idle 占主导→“应用侧连接池配置检查”;active 且执行慢→“慢查询”场景(性能类故障)。

2.3.4 应用侧连接池配置检查

  • 目的:核算“实例副本数 × 单池上限 + 运维保留”是否超过数据库容量,并确认池的回收、探活、超时配置合理。

  • OS/应用侧方法(厂商中立)[只读]:导出连接池配置(HikariCP maximumPoolSize/idleTimeout/maxLifetime、pgBouncer pool_size/pool_mode、Druid、SQLAlchemy pool_size/max_overflow、Go SetMaxOpenConns)与副本数/HPA 上限;在应用主机确认到数据库的 ESTABLISHED 连接数与池上限一致。

# [只读] 统计本机到数据库端口的连接数,与池上限对照
ss -tn state established "( dport = :<PORT> )" | wc -l
  • MySQL 8.0 [只读]:用数据库侧观测反推池行为。
SHOW GLOBAL STATUS WHERE Variable_name IN
('Connections','Threads_created','Threads_cached','Aborted_clients','Aborted_connects');
SHOW VARIABLES WHERE Variable_name IN ('wait_timeout','interactive_timeout');
  • PostgreSQL 14+ [只读]
SELECT datname, numbackends, xact_commit, xact_rollback,
       sessions, sessions_abandoned, sessions_fatal, sessions_killed
FROM pg_stat_database WHERE datname = '<DB>';
SELECT name, setting FROM pg_settings
WHERE name IN ('idle_in_transaction_session_timeout','idle_session_timeout','tcp_keepalives_idle');
  • 关键判断字段:Connections/sessions 的增长速率(每秒新建连接数高说明没有复用)、Threads_created 相对 Connections 的比例、Aborted_clients/sessions_abandoned 反映的异常断开;池 maxLifetime 应小于中间设备与数据库 wait_timeout/idle_session_timeout。

  • 下一步:池总量超容量→缩小单池并集中排队(应用侧变更优先);每请求新建连接→修复池化;maxLifetime 大于中间设备回收时间→按“网络连通性故障”场景调整保活;池配置合理但仍不足→做容量评估后再考虑提升上限。

2.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"连接数耗尽"场景(连接类故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
连接池配置:<VALUE>(单实例池上限 × 应用实例数,含定时任务与旁路脚本)
 
【前置检查补充】
0.2 需确认的日志:实例错误日志。
0.7 连接数指标的采样间隔是否足以反映短时尖峰;不足时在结论中说明并要求 DBA 补瞬时值。
  CMS2_WORKSPACE 路由下可用 starops observe entity metric-data --interval 10s 提高粒度。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_Sessions —— 当前活跃连接数与当前总连接数(个)
  MySQL_ThreadStatus —— 活跃线程数与线程连接数(个)
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:连接数与连接数使用率、活跃会话数、(若暴露)连接拒绝数。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):错误日志中的连接超限记录及其来源地址。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:扩缩容、规格变更、参数组变更、备份任务。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:max_connections 等参数当前值、实例规格、代理端点连接上限。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
需 DBA 执行并粘贴:连接上限与历史峰值、按来源聚合的连接分布、空闲事务数量与最长时长。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 取连接数与活跃会话曲线,标出触达上限的时间点,与基线 P95 比对。
  2) 查错误日志的连接超限记录,统计频率与来源分布。
  3) 调 OpenAPI 读 max_connections 与实例规格,核对代理层上限是否低于后端上限。
  4) 查事件:窗口内是否有发版、扩容、参数组变更或备份任务。
  5) 从 ARMS 取连接池等待时间与获取失败率,区分应用侧池不足与后端上限不足。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 上限与峰值(确认是否真的触顶及触顶时刻):
   MySQL:SELECT @@max_connections;
   SHOW GLOBAL STATUS WHERE Variable_name IN ('Threads_connected',
   'Threads_running','Max_used_connections','Max_used_connections_time',
   'Connection_errors_max_connections');
   PostgreSQL:SHOW max_connections; SHOW superuser_reserved_connections;
   SELECT count(*) FROM pg_stat_activity;
2) 连接来源分布(定位占用最多的客户端与账号):
   MySQL:SELECT host, user, count(*) c FROM information_schema.processlist
   GROUP BY 1,2 ORDER BY c DESC LIMIT 20;
   PostgreSQL:SELECT client_addr, usename, application_name, state, count(*) c
   FROM pg_stat_activity GROUP BY 1,2,3,4 ORDER BY c DESC LIMIT 20;
3) 空闲与空闲事务(区分泄漏与真实并发):
   MySQL:SELECT command, count(*) FROM information_schema.processlist GROUP BY 1;
   SELECT trx_id, trx_started, trx_state FROM information_schema.innodb_trx
   ORDER BY trx_started;
   PostgreSQL:SELECT state, count(*), max(now()-state_change) AS longest
   FROM pg_stat_activity GROUP BY 1;
4) 连接拒绝与错误计数(补齐原模板缺失的状态变量):
MySQL:SHOW GLOBAL STATUS WHERE Variable_name IN ('Aborted_connects',
'Aborted_clients','Connection_errors_max_connections',
'Connection_errors_internal','Threads_connected','Threads_running');
-- Aborted_connects 统计连接建立前被拒绝的次数(认证失败、超限、超时等)
-- Connection_errors_max_connections 统计因达到 max_connections 被拒绝的次数
PostgreSQL:SELECT datname, numbackends, xact_commit, xact_rollback,
conflicts, temp_files, deadlocks FROM pg_stat_database
WHERE datname = current_database();
 
【候选原因与判定补充】
- 连接未关闭或连接泄漏
- 每请求新建连接,未做池化
- 连接池总上限(单池 × 副本数)大于数据库容量
- 慢查询或锁等待占住会话
- 发布扩容后应用副本数增加但单池大小未缩减
- 健康检查或探针连接过多
- 代理与数据库连接复用失配
- 空闲事务长期占用连接
注意:PostgreSQL 的 pg_stat_database.sessions 只统计成功建立的会话,被拒绝的连接
  不计入,不能用它证明没有超限。
  平台连接数指标的采样间隔可能掩盖短时尖峰,判定触顶需以 DBA 粘贴的瞬时值补证。

2.5 解决方案

先限流、降低重试并修复泄漏;必要时在确认事务影响后终止明确的异常会话。KILL <PID>、SELECT pg_terminate_backend(<PID>) 均为 [高风险]:会回滚事务、造成应用错误并可能增加 IO,只能在确认会话所有者、事务内容、回滚成本和重试影响后执行。

提高 max_connections 是 [变更]:必须先核算每连接内存、工作内存、线程/进程开销、代理能力和实例规格,并验证是否需重启;它不是连接泄漏的长期修复。优先采用连接池/代理、全局连接预算和排队背压。

2.6 验证与预防

确认连接成功率恢复,当前/峰值连接留有按历史波动和故障切换场景计算的余量,且连接来源分布合理。为每个服务设全局连接预算:实例数 × 单池上限 + 运维保留;监控连接使用率、创建速率、池等待、空闲事务和拒绝数;为 DBA 预留独立管理入口。

3. 认证失败

3.1 现象描述

出现 MySQL Access denied for user、PostgreSQL password authentication failed、no pg_hba.conf entry,或同一账号在部分来源/数据库成功、部分失败。也可能表现为“能连上但无权访问对象”,这属于授权问题而非认证问题。

3.2 常见原因

密码轮换不同步;账号锁定或过期;MySQL user@host 匹配到意外账户、驱动不支持认证插件;PostgreSQL HBA 规则顺序/网段/数据库不匹配;连错实例或数据库;只授予对象权限但未授予登录能力;白名单未覆盖 NAT 后真实出口。

3.3 排查步骤

3.3.1 账号密码验证

  • 目的:确认账号存在、未锁定/未过期、认证插件与客户端兼容,且当前凭据版本与数据库一致。

  • MySQL 8.0 [只读]

SELECT user, host, plugin, account_locked, password_expired,
       password_last_changed, password_lifetime
FROM mysql.user WHERE user = '<USER>';
SELECT CURRENT_USER() AS matched_account, USER() AS client_claim;
  • PostgreSQL 14+ [只读]
SELECT rolname, rolcanlogin, rolvaliduntil, rolconnlimit
FROM pg_roles WHERE rolname = '<USER>';
SELECT current_user, session_user;
  • 关键判断字段:account_locked='N'、password_expired='N'、plugin 被驱动支持(如 caching_sha2_password 需较新驱动);PostgreSQL rolcanlogin=true、rolvaliduntil 未过期;CURRENT_USER() 与预期账号一致,说明没有匹配到通配账户。

  • 下一步:账号锁定/过期→按身份流程解锁或轮换([变更],不共享超级账号);插件不兼容→升级驱动或迁移认证方式;账号状态正常仍失败→“账号权限与白名单检查”;凭据疑似不同步→通过密钥管理系统灰度轮换,禁止在工单中写明文口令。

3.3.2 账号权限与白名单检查

  • 目的:区分“认证不通过”与“认证通过但无库/表权限”,并确认来源地址被账号规则或平台白名单允许。

  • 云控制台/网络侧方法(厂商中立)[只读]:核对实例白名单/访问控制列表中的网段是否覆盖应用真实出口地址(含 NAT、SNAT、容器出口、跨可用区节点);确认白名单分组与当前实例、当前端点绑定。

  • MySQL 8.0 [只读]

SHOW GRANTS FOR '<USER>'@'<HOST>';
SELECT user, host, plugin FROM mysql.user
WHERE user IN ('<USER>','') ORDER BY host;
SELECT * FROM information_schema.schema_privileges WHERE grantee LIKE '%<USER>%';
  • PostgreSQL 14+ [只读]
-- PostgreSQL 14 的 pg_hba_file_rules 没有 rule_number 列(rule_number/file_name 是 PG16 新增)
SELECT line_number, type, database, user_name, address, netmask, auth_method, options, error
FROM pg_hba_file_rules ORDER BY line_number;
SELECT datname, datallowconn, datconnlimit, has_database_privilege('<USER>', datname, 'CONNECT') AS can_connect
FROM pg_database WHERE datname = '<DB>';
  • 注意:pg_hba_file_rules 在 PostgreSQL 14 默认仅超级用户可读(pg_monitor 不足);它反映的是 pg_hba.conf 文件的当前内容,而不是已 reload 生效的规则,文件改过但未 reload 时二者会不一致(可用 pg_conf_load_time() 与最近一次 reload 时间对照)。托管实例通常不暴露该文件,应以控制台白名单/参数配置为准。PG16 起该视图另有 rule_number、file_name 列。

  • 关键判断字段:MySQL 中 user@host 的匹配优先级(更具体的 host 优先,空用户名的匿名账号会抢占匹配);PostgreSQL 按 line_number 升序的首条匹配 HBA 规则生效、address/netmask 是否覆盖客户端地址、auth_method 是否与客户端一致、error 列是否提示规则解析失败;datallowconn、CONNECT 权限与 datconnlimit。

  • 下一步:白名单不含来源→按最小网段补充([变更]);HBA 规则顺序错误→最小化调整并保留备用管理会话;仅缺对象权限→按最小权限授予,不要误改认证配置;来源与规则都正确→“连接地址与端口匹配确认”。

3.3.3 连接地址与端口匹配确认

  • 目的:排除“认证失败其实是连错实例/连错库/连到只读端点或代理”的情况;同名账号在不同实例上的口令往往不同。

  • MySQL 8.0 [只读]

SELECT @@hostname AS server_host, @@port AS server_port,
       @@server_id AS server_id, DATABASE() AS current_db,
       @@read_only AS read_only;
  • PostgreSQL 14+ [只读]
SELECT inet_server_addr() AS server_addr, inet_server_port() AS server_port,
       current_database() AS current_db, pg_is_in_recovery() AS in_recovery,
       inet_client_addr() AS client_addr;
  • 关键判断字段:服务器标识(@@hostname/@@server_id/inet_server_addr())是否是目标实例;current_db 是否是账号被授权的库;inet_client_addr() 是否等于白名单登记地址(不等说明经过 NAT 或代理);read_only/in_recovery 是否说明连到了只读节点。

  • 注意:inet_client_addr()、inet_server_addr()、inet_server_port() 在 Unix domain socket 连接下返回 NULL,此时 NULL 只说明是本机套接字连接,不能据此判定 NAT、代理或端口映射。

  • 下一步:连错实例/端点→修正配置并核对凭据来源;连到只读节点→改用写端点;客户端地址与白名单不符→回到“账号权限与白名单检查”与“网络连通性故障”场景的“数据库白名单配置检查”;全部匹配仍失败→安全重置凭据并检查时钟偏差、密钥管理系统同步与审计日志。

3.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"认证失败"场景(连接类故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
账号与库:<USER> / <DB>;是否新账号或刚轮转过凭据:是 / 否
 
【前置检查补充】
0.2 需确认的日志:实例错误日志与审计日志(审计或 SQL 洞察需已开启)。
0.7 审计日志是否覆盖故障窗口;未开启时认证失败明细只能由 DBA 提供。
  CMS2_WORKSPACE 路由下可用 starops observe entity log-data 查审计日志投递库。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  本场景无直接对应的性能 Key,以日志与审计为主;MySQL_Sessions 仅用于侧证连接量是否同时异常。
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:(若暴露)连接失败率与失败计数。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):错误日志与审计日志中的认证失败条目(错误码、账号、
  来源地址、时间分布)。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:凭据轮转、白名单变更、参数组变更、密钥管理同步失败。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:账号与权限清单、白名单、是否强制加密传输。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
需 DBA 执行并粘贴:账号状态与认证插件、HBA 规则顺序、连接实际落到的库与角色。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 查错误与审计日志,按错误码分类(MySQL 1045、密码过期、账号锁定;
     PostgreSQL 28P01、28000)并统计账号与来源地址分布。
  2) 判断是全量失败还是部分节点失败:按来源地址聚合,比对成功与失败节点的出口地址。
  3) 查事件与 OpenAPI:窗口内是否轮转凭据、改过白名单或强制加密开关。
  4) 从 ARMS 取失败样本的异常类型,区分认证失败与 TLS 握手失败。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 账号状态与认证插件:
   MySQL:SELECT user, host, plugin, password_expired, account_locked,
   password_lifetime FROM mysql.user WHERE user = '<USER>';
   PostgreSQL:SELECT rolname, rolcanlogin, rolvaliduntil FROM pg_authid
   WHERE rolname = '<USER>';
2) PostgreSQL HBA 规则(PG14 起该视图仅超级用户可读,pg_monitor 不足;
   反映文件内容而非已生效规则):
   SELECT line_number, type, database, user_name, address, auth_method, error
   FROM pg_hba_file_rules ORDER BY line_number;
3) 连接实际落点(确认库、角色、来源地址与授权范围一致):
   MySQL:SELECT current_user(), user(), @@hostname, database();
   PostgreSQL:SELECT current_user, current_database(), inet_client_addr(),
   inet_server_addr();  -- Unix socket 连接下 inet_* 返回 NULL
 
【候选原因与判定补充】
- 密码轮换不同步
- 账号锁定或已过期
- MySQL 的 user@host 匹配到意外账户,或驱动不支持所需认证插件
- PostgreSQL 的 HBA 规则顺序、网段或数据库不匹配
- 连错实例或数据库
- 只授予对象权限但未授予登录能力
- 白名单未覆盖 NAT 后的真实出口地址
密码错误与无匹配 HBA 条目是两类问题:前者改凭据,后者改 HBA 或白名单。
  部分节点失败时优先怀疑 host 通配范围与客户端出口 IP 变化,可用日志与链路里的客户端
  地址侧证。

3.5 解决方案

通过密钥管理系统轮换凭据并灰度更新应用;按最小权限修复账号、来源和数据库授权。修改用户、HBA、白名单或认证插件均为 [变更],应先建立备用管理会话,防止把所有管理员锁在外部。不得通过关闭认证、放开 0.0.0.0/0 白名单或共享超级账号止损。

3.6 验证与预防

从实际应用来源验证登录、目标数据库和最小业务语句;确认旧凭据按计划失效且无持续失败。建立凭据双版本轮换、到期告警、授权审计和非生产兼容性测试;错误日志与工单中隐藏口令和敏感连接串。

4. SSL/TLS 故障

4.1 现象描述

报证书未知、主机名不匹配、证书过期、协议/密码套件不兼容,或服务端强制 TLS 而客户端使用明文;也可能能连接但未达到要求的身份校验级别(加密了但没验证服务端身份)。

4.2 常见原因

CA 轮换未同步;客户端时钟偏差;使用 IP 或别名导致主机名校验失败;旧驱动不支持服务端协议;代理证书链不完整;将“加密”误认为“已验证服务端身份”;容器镜像内 CA 包过期。

4.3 排查步骤

4.3.1 SSL 证书有效性检查

  • 目的:确认服务端证书在有效期内、SAN 覆盖所用连接地址、且未被吊销或提前轮换。

  • 通用 [只读]

# [只读] 仅读取公开证书信息,不提供任何凭据
openssl s_client -connect <HOST>:<PORT> -servername <HOST> </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName
  • MySQL 8.0 [只读]
-- have_ssl 自 MySQL 8.0.26 起已弃用,不要再用它判断是否启用 TLS
SHOW VARIABLES WHERE Variable_name IN ('ssl_ca','ssl_cert','ssl_key','tls_version','require_secure_transport');
SHOW STATUS LIKE 'Ssl_%';
-- performance_schema.tls_channel_status 为 8.0.21+ 提供各通道 TLS 生效状态与证书属性
SELECT CHANNEL, PROPERTY, VALUE FROM performance_schema.tls_channel_status
WHERE PROPERTY IN ('Enabled','ssl_ca','Ssl_cipher','tls_version',
                   'Ssl_server_not_before','Ssl_server_not_after');
  • PostgreSQL 14+ [只读]
SHOW ssl;
SELECT name, setting FROM pg_settings
WHERE name IN ('ssl_cert_file','ssl_key_file','ssl_ca_file','ssl_min_protocol_version');
  • 关键判断字段:notBefore/notAfter 与当前时间的关系(同时确认客户端时钟无偏差);subjectAltName 是否包含实际使用的 <HOST>(用 IP 连接时需 IP SAN);MySQL Ssl_server_not_after;服务端是否启用 TLS 以 performance_schema.tls_channel_status(8.0.21+)的 Enabled 或 SHOW STATUS LIKE 'Ssl_%' 为准(have_ssl 自 8.0.26 起已弃用,不作判据);PostgreSQL 侧看 ssl=on。

  • 下一步:证书过期或即将过期→安排轮换([变更],新旧 CA 并行灰度);SAN 不含主机名→改用证书覆盖的正式端点;服务端未启用 TLS→按合规要求开启;证书有效但仍报错→“客户端 SSL 配置核对”、“证书链与 CA 信任验证”。

4.3.2 客户端 SSL 配置核对

  • 目的:确认客户端确实以要求的加密与校验级别连接,而不是静默降级为明文或“仅加密不校验”。

  • 应用侧方法(厂商中立)[只读]:核对驱动参数——MySQL --ssl-mode(DISABLED/PREFERRED/REQUIRED/VERIFY_CA/VERIFY_IDENTITY)、JDBC sslMode/useSSL/verifyServerCertificate;PostgreSQL sslmode(disable…verify-full)、sslrootcert;确认容器镜像内 CA 包与挂载路径存在且权限受限。

  • MySQL 8.0 [只读]

mysql --ssl-mode=VERIFY_IDENTITY --ssl-ca=<PATH> \
  -h <HOST> -P <PORT> -u <USER> -p -e "STATUS; SHOW STATUS LIKE 'Ssl_%';"
  • PostgreSQL 14+ [只读]
psql -W "host=<HOST> port=<PORT> dbname=<DB> user=<USER> sslmode=verify-full sslrootcert=<PATH>" \
  -c "SELECT ssl, version, cipher, bits FROM pg_stat_ssl WHERE pid = pg_backend_pid();"
  • 关键判断字段:MySQL STATUS 输出的 SSL: Cipher in use 非空、Ssl_version 符合策略;PostgreSQL pg_stat_ssl.ssl=true、version、cipher、bits;应用会话与 DBA 客户端结论必须一致,抽查应用侧真实会话而非只测本地客户端。

  • 下一步:应用未加密→修正驱动参数并按需开启服务端强制(require_secure_transport/HBA hostssl,均为 [变更]);加密但未校验身份→提升到 VERIFY_IDENTITY/verify-full;协议版本不兼容→升级驱动/运行时并灰度;报证书不受信→“证书链与 CA 信任验证”。

4.3.3 证书链与 CA 信任验证

  • 目的:确认客户端的信任锚能完整验证服务端链路,包括代理终止 TLS 时的两段链路各自的证书责任边界。

  • 通用 [只读]

# [只读] 查看完整链与验证结果;返回码 0 表示验证通过
openssl s_client -connect <HOST>:<PORT> -servername <HOST> -showcerts -verify_return_error </dev/null
# 上一条只把链打到屏幕;openssl verify 需要文件输入,先把服务端证书链落盘
openssl s_client -connect <HOST>:<PORT> -servername <HOST> -showcerts </dev/null 2>/dev/null \
  | sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' > /tmp/server-chain.pem
openssl verify -CAfile <PATH> /tmp/server-chain.pem
  • MySQL 8.0 [只读]
SHOW VARIABLES WHERE Variable_name IN ('ssl_ca','ssl_capath','ssl_crl','ssl_crlpath','tls_ciphersuites');
SHOW STATUS LIKE 'Ssl_verify%';
  • PostgreSQL 14+ [只读]
SELECT a.pid, a.usename, a.application_name, s.ssl, s.version, s.cipher, s.client_dn, s.issuer_dn
FROM pg_stat_activity a JOIN pg_stat_ssl s USING (pid)
WHERE a.pid = pg_backend_pid();
  • 关键判断字段:Verify return code: 0 (ok);链中是否缺少中间 CA;托管服务是否使用厂商 CA 包(需从官方渠道下载并校验指纹);代理终止 TLS 时数据库视图只反映“代理→后端”这一段。

  • 注意:pg_stat_ssl 只能用于确认本连接是否加密及其协议版本、密码套件、位数,以及服务端是否收到并解析了客户端证书——client_dn/client_serial/issuer_dn 描述的是客户端提交的证书及其签发者,服务于服务端校验客户端证书(hostssl ... clientcert=verify-full 场景),不能用来判断“客户端是否信任服务端证书”。服务端证书链的信任结论只能由客户端侧得出:使用 sslmode=verify-full 连接成功,并配合 openssl s_client -verify_return_error/openssl verify 的返回码确认。

  • 下一步:链不完整→补全中间 CA/使用厂商 CA 包;信任锚缺失→分发受信 CA(限权 0600);代理段与后端段策略不一致→分别核对并明确责任边界;需临时降级校验→仅经安全审批、限时限源,并登记为待整改项。

4.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"SSL/TLS 故障"场景(连接类故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
连接串的 sslmode 或 ssl-mode:<VALUE>;是否强制加密传输:是 / 否
 
【前置检查补充】
0.2 需确认的日志:实例错误日志。
0.7 服务端证书信息是否可通过 OpenAPI 读取;不可得时由 DBA 用 s_client 采集证书链。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  本场景无直接对应的性能 Key,证据以错误日志、链路异常类型与证书信息为主。
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:本场景无直接指标,以失败率与日志为主。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):错误日志中的 TLS 握手失败、证书过期、协议或套件不
  匹配记录。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:托管侧证书轮转、强制加密开关变更。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:实例 SSL 开关、服务端证书有效期与 CA 信息、是否强制加密。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
降级兜底:服务端证书信息若无法通过 OpenAPI 获取,必须由运维用 s_client
  采集;两者都拿不到时证书类结论标注为未验证。
需 DBA 执行并粘贴:服务端证书有效期与 SAN、证书链验证结果、服务端与账号的加密要求。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 调 OpenAPI 读实例 SSL 开关与服务端证书有效期,确认是否在故障窗口内到期或刚轮转。
  2) 查错误日志,按过期 / 主机名不匹配 / CA 不受信任 / 协议或套件不匹配分类计数。
  3) 从 ARMS 取异常类型与客户端分布,确认是否只有特定版本或特定节点失败。
  4) 查事件:窗口内的证书轮转与加密相关参数变更。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 服务端证书链与有效期(在能连通实例的运维机执行):
   openssl s_client -connect <HOST>:<PORT> -servername <HOST> -showcerts \
   </dev/null 2>/dev/null | sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' \
   > /tmp/server-chain.pem
   openssl x509 -in /tmp/server-chain.pem -noout -dates -subject -ext subjectAltName
   openssl verify -CAfile <PATH> /tmp/server-chain.pem   # 返回码 0 表示链完整且受信任
2) 服务端与账号的加密要求:
   MySQL:SHOW VARIABLES WHERE Variable_name IN ('have_ssl','ssl_ca','ssl_cert',
   'require_secure_transport');  SHOW CREATE USER '<USER>'@'<HOST>';
   PostgreSQL:SHOW ssl; SHOW ssl_min_protocol_version;
   SELECT ssl, version, cipher, client_dn FROM pg_stat_ssl
   WHERE pid = pg_backend_pid();  -- client_dn 描述客户端证书,不是服务端
 
【候选原因与判定补充】
- CA 轮换未同步到客户端
- 客户端时钟偏差导致有效期校验失败
- 使用 IP 或别名连接,导致主机名校验失败
- 旧驱动不支持服务端要求的协议或密码套件
- 代理证书链不完整,缺中间证书
- 把"已加密"误认为"已验证服务端身份"(sslmode 不足)
- 容器镜像内的 CA 包过期
五类分开判:证书过期、主机名不匹配、CA 不受信任、协议或套件不匹配、
  要求加密但未启用。只有部分客户端失败时优先查该客户端的 CA bundle 与 TLS 版本下限,
  不要先改服务端。

4.5 解决方案

优先升级信任链和驱动,使用 VERIFY_IDENTITY/verify-full。更换证书、CA、TLS 最低版本或强制加密为 [变更],需支持新旧 CA 并行的灰度期和回滚。临时降低校验等级会暴露中间人风险,只能经安全审批、限时、限源使用,不能成为长期方案。

4.6 验证与预防

验证完整链路的协议、密码套件、主机名和到期日;抽查应用连接而非只测 DBA 客户端。建立证书到期提前告警、CA 轮换演练、驱动兼容矩阵和禁止明文连接策略。

5. 网络连通性故障

5.1 现象描述

连接间歇超时、跨可用区时延抖动、部分应用节点可连而部分不可连、长连接被重置。数据库本身健康,但端到端路径异常。

5.2 常见原因

安全组/ACL 规则漂移;路由或 DNS 缓存异常;VPC 对等/专线/终端节点异常;NAT 端口耗尽;MTU/分片问题;代理、负载均衡或防火墙提前回收空闲连接;跨区域链路抖动;客户端超时设置相互矛盾。

5.3 排查步骤

5.3.1 VPC/子网配置检查

  • 目的:确认应用与数据库在网络拓扑上确实可互通:同 VPC 或已建立对等/专线/网关,子网路由指向正确,跨可用区路径符合设计。

  • 云控制台方法(厂商中立)[只读]:核对实例所属 VPC/子网/可用区与应用一致;检查路由表是否有到目标网段的有效条目;确认对等连接、专线、VPN、终端节点(Endpoint/私有链接)状态为可用;检查是否存在网段重叠导致路由不可达。

# [只读] 在应用主机侧验证本机网段、路由与实际出口路径
ip -brief address
ip route get <HOST>
traceroute -n -T -p <PORT> <HOST>
  • MySQL 8.0 [只读]:从数据库侧看到的客户端地址可反证实际路径与 NAT 行为。
SELECT SUBSTRING_INDEX(host,':',1) AS client_ip, COUNT(*) AS sessions
FROM information_schema.processlist GROUP BY client_ip ORDER BY sessions DESC;
  • PostgreSQL 14+ [只读]
SELECT client_addr, count(*) AS sessions, min(backend_start) AS oldest_session
FROM pg_stat_activity WHERE client_addr IS NOT NULL
GROUP BY client_addr ORDER BY sessions DESC;
  • 关键判断字段:ip route get 的出口网卡/网关是否符合设计;traceroute -T 到端口的最后可达跳;数据库看到的 client_addr 是应用真实地址还是 NAT/代理地址;失败节点与成功节点在子网、可用区、路由表上的差异。

  • 下一步:路由缺失或对等/专线异常→由网络负责人按最小范围修复([变更]);仅部分子网失败→对比该子网的路由与 ACL,进入“安全组规则检查”;路径正常→“数据库白名单配置检查”、“DNS 解析验证”。

5.3.2 安全组规则检查

  • 目的:确认双向的安全组/网络 ACL/主机防火墙都放通了数据库端口,并排除规则漂移与优先级覆盖。

  • 云控制台方法(厂商中立)[只读]:检查数据库侧入方向规则是否放通应用来源(安全组引用或最小网段)与目标端口;检查应用侧出方向规则;检查子网级 ACL 的允许/拒绝顺序与优先级;查看规则最近变更记录,定位漂移时间点。

# [只读] 区分“被丢弃(超时)”与“被拒绝(RST)”;并检查主机本地防火墙
nc -vz -w 5 <HOST> <PORT>
sudo iptables -L -n --line-numbers 2>/dev/null | head -50
  • MySQL 8.0 [只读]:数据库侧统计可反映握手是否根本没有到达。
SHOW GLOBAL STATUS WHERE Variable_name IN
('Connections','Aborted_connects','Aborted_clients','Connection_errors_accept','Connection_errors_internal');
  • PostgreSQL 14+ [只读]
SELECT datname, sessions, sessions_abandoned, sessions_fatal, sessions_killed
FROM pg_stat_database WHERE datname = '<DB>';
  • 关键判断字段:nc 超时(策略丢弃)vs refused(无监听或主动拒绝);数据库侧 Connections/sessions 在故障窗口的增量;规则变更时间是否与故障起点吻合。

  • 注意:PostgreSQL 14 的 pg_stat_database.sessions 只统计成功建立的会话——被 pg_hba 拒绝、认证失败、TLS 握手失败的连接其实已经到达数据库,但不计入该列。因此 sessions 无增长只能说明“没有成功建连”,不能推断“未到达数据库”;要区分“未到达”与“到达但被拒”,需结合服务端日志(临时开启 log_connections,属 [变更])与其中的 FATAL: no pg_hba.conf entry、认证失败、TLS 握手失败记录,MySQL 侧可对照 Aborted_connects、Connection_errors_* 与错误日志。

  • 下一步:规则缺失→按最小网段与最小端口放通([变更],禁止 0.0.0.0/0);ACL 拒绝规则优先级过高→调整顺序;握手已到达数据库但失败→“认证失败”场景或“SSL/TLS 故障”场景;规则正确仍不通→“数据库白名单配置检查”、“DNS 解析验证”。

5.3.3 数据库白名单配置检查

  • 目的:托管数据库通常有独立于安全组的“白名单/访问控制”,需确认它覆盖了应用的真实出口地址与所用端点。

  • 云控制台方法(厂商中立)[只读]:核对实例白名单分组内容、生效端点(内网/外网/代理/只读)、是否引用了过期的临时地址;确认容器/弹性伸缩场景下新增节点的出口网段已纳入;确认变更未被后续操作覆盖。

  • MySQL 8.0 [只读]:数据库账号层面的 host 限制与平台白名单是两道关卡,需同时满足。

SELECT user, host FROM mysql.user WHERE user = '<USER>' ORDER BY host;
SELECT USER() AS client_claim, CURRENT_USER() AS matched_account,
       SUBSTRING_INDEX(USER(),'@',-1) AS seen_client_host;
  • PostgreSQL 14+ [只读]
-- PG14 无 rule_number 列,按 line_number 排序;rule_number/file_name 为 PG16 新增
SELECT line_number, type, database, user_name, address, netmask, auth_method, options, error
FROM pg_hba_file_rules ORDER BY line_number;
SELECT inet_client_addr() AS seen_client_addr;
  • 注意:pg_hba_file_rules 在 PostgreSQL 14 默认仅超级用户可读(pg_monitor 不足),且反映 pg_hba.conf 文件当前内容而非已 reload 生效的规则;托管实例应以控制台白名单/参数配置为准。inet_client_addr() 在 Unix domain socket 连接下返回 NULL。

  • 关键判断字段:数据库实际看到的客户端地址(seen_client_host/inet_client_addr())是否在白名单与账号 host/HBA address 范围内(HBA 以 line_number 升序的首条匹配规则生效);是否存在 NAT 导致地址与预期不同;白名单是否绑定到了当前使用的端点。

  • 下一步:白名单缺失→按最小网段补充([变更],并登记有效期与负责人);账号 host/HBA 不匹配→按“认证失败”场景的“账号权限与白名单检查”修正;地址与预期不符→核对 NAT/SNAT 与容器出口策略;白名单正确仍失败→“DNS 解析验证”与“安全组规则检查”。

5.3.4 DNS 解析验证

  • 目的:确认各来源解析到的是预期端点地址,排除私网 DNS 漂移、缓存陈旧、解析到公网地址或故障切换后 TTL 未过期。

  • OS 方法(厂商中立)[只读]:在每类应用节点上分别解析并比对;确认使用的是 VPC 内私网解析器;检查应用运行时的 DNS 缓存(JVM networkaddress.cache.ttl、容器 ndots/search 配置、本地缓存守护进程)。

# [只读] 比对系统解析与权威解析,并观察 TTL
getent ahosts <HOST>
dig +noall +answer +ttlid <HOST>
cat /etc/resolv.conf
  • MySQL 8.0 [只读]:解析结果需与数据库实际身份一致。
SELECT @@hostname AS server_host, @@server_id AS server_id, @@read_only AS read_only;
  • PostgreSQL 14+ [只读]
SELECT inet_server_addr() AS server_addr, pg_is_in_recovery() AS in_recovery;
  • 关键判断字段:不同节点解析结果是否一致且属于预期网段;TTL 是否过长导致切换后仍指向旧节点;解析地址对应的实例角色(读写/只读)是否符合调用方预期;/etc/resolv.conf 是否指向预期解析器。

  • 下一步:解析漂移→修正 DNS 记录并缩短 TTL([变更]);应用缓存过久→调整客户端 DNS 缓存策略并支持重连;解析到只读节点→修正端点用途;解析正常但仍抖动→按本场景“解决方案”处理链路层重传、MTU、NAT 空闲回收。

5.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"网络连通性故障"场景(连接类故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【前置检查补充】
0.2 需确认的日志:实例错误日志。
0.7 应用侧是否有人可在与应用相同网络位置执行探测;没有则链路结论无法闭环。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_NetworkTraffic —— 每秒进出流量(KB/s)
  MySQL_Sessions —— 当前活跃连接数与当前总连接数(个)
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:实例可用性、连接数与网络流量类指标。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):错误日志中的连接中断与重置记录及其时间分布。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:网络维护、专线或对等连接异常、代理端点健康变化、主备切换。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:VPC 与子网、安全组、白名单、端点清单与代理状态。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
UModel 拓扑:实例与调用它的应用、所在可用区的依赖关系。
  降级兜底:RTT、重传、丢包这类链路质量数据通常不在数据库监控里;
  若 ARMS 与应用侧探测都没有,链路质量结论标注为不可得,转由网络负责人排查。
需 DBA 执行并粘贴:应用侧同网络位置的 DNS 与 TCP 探测结果、数据库实际看到的客户端地址。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 调 OpenAPI 核对 VPC、子网、安全组、白名单与端点清单,与客户端真实出口 IP 比对。
  2) 查事件:网络维护、专线或对等连接异常、代理端点健康变化。
  3) 从 ARMS 取建连超时率与 RTT 分布,按可用区与客户端分组,判断全局还是局部。
  4) 查错误日志中连接被重置或中断的时间分布,与网络事件对齐。
  5) 用 UModel 列出受影响的应用与调用路径,界定影响面。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 应用侧链路探测(必须在与应用相同的容器或子网内执行):
   getent hosts <HOST>            # 解析结果是否落在预期网段
   ping -c 4 <HOST>               # ICMP 被禁不能作为 TCP 不通的证据
   nc -vz -w 5 <HOST> <PORT>      # 超时多为策略丢弃,立即 refused 多为无监听
2) 数据库实际看到的客户端地址(核对 NAT 后出口是否在白名单内):
   MySQL:SELECT user(), @@hostname;
   PostgreSQL:SELECT inet_client_addr(), inet_server_addr();
 
【候选原因与判定补充】
- 安全组或 ACL 规则漂移
- 路由或 DNS 缓存异常
- VPC 对等、专线或终端节点异常
- NAT 端口耗尽
- MTU 或分片问题
- 代理、负载均衡或防火墙提前回收空闲连接
- 跨区域链路抖动
- 客户端各层超时设置相互矛盾
区分完全不可达、间歇丢包、握手后被中断三类,证据要求不同。
  Agent 只能证明平台侧策略是否放通,端到端可达性必须由应用侧探测输出佐证。

5.5 解决方案

修复最小范围网络策略、路由和 DNS;对连接池设置低于中间设备空闲回收时间的保活/最大生命周期,设置分层且一致的连接、语句和请求超时。网络策略和 MTU 修改属于 [变更],需双向流量验证和快速回滚;不要简单无限增大超时掩盖丢包。

5.6 验证与预防

在各可用区真实应用节点执行连续合成事务,确认成功率、建连时延和重传回到各自历史基线。持续监控 DNS、NAT 端口、TCP 重置、跨区时延;在网络变更前后自动执行连通性矩阵,并把安全组/白名单纳入配置基线审计。

二、性能类故障

1. 慢查询

1.1 现象描述

接口延迟、超时或吞吐下降;慢日志/统计扩展显示特定 SQL 累计耗时或尾延迟上升。慢查询可能来自执行计划、锁等待、缓存冷却、数据量变化或资源争用,必须先证明“慢在哪一层”。

1.2 常见原因

1.2.1 全表扫描

  • 判断依据:MySQL 计划出现 type=ALL、Using where 且 rows 接近表行数;PostgreSQL 出现 Seq Scan 且 pg_stat_user_tables.seq_tup_read 快速增长;SUM_ROWS_EXAMINED 远大于 SUM_ROWS_SENT。

  • 注意边界:小表或高选择率查询的全表扫描可能是最优计划,不能一概而论;判定需结合表规模、选择性和实际耗时。

  • 处置指向:先确认谓词可索引化(见“索引使用情况分析”),再评估新建索引的写放大与空间成本;无法索引化时考虑改写查询、限制返回集或预聚合。

1.2.2 索引失效

  • 判断依据:索引存在但计划不使用;常见触发条件包括对索引列做函数/表达式运算、隐式类型转换(字符串列传入数字)、字符集或排序规则不一致、前导列缺失、LIKE '%x'、OR 条件无法合并、数据分布导致优化器认为回表更贵。

  • 验证方法:对比“原谓词”与“显式转换后谓词”的计划差异;检查列与参数的类型/字符集;查看 pg_stats.correlation 判断回表成本。

  • 处置指向:修正参数类型与字符集、改写谓词使索引可用、必要时建立表达式索引或覆盖索引([变更])。

1.2.3 不合理的 JOIN

  • 判断依据:连接顺序把大表放在驱动位置、缺少连接列索引、连接算法(Nested Loop/Hash/Merge)与数据量不匹配、笛卡尔积或缺失连接条件、多层嵌套视图导致谓词无法下推。

  • 验证方法:在 EXPLAIN 输出中检查每层的估算行数放大倍数与连接算法;核对连接列两侧的类型、索引与统计信息。

  • 处置指向:为连接列补索引、拆分复杂查询、减少中间结果、必要时改写为分步查询或物化中间结果;避免在生产用优化器提示掩盖统计问题。

1.2.4 数据量增长导致性能退化

  • 判断依据:同一 SQL 指纹的 mean_exec_time 随时间线性/超线性上升;table_rows/n_live_tup、表与索引大小持续增长;缓存命中下降、物理读上升;分区或归档策略缺失。

  • 验证方法:对比多个时间点的表规模、索引规模、缓存命中与该指纹的平均耗时;确认增长是业务自然增长还是清理失效(死元组、无保留策略)。MySQL 8.0 用 information_schema.tables 的 table_rows/data_length 做多点对比前,必须先 SET SESSION information_schema_stats_expiry = 0(绕过数据字典缓存直读存储引擎,有额外开销)或先 ANALYZE TABLE([变更]),否则 information_schema_stats_expiry 默认 86400 秒会让两次采样返回完全相同的缓存值。

  • 处置指向:建立分区/归档/保留策略,评估索引与查询在目标数据量下的表现(上线前做数据规模逼真的计划回归);容量层面参见“磁盘空间不足”场景(存储与日志故障)。

1.3 排查步骤

1.3.1 慢查询日志定位与提取

  • 目的:用累计统计而不是单次最慢定位真正的耗时大户,取得可复现的 SQL 指纹与调用量。

  • MySQL 8.0 [只读]

SHOW VARIABLES WHERE Variable_name IN ('slow_query_log','long_query_time','log_output','log_queries_not_using_indexes');
SELECT DIGEST, DIGEST_TEXT, COUNT_STAR,
       ROUND(SUM_TIMER_WAIT/1e12,3) AS total_s,
       ROUND(AVG_TIMER_WAIT/1e9,3)  AS avg_ms,
       SUM_ROWS_EXAMINED, SUM_ROWS_SENT, FIRST_SEEN, LAST_SEEN
FROM performance_schema.events_statements_summary_by_digest
WHERE SCHEMA_NAME = '<DB>'
ORDER BY SUM_TIMER_WAIT DESC LIMIT 20;
  • PostgreSQL 14+ [只读]
SELECT name, setting FROM pg_settings
WHERE name IN ('log_min_duration_statement','shared_preload_libraries','track_io_timing');
SELECT queryid, calls, total_exec_time, mean_exec_time, stddev_exec_time,
       rows, shared_blks_read, temp_blks_written, query
FROM pg_stat_statements
WHERE dbid = (SELECT oid FROM pg_database WHERE datname = '<DB>')
ORDER BY total_exec_time DESC LIMIT 20;
  • 关键判断字段:total_s/total_exec_time(总贡献)、avg_ms/mean_exec_time(单次成本)、stddev_exec_time(尾延迟波动)、COUNT_STAR/calls(调用量)、SUM_ROWS_EXAMINED 与 SUM_ROWS_SENT 的比值(扫描放大)、FIRST_SEEN/LAST_SEEN 是否与故障窗口吻合。SQL 文本需脱敏后记录。

  • 下一步:少量单次很慢→“执行计划分析(EXPLAIN)”;高频小 SQL 累计大→合并批量/缓存,并查应用 N+1;扫描放大明显→“索引使用情况分析”;temp_blks_written 高→“内存压力或 OOM”场景;统计缺失(未装 pg_stat_statements/未开慢日志)→按 [变更] 流程开启并设自动到期。

1.3.2 执行计划分析(EXPLAIN)

  • 目的:确认访问路径、连接顺序、估算行数与真实数据分布是否吻合,且优先使用不执行语句的计划。

  • MySQL 8.0 [只读]

-- FORMAT=TREE 面向 SELECT;对单表 UPDATE/DELETE 在 8.0 输出不可靠,见下文对 DML 的说明
EXPLAIN FORMAT=TREE SELECT * FROM <SCHEMA>.<TABLE> WHERE <COLUMN> = <VALUE>;
EXPLAIN FORMAT=JSON SELECT * FROM <SCHEMA>.<TABLE> WHERE <COLUMN> = <VALUE>;
  • PostgreSQL 14+ [只读]
EXPLAIN (VERBOSE, COSTS, SETTINGS)
SELECT * FROM <SCHEMA>.<TABLE> WHERE <COLUMN> = <VALUE>;
  • 关键判断字段:访问方式(ref/range/ALL、Index Scan/Seq Scan/Bitmap Heap Scan)、rows 估算与真实量级差距、连接算法与顺序、是否出现 Using temporary/Using filesort/Sort/Hash 溢写、并行度、SETTINGS 显示的非默认优化器参数。

  • 下一步:估算严重失真→“表统计信息检查”;有索引却不用→“索引使用情况分析”;连接顺序或算法不合理→“不合理的 JOIN”;计划正常但仍慢→“锁等待与资源竞争排查”与“I/O 性能异常”场景。

[高风险] EXPLAIN ANALYZE / EXPLAIN (ANALYZE) 会真实执行 SQL;对 INSERT/UPDATE/DELETE 可产生数据变更,对慢查询可能再次压垮生产。仅在确认语句只读、限定数据范围、设置语句超时、评估锁与 IO,并优先在副本/预发执行后使用。MySQL 8.0 的 EXPLAIN ANALYZE 仅支持 SELECT、TABLE 以及多表 UPDATE/DELETE,不支持单表 UPDATE/DELETE;PostgreSQL 可用 EXPLAIN (ANALYZE, BUFFERS)。 分析 DML 计划时:EXPLAIN FORMAT=TREE 对单表 UPDATE/DELETE 在 8.0 不可靠(可能报错或给出无参考价值的输出),应改用 EXPLAIN FORMAT=JSON UPDATE ...、传统 EXPLAIN UPDATE ...,或分析访问路径等价的 SELECT(把 SET 子句换成 SELECT 列、保留同样的谓词)——这三种方式都不执行 DML。

1.3.3 索引使用情况分析

  • 目的:确认现有索引是否被使用、选择性是否足够,以及是否存在冗余索引带来的写放大。

  • MySQL 8.0 [只读]

SHOW INDEX FROM <SCHEMA>.<TABLE>;
SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME,
       COUNT_STAR, COUNT_READ, COUNT_WRITE
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE OBJECT_SCHEMA = '<SCHEMA>' AND OBJECT_NAME = '<TABLE>'
ORDER BY COUNT_STAR DESC;
  • PostgreSQL 14+ [只读]
SELECT indexrelname, idx_scan, idx_tup_read, idx_tup_fetch,
       pg_size_pretty(pg_relation_size(indexrelid)) AS idx_size
FROM pg_stat_user_indexes
WHERE relname = '<TABLE>' ORDER BY idx_scan;
SELECT relname, seq_scan, seq_tup_read, idx_scan
FROM pg_stat_user_tables WHERE relname = '<TABLE>';
  • 关键判断字段:Cardinality/索引选择性;INDEX_NAME IS NULL 的行代表未走索引的表访问;idx_scan = 0 的索引长期未被使用(需覆盖足够观察周期后才能判定冗余);seq_scan 与 idx_scan 的比例;索引大小相对表大小的成本。

  • 下一步:谓词无可用索引→评估新建索引([变更],见本场景“解决方案”);索引存在但未命中→“索引失效”;索引长期零扫描→纳入索引收益/写成本审计后再决定下线;索引正常→“表统计信息检查”。

1.3.4 表统计信息检查

  • 目的:判断优化器估算失真是否源于统计信息陈旧、采样不足或数据分布倾斜。

  • MySQL 8.0 [只读]

-- 前提:information_schema.tables 的 table_rows/data_length/data_free/update_time 等动态列取自数据字典缓存,
-- information_schema_stats_expiry 默认 86400 秒,缓存未过期时多次采样可能返回完全相同的值。
-- 需要实时值时先在本会话关闭缓存(仅影响当前会话,会绕过缓存直读存储引擎、有额外开销);
-- 或先对目标表执行 ANALYZE TABLE(属 [变更],会消耗 CPU/IO 并可能改变计划)。
SET SESSION information_schema_stats_expiry = 0;
SELECT table_schema, table_name, table_rows, avg_row_length, data_length, index_length, update_time
FROM information_schema.tables WHERE table_schema='<SCHEMA>' AND table_name='<TABLE>';
-- InnoDB 持久化统计表在 mysql 库,不在 information_schema
SELECT * FROM mysql.innodb_table_stats WHERE database_name='<DB>' AND table_name='<TABLE>';
SELECT * FROM mysql.innodb_index_stats WHERE database_name='<DB>' AND table_name='<TABLE>';
SELECT * FROM information_schema.statistics WHERE table_schema='<SCHEMA>' AND table_name='<TABLE>';
  • PostgreSQL 14+ [只读]
SELECT relname, n_live_tup, n_dead_tup, n_mod_since_analyze,
       last_analyze, last_autoanalyze, last_vacuum, last_autovacuum
FROM pg_stat_user_tables WHERE relname='<TABLE>';
SELECT attname, n_distinct, null_frac, correlation
FROM pg_stats WHERE schemaname='<SCHEMA>' AND tablename='<TABLE>';
  • 关键判断字段:MySQL 侧看 mysql.innodb_table_stats 的 n_rows(InnoDB 估算行数)、clustered_index_size(聚簇索引页数,反映真实物理规模)、last_update(统计信息最后一次更新时间,距今越远越可疑),以及 mysql.innodb_index_stats 中各索引的 n_diff_pfx%(前缀基数,反映选择性);table_rows/n_live_tup 与真实量级偏差(table_rows 为估算值,且未按上文关闭 information_schema_stats_expiry 或未 ANALYZE TABLE 时可能是最长 24 小时前的缓存值);n_mod_since_analyze 相对表规模的比例;last_analyze/last_autoanalyze 距今时间;n_distinct、null_frac、correlation 反映的分布与物理有序性;n_dead_tup 高会同时影响估算与扫描成本。

  • 下一步:统计明显陈旧→在维护窗口执行 ANALYZE([变更],会消耗 CPU/IO 并可能改变计划,先留存原计划证据);死元组过多→检查 autovacuum 与“长事务”场景(锁与并发故障);分布倾斜→考虑扩展统计、直方图或改写 SQL;统计正常→“锁等待与资源竞争排查”。

1.3.5 锁等待与资源竞争排查

  • 目的:证明 SQL 的耗时是花在执行上还是花在等待锁、IO、缓冲或后台任务上,避免把等待问题当成计划问题优化。

  • MySQL 8.0 [只读]

SELECT EVENT_NAME, COUNT_STAR, ROUND(SUM_TIMER_WAIT/1e12,3) AS total_s
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE COUNT_STAR > 0
ORDER BY SUM_TIMER_WAIT DESC LIMIT 20;
SELECT * FROM sys.innodb_lock_waits ORDER BY wait_age_secs DESC;
  • PostgreSQL 14+ [只读]
SELECT wait_event_type, wait_event, state, COUNT(*) AS sessions,
       max(now()-query_start) AS max_age
FROM pg_stat_activity
WHERE state <> 'idle'
GROUP BY wait_event_type, wait_event, state
ORDER BY sessions DESC;
SELECT pid, pg_blocking_pids(pid) AS blockers, wait_event_type, wait_event, left(query, 120) AS q
FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid)) > 0;
  • 关键判断字段:等待类别分布(wait/io/*、wait/lock/*、Lock、LWLock、IO、IPC);wait_age_secs 与阻塞链;等待占总耗时的比例;同一 SQL 指纹在无竞争时段的耗时对照。

  • 下一步:锁等待占主导→“锁等待”场景(锁与并发故障);IO 等待占主导→“I/O 性能异常”场景;缓冲/内存相关等待→“内存压力或 OOM”场景;无明显等待→回到“执行计划分析(EXPLAIN)”优化计划,并评估“数据量增长导致性能退化”的数据量因素。

1.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"慢查询"场景(性能类故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
目标 SQL 或接口:<VALUE>;相关 SLO:<VALUE>(如 P99 响应时间目标)
 
【前置检查补充】
0.2 需确认的日志:慢日志(slow log)。
0.7 慢日志阈值是否过高导致目标 SQL 未被记录(用 OpenAPI 读参数确认)。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_QPSTPS —— 每秒 SQL 执行数与每秒事务数(个/秒)
  MySQL_SelectScan —— 全表扫描次数(个)
  MySQL_TempDiskTableCreates —— 磁盘上自动创建的临时表数量(个)
  MySQL_MemCpuUsage —— CPU 使用率与内存使用率(%;Serverless 用 MySQL_RCU_MemCpuUsage)
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:QPS/TPS、活跃会话数、CPU 与 IO 使用率。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):慢日志投递库中按 SQL 指纹聚合的耗时、执行次数与扫描行数。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:发版、参数组变更、批量导入、索引或统计信息维护。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:慢日志阈值等参数当前值、实例规格。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
降级兜底:慢日志未开启或阈值过高时,目标 SQL 可能根本没被记录:
  此时不得断言"没有慢查询",应标注慢日志覆盖不全并要求先调阈值(属 [变更])。
需 DBA 执行并粘贴:目标 SQL 的执行计划、索引命中与选择性、统计信息新鲜度。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 从慢日志按 SQL 指纹聚合,取窗口内耗时 P95、执行次数与扫描行数排名前列的指纹。
  2) 与基线比对,判断是新出现的指纹、既有指纹变慢,还是调用量上涨。
  3) 从 ARMS 取该 SQL 所在接口的调用量与耗时,确认是数据库侧变慢还是调用放大(N+1)。
  4) 查事件:窗口前后是否有发版、参数变更、数据导入。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 执行计划(ANALYZE 会真实执行,写语句禁止直接跑;优先在只读副本执行):
   MySQL:EXPLAIN FORMAT=TREE <SQL>;      -- 面向 SELECT,不执行
   EXPLAIN ANALYZE <SQL>;         -- 8.0.18+,会真实执行
   PostgreSQL:EXPLAIN (ANALYZE, BUFFERS, VERBOSE) <SQL>;
注意:EXPLAIN ANALYZE 和 PostgreSQL 的 EXPLAIN (ANALYZE, ...) 会真实执行 SQL,
  必须在只读副本上执行,禁止在主库高负载时执行写语句的 ANALYZE。
  若无只读副本或只读副本不可用,标注执行计划不可得,置信度不得高于中。
2) 索引命中与选择性:
   MySQL:SHOW INDEX FROM <SCHEMA>.<TABLE>;
   SELECT OBJECT_NAME, INDEX_NAME, COUNT_STAR FROM
   performance_schema.table_io_waits_summary_by_index_usage
   WHERE OBJECT_SCHEMA='<SCHEMA>' AND OBJECT_NAME='<TABLE>'
   ORDER BY COUNT_STAR DESC;
   PostgreSQL:SELECT indexrelname, idx_scan, idx_tup_read FROM pg_stat_user_indexes
   WHERE relname='<TABLE>' ORDER BY idx_scan;
3) 统计信息新鲜度:
   MySQL:SET SESSION information_schema_stats_expiry = 0;  -- 默认 86400 秒缓存
   SELECT table_rows, update_time FROM information_schema.tables
   WHERE table_schema='<SCHEMA>' AND table_name='<TABLE>';
   SELECT * FROM mysql.innodb_table_stats WHERE table_name='<TABLE>';
   PostgreSQL:SELECT n_live_tup, n_dead_tup, last_analyze, last_autoanalyze
   FROM pg_stat_user_tables WHERE relname='<TABLE>';
 
【候选原因与判定补充】
- 全表扫描
- 索引失效(类型转换、函数包裹、前导列缺失等)
- 不合理的 JOIN(顺序或算法不当)
- 数据量增长导致性能退化
执行计划类结论必须以 DBA 粘贴的计划为依据,Agent 不得由指标反推计划;
  缺计划时置信度不得高于中,并列入数据缺口。
  判断是单条 SQL 变慢还是整体变慢:后者改按 CPU、内存、I/O 与锁等待方向排查。

1.5 解决方案

先限流、缓存、减少返回数据或回滚问题发布。建索引、改 SQL、更新统计信息均为 [变更]:建索引前评估锁、额外空间、写放大、复制延迟和回滚;ANALYZE TABLE <TABLE> 与 PostgreSQL ANALYZE <SCHEMA>.<TABLE> 也会消耗 CPU/IO 并可能改变计划,应在维护窗口或小范围执行并保留原计划证据。不要因看到全表扫描就机械建索引。

1.6 验证与预防

以相同参数分布和业务流量验证延迟、扫描行、临时文件、锁等待和资源消耗,并观察计划稳定性及复制延迟。上线前做数据规模逼真的计划回归;维护 SQL 指纹基线、统计信息健康度和索引收益/写成本审计。

2. CPU 使用率异常

2.1 现象描述

数据库 CPU 相比自身历史基线持续抬升,队列和延迟同步增加;也可能 CPU 高但吞吐正常,或 CPU 不高却因单核热点、配额节流而慢。

2.2 常见原因

2.2.1 复杂查询或全表扫描

  • 判断依据:少数 SQL 指纹贡献大部分执行时间,SUM_ROWS_EXAMINED/shared_blks_hit 高而返回行少;计划中出现大范围扫描、嵌套循环放大或复杂表达式计算。

  • 验证方法:对照“慢查询”场景的“慢查询日志定位与提取”与“慢查询”场景的“执行计划分析(EXPLAIN)”的指纹与计划;在低峰或副本上复现单条 SQL 的资源消耗。

  • 处置指向:按“慢查询”场景的方案优化计划与索引;短期用限流、缓存或降级减少调用量。

2.2.2 并发过高导致上下文切换

  • 判断依据:活跃并发远超 vCPU 数,单条 SQL 耗时随并发上升而恶化,LWLock/自旋类等待增多,吞吐不再随并发增长甚至下降。

  • 验证方法:对比不同并发水平下的“吞吐-延迟”曲线;观察 Threads_running 与每秒查询数的关系是否已过拐点。

  • 处置指向:在应用与代理层设置并发上限与排队背压,收敛连接池;避免用提高 max_connections 解决并发过载。

2.2.3 排序/聚合操作

  • 判断依据:Created_tmp_disk_tables、Sort_merge_passes、temp_files/temp_bytes、temp_blks_written 增长;计划中出现 Using filesort、Using temporary、Sort、HashAggregate 且数据量大。

  • 验证方法:确认排序/分组列有无可用索引;核对每操作可用工作内存与并发数的乘积是否超出实例内存预算。

  • 处置指向:用索引消除排序、减少分组基数、分页改造;工作内存参数调整属 [变更],须按“每操作 × 并发”建模,参见“内存压力或 OOM”场景。

2.2.4 编译与解析开销

  • 判断依据:大量一次性/未参数化 SQL(指纹数量异常多、每个 calls 很小);MySQL 中 Com_stmt_prepare 与执行次数不匹配;PostgreSQL 中相同结构语句因字面量不同形成大量不同 queryid。

  • 验证方法:统计指纹数量与调用分布;检查应用是否使用预编译语句与参数绑定;观察短连接是否每次重新准备语句。

  • 处置指向:改用参数化/预编译语句并复用连接;减少动态 SQL 拼接;对高频语句评估服务端预处理与缓存策略。

2.3 排查步骤

2.3.1 CPU 使用率趋势分析

  • 目的:先确认 CPU 抬升的形态(阶跃/缓升/周期性/毛刺)、起始时间点,以及是否伴随吞吐同步增长。

  • 云控制台/OS 方法(厂商中立)[只读]:查看实例 CPU 使用率、可运行队列、突发实例的 CPU 积分与节流指标、规格变更与维护事件;有主机权限时用 top/vmstat 1 区分 user/sys/iowait/steal。

  • MySQL 8.0 [只读]

SHOW GLOBAL STATUS WHERE Variable_name IN
('Uptime','Queries','Questions','Com_select','Com_insert','Com_update','Com_delete',
 'Threads_running','Threads_connected','Created_tmp_disk_tables','Select_scan','Sort_merge_passes');
  • PostgreSQL 14+ [只读]
SELECT datname, xact_commit, xact_rollback, tup_returned, tup_fetched,
       tup_inserted, tup_updated, tup_deleted, temp_files, temp_bytes, stats_reset
FROM pg_stat_database WHERE datname = '<DB>';
  • 关键判断字段:两次采样差值算出的每秒事务/查询数;Threads_running 反映的真实并发;tup_returned/tup_fetched 比值反映扫描效率;Created_tmp_disk_tables、temp_bytes 反映排序/聚合落盘;CPU 抬升是否与业务量同比例(同比例多为容量问题,不同比例多为效率问题)。

  • 下一步:负载同比例增长→容量评估与限流;吞吐不变而 CPU 升→“高 CPU 消耗 SQL 定位”;Threads_running 高但吞吐低→“并发连接数与活跃线程分析”;steal/积分节流→平台侧规格问题;iowait 高→“I/O 性能异常”场景。

2.3.2 高 CPU 消耗 SQL 定位

  • 目的:找出对 CPU 时间贡献最大的 SQL 指纹与后台任务,而不是只看当前最慢的一条。

  • MySQL 8.0 [只读]

SELECT DIGEST_TEXT, COUNT_STAR,
       ROUND(SUM_TIMER_WAIT/1e12,3) AS total_s,
       SUM_ROWS_EXAMINED, SUM_ROWS_SENT,
       SUM_CREATED_TMP_DISK_TABLES, SUM_SORT_MERGE_PASSES, SUM_NO_INDEX_USED
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC LIMIT 20;
  • PostgreSQL 14+ [只读]
SELECT queryid, calls, total_exec_time, mean_exec_time, rows,
       shared_blks_hit, shared_blks_read, temp_blks_written, query
FROM pg_stat_statements
ORDER BY total_exec_time DESC LIMIT 20;
SELECT pid, backend_type, state, wait_event_type, wait_event,
       now()-query_start AS age, left(query,120) AS q
FROM pg_stat_activity WHERE state='active' ORDER BY query_start LIMIT 20;
  • 关键判断字段:总执行时间包含等待,需结合等待事件才能近似 CPU 时间;SUM_ROWS_EXAMINED、SUM_NO_INDEX_USED、SUM_SORT_MERGE_PASSES 指向计算型开销;shared_blks_hit 高而 read 低但耗时长通常是纯 CPU 型;backend_type 区分用户会话与后台进程(autovacuum、walwriter、checkpointer)。

  • 下一步:集中在少数指纹→“慢查询”场景;集中在后台维护→调整维护窗口与节奏([变更]);排序/聚合型→“排序/聚合操作”与“内存压力或 OOM”场景;无 SQL 可解释→“系统进程与数据库进程区分”。

2.3.3 并发连接数与活跃线程分析

  • 目的:判断 CPU 抬升是否由并发过高、上下文切换和锁竞争引起,而非单条 SQL 变慢。

  • MySQL 8.0 [只读]

SHOW GLOBAL STATUS WHERE Variable_name IN
('Threads_running','Threads_connected','Threads_created','Table_locks_waited','Innodb_row_lock_waits');
SELECT COUNT(*) AS running, PROCESSLIST_STATE
FROM performance_schema.threads
WHERE TYPE='FOREGROUND' AND PROCESSLIST_COMMAND <> 'Sleep'
GROUP BY PROCESSLIST_STATE ORDER BY running DESC;
  • PostgreSQL 14+ [只读]
SELECT count(*) FILTER (WHERE state='active') AS active,
       count(*) FILTER (WHERE state='idle') AS idle,
       count(*) FILTER (WHERE wait_event_type='Lock') AS waiting_on_lock,
       count(*) FILTER (WHERE wait_event_type='LWLock') AS waiting_lwlock
FROM pg_stat_activity;
SELECT name, setting FROM pg_settings
WHERE name IN ('max_connections','max_parallel_workers','max_parallel_workers_per_gather');
  • 关键判断字段:活跃并发是否远超实例 vCPU 数(并发过高会把 CPU 花在调度与竞争上);Threads_created 增长快说明连接未复用;锁等待与 LWLock 等待数量;并行 worker 配置是否放大了单查询的 CPU 占用。

  • 下一步:并发远超 vCPU→在应用/代理层限制并发并排队(优先应用侧);连接创建速率高→“连接数耗尽”场景(连接类故障);锁等待多→“锁等待”场景(锁与并发故障);并行度过高→评估调低并行参数([变更])。

2.3.4 系统进程与数据库进程区分

  • 目的:确认 CPU 是被数据库主进程消耗,还是被备份代理、日志采集、监控探针、加密/压缩或平台后台任务占用。

  • OS/云控制台方法(厂商中立)[只读]:有主机权限时用 top -H、pidstat 1、ps -eo pid,pcpu,comm --sort=-pcpu | head 查看进程级占用;托管实例无主机权限时,用控制台的进程/会话诊断、备份任务时间表与平台事件对齐时间线。

  • MySQL 8.0 [只读]:区分前台会话与后台线程。

SELECT NAME, TYPE, PROCESSLIST_COMMAND, PROCESSLIST_STATE, COUNT(*) AS threads
FROM performance_schema.threads
GROUP BY NAME, TYPE, PROCESSLIST_COMMAND, PROCESSLIST_STATE
ORDER BY threads DESC LIMIT 30;
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty';
  • PostgreSQL 14+ [只读]
SELECT backend_type, count(*) AS procs,
       count(*) FILTER (WHERE state='active') AS active
FROM pg_stat_activity GROUP BY backend_type ORDER BY procs DESC;
SELECT pid, backend_type, query_start, left(query,100) AS q
FROM pg_stat_activity WHERE backend_type <> 'client backend';
  • 关键判断字段:TYPE='BACKGROUND' 线程或 backend_type 非 client backend 的进程是否占据主要活跃度(autovacuum、checkpointer、walwriter、logical replication launcher);OS 侧非数据库进程(备份、日志代理、杀毒、监控)的 CPU 占用;sys 时间高通常指向系统调用/中断而非 SQL。

  • 下一步:数据库后台任务为主→调整 autovacuum/检查点/备份窗口([变更]);非数据库进程为主→由平台或运维侧治理,不改数据库参数;确认为 SQL 型→回到“高 CPU 消耗 SQL 定位”;无法归因且平台受限→提交工单并附时间线证据。

2.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"CPU 使用率异常"场景(性能类故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【前置检查补充】
0.2 需确认的日志:慢日志与实例错误日志。
0.7 实例是否为突发规格;若是,需确认 CPU 积分与节流指标是否可得。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_MemCpuUsage —— CPU 与内存使用率(%;Serverless 用 MySQL_RCU_MemCpuUsage)
  MySQL_QPSTPS —— 每秒 SQL 执行数与每秒事务数(个/秒)
  MySQL_ThreadStatus —— 活跃线程数与线程连接数(个)
  MySQL_SelectScan —— 全表扫描次数(个)
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:CPU 使用率、活跃会话与线程数、QPS、连接数、(突发规格)CPU
  积分与节流指标。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):慢日志投递库中按指纹聚合的耗时总和;错误日志中的资
  源类告警。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:规格变更、备份、平台维护、参数组变更。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:实例规格与 vCPU、参数组当前值。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
降级兜底:突发规格的 CPU 积分与节流指标不一定暴露;缺失时不得排除节流,
  应标注为未验证并要求托管侧确认。
需 DBA 执行并粘贴:按累计耗时排序的语句摘要、当前活跃线程数与等待事件分布。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 取 CPU 曲线与 QPS、连接数、活跃会话叠加,区分持续走高与瞬时尖刺,并与基线 P95 比对。
  2) 判断是否触达规格上限或突发额度被节流(CPU 积分类指标)。
  3) 从慢日志按指纹聚合耗时总和,列出 CPU 贡献的候选语句。
  4) 查事件:备份窗口、规格变更、参数组变更、平台维护。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 高消耗语句排名:
   MySQL:SELECT DIGEST_TEXT, COUNT_STAR, SUM_TIMER_WAIT, SUM_ROWS_EXAMINED
   FROM performance_schema.events_statements_summary_by_digest
   ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;
   PostgreSQL:SELECT query, calls, total_exec_time, mean_exec_time, rows
   FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;
   -- 需已安装 pg_stat_statements 扩展
2) 并发与等待分布:
   MySQL:SHOW GLOBAL STATUS WHERE Variable_name IN ('Threads_running',
   'Threads_connected','Created_tmp_disk_tables','Select_scan');
   PostgreSQL:SELECT state, wait_event_type, wait_event, count(*)
   FROM pg_stat_activity GROUP BY 1,2,3 ORDER BY 4 DESC;
 
【候选原因与判定补充】
- 复杂查询或全表扫描
- 并发过高导致上下文切换
- 排序与聚合操作消耗 CPU
- 语句编译与解析开销(如未使用预编译、大量硬解析)
CPU 高但 QPS 未涨时优先查计划退化、统计信息陈旧与后台任务,不要先建议升配。
  仅凭指标无法区分有效利用与低效放大,必须结合语句侧证据;缺该证据时明确写出。

2.5 解决方案

优先限流、回滚异常发布、优化高贡献 SQL、分散批任务;确认长期需求后扩容。参数、并行度、实例规格变更为 [变更],须评估重启/切换、成本、计划变化和回滚。不要以固定 CPU 百分比单独判定故障,CPU 接近规格上限但 SLO、队列和吞吐稳定时可能是有效利用。

2.6 验证与预防

验证业务 SLO、运行队列、吞吐/CPU 比和热点 SQL 贡献恢复历史区间;观察完整峰值周期。建立按工作负载分层的基线、发布前压测、查询预算、批任务窗口和扩容触发条件。

3. 内存压力或 OOM

3.1 现象描述

实例可用内存下降、交换/内存回收增多、进程被 OOM、连接断开或重启;查询大量落盘,延迟波动。托管平台可能仅暴露“可用内存”“缓存”“临时文件”等聚合指标。

3.2 常见原因

3.2.1 缓存配置不合理

  • 判断依据:全局缓存过大挤压会话内存与操作系统余量(表现为 OOM 或 swap),或过小导致物理读与 IO 时延上升;参数与实例规格不匹配,或规格缩容后未同步调整。

  • 验证方法:核算缓存 + 会话内存最坏用量 + 平台保留是否超过实例内存;对比缓存调整前后的物理读与 IO 时延。

  • 处置指向:按规格重新建模缓存比例([变更],需确认是否重启),在预发压测验证;过度缩小内存会把问题转化为 IO 故障。

3.2.2 大查询导致内存溢出

  • 判断依据:单条大排序/哈希/聚合/大结果集查询运行期间内存陡增、临时文件暴涨;work_mem 与并行 worker 叠加放大;批处理与在线业务重叠。

  • 验证方法:用“内存分配与泄漏排查”定位高内存会话;在副本上复现该查询并观察临时文件与内存曲线。

  • 处置指向:限制该类查询的并发与运行窗口、改写为分批/流式处理、按会话级设置更保守的工作内存;必要时对该会话降级或取消([高风险],按“连接数耗尽”场景“解决方案”的前置条件执行)。

3.2.3 连接数过多占用内存

  • 判断依据:内存用量与连接数强相关;每连接固定开销(线程栈、会话缓冲、PostgreSQL 每后端进程基础内存)× 连接数已接近规格;连接池总量超容量。

  • 验证方法:以“连接数 × 每连接内存估算”对照实例内存;观察连接数回落后内存是否同步回落。

  • 处置指向:收敛连接池与副本数,引入连接代理复用;参见“连接数耗尽”场景(连接类故障)的全局连接预算方法。

3.2.4 临时表/排序占用

  • 判断依据:Created_tmp_disk_tables、Sort_merge_passes、temp_files/temp_bytes、temp_blks_written 持续增长;计划中出现 Using temporary、Using filesort、外部排序/哈希溢写。

  • 验证方法:定位产生临时对象的 SQL 指纹(“慢查询”场景的“慢查询日志定位与提取”、“CPU 使用率异常”场景的“高 CPU 消耗 SQL 定位”);确认临时空间落在哪类存储上及其容量。

  • 处置指向:用索引消除排序、降低分组基数、分页与预聚合;控制并发;临时空间容量问题参见“磁盘空间不足”场景(存储与日志故障)。

3.3 排查步骤

3.3.1 内存使用趋势分析

  • 目的:确认内存下降的形态与起点,并核算参数上限在最坏并发下的理论用量是否超过实例规格。

  • 云控制台/OS 方法(厂商中立)[只读]:查看内存使用率、可用内存、swap、OOM/重启事件;有主机权限时用 free -m、vmstat 1、cat /proc/meminfo 观察回收与 swap 活动。

  • MySQL 8.0 [只读]

SHOW VARIABLES WHERE Variable_name IN
('innodb_buffer_pool_size','innodb_buffer_pool_instances','max_connections',
 'sort_buffer_size','join_buffer_size','read_buffer_size','read_rnd_buffer_size',
 'tmp_table_size','max_heap_table_size','table_open_cache','thread_stack');
SHOW GLOBAL STATUS WHERE Variable_name IN
('Threads_connected','Threads_running','Created_tmp_tables','Created_tmp_disk_tables');
  • PostgreSQL 14+ [只读]
SELECT name, setting, unit, source, pending_restart
FROM pg_settings
WHERE name IN ('shared_buffers','work_mem','maintenance_work_mem','hash_mem_multiplier',
               'max_connections','temp_buffers','effective_cache_size','huge_pages',
               'autovacuum_max_workers','max_parallel_workers');
  • 关键判断字段:全局缓存(innodb_buffer_pool_size/shared_buffers)与实例内存的比例;会话级缓冲 × 并发 × 每查询可能的多次分配(PostgreSQL 每个排序/哈希节点可各用一份 work_mem,并行 worker 会翻倍);pending_restart 是否表示参数未生效;内存下降与连接数/临时表增长的时间相关性。

  • 下一步:连接数相关→“连接数耗尽”场景(连接类故障);缓存命中相关→“Buffer Pool / Shared Buffer 命中率检查”;疑似持续泄漏→“内存分配与泄漏排查”;已发生 OOM→“OOM 日志分析”。

3.3.2 Buffer Pool / Shared Buffer 命中率检查

  • 目的:判断缓存是否被挤压或过小,理解内存与 IO 的取舍关系;命中率需与工作集、IO 时延一起解读,不套用固定目标值。

  • MySQL 8.0 [只读]

SHOW GLOBAL STATUS WHERE Variable_name IN
('Innodb_buffer_pool_read_requests','Innodb_buffer_pool_reads',
 'Innodb_buffer_pool_pages_total','Innodb_buffer_pool_pages_free',
 'Innodb_buffer_pool_pages_dirty','Innodb_buffer_pool_wait_free',
 'Innodb_pages_read','Innodb_pages_written');
SELECT POOL_ID, POOL_SIZE, FREE_BUFFERS, DATABASE_PAGES, MODIFIED_DATABASE_PAGES
FROM information_schema.innodb_buffer_pool_stats;
  • PostgreSQL 14+ [只读]
SELECT datname, blks_read, blks_hit,
       blk_read_time, blk_write_time
FROM pg_stat_database WHERE datname='<DB>';
SELECT relname, heap_blks_read, heap_blks_hit, idx_blks_read, idx_blks_hit
FROM pg_statio_user_tables
ORDER BY (heap_blks_read + idx_blks_read) DESC LIMIT 20;
  • 关键判断字段:用两个时间点差值计算物理读增量与命中比(累计值直接相除会被历史稀释);Innodb_buffer_pool_wait_free 非零说明缓冲区吃紧;FREE_BUFFERS、脏页比例;PostgreSQL 中高 blks_read 对象说明工作集超出缓存或被大扫描冲刷。

  • 下一步:命中下降由大扫描冲刷引起→优化 SQL(“慢查询”场景)而非直接扩缓存;工作集确实超出内存→评估扩容或数据分层([变更]);缓存被会话内存挤压→“内存使用趋势分析”、“内存分配与泄漏排查”;伴随 IO 时延升高→“I/O 性能异常”场景。

3.3.3 内存分配与泄漏排查

  • 目的:区分“并发峰值导致的瞬时高用量”与“不随负载回落的持续增长”,后者需要怀疑扩展/引擎缺陷或未释放的会话资源。

  • MySQL 8.0 [只读]

SELECT EVENT_NAME, CURRENT_COUNT_USED,
       CURRENT_NUMBER_OF_BYTES_USED, HIGH_NUMBER_OF_BYTES_USED
FROM performance_schema.memory_summary_global_by_event_name
WHERE CURRENT_NUMBER_OF_BYTES_USED > 0
ORDER BY CURRENT_NUMBER_OF_BYTES_USED DESC LIMIT 20;
SELECT THREAD_ID, EVENT_NAME, CURRENT_NUMBER_OF_BYTES_USED
FROM performance_schema.memory_summary_by_thread_by_event_name
ORDER BY CURRENT_NUMBER_OF_BYTES_USED DESC LIMIT 20;
  • PostgreSQL 14+ [只读]
SELECT datname, temp_files, temp_bytes FROM pg_stat_database WHERE datname='<DB>';
SELECT pid, backend_type, state, now()-backend_start AS conn_age,
       now()-xact_start AS xact_age, wait_event_type, left(query,120) AS q
FROM pg_stat_activity
WHERE state <> 'idle' ORDER BY backend_start LIMIT 30;
SELECT name, setting FROM pg_settings WHERE name IN ('shared_preload_libraries','max_locks_per_transaction');
  • 关键判断字段:内存类别的当前值与高水位差距;单线程占用是否异常集中;长连接的内存是否随连接年龄单调上升(典型泄漏形态);temp_bytes 是否随并发回落;是否加载了第三方扩展/插件。

  • 注意:PostgreSQL 14 没有完整的全局内存归因视图,需结合云指标、进程级 RSS(若有权限)与计划分析;MySQL 部分内存 instrument 默认未启用,开启属 [变更]。

  • 下一步:随并发涨落→限制并发与工作内存;单调持续增长→保留证据并升级云厂商/社区(怀疑缺陷),期间用滚动重启会话或计划内重启缓解([高风险],需评估中断影响);集中于某扩展→评估下线或升级该扩展。

3.3.4 OOM 日志分析

  • 目的:确认是否真的发生 OOM、被终止的是哪个进程、触发时刻的并发与查询特征,防止误把普通重启当 OOM。

  • 云控制台/OS 方法(厂商中立)[只读]:从平台日志服务读取数据库错误日志与实例事件;有主机权限时检查内核日志中的 OOM killer 记录(如 dmesg -T | grep -i -E 'oom|killed process'、journalctl -k);记录 UTC 时间点用于对齐监控曲线。

  • MySQL 8.0 [只读]

SHOW GLOBAL STATUS LIKE 'Uptime';
SELECT VARIABLE_NAME, VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME IN ('Aborted_clients','Connection_errors_internal','Innodb_buffer_pool_resize_status');
  • PostgreSQL 14+ [只读]
SELECT pg_postmaster_start_time() AS last_start,
       now()-pg_postmaster_start_time() AS uptime;
SELECT datname, sessions_fatal, sessions_killed, sessions_abandoned, stats_reset
FROM pg_stat_database WHERE datname='<DB>';
  • 关键判断字段:Uptime/last_start 是否与 OOM 时刻吻合;错误日志中的 Out of memory、server process was terminated by signal 9、terminating connection because of crash of another server process;OOM 时刻的连接数、活跃排序/哈希数量与批任务时间表;被杀进程是数据库主进程还是同主机的其他进程。

  • 下一步:确认 OOM→按本场景“解决方案”做容量与并发建模,收敛工作内存与连接预算([变更]);非 OOM 重启→按平台事件排查(“连接超时/拒绝”场景的“实例运行状态确认”);反复 OOM 且无法归因→保留日志与曲线提工单,避免只调大内存参数掩盖问题。

3.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"内存压力或 OOM"场景(性能类故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
实例规格内存:<VALUE>;是否发生过 OOM 或自动重启:是 / 否
 
【前置检查补充】
0.2 需确认的日志:实例错误日志。
0.7 OOM 与重启是否体现在系统事件中;事件缺失时以错误日志与指标断点为准。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_MemCpuUsage —— CPU 与内存使用率(%;Serverless 用 MySQL_RCU_MemCpuUsage)
  MySQL_InnoDBBufferRatio —— 缓冲池读命中率、使用率、脏块百分比(%)
  MySQL_TempDiskTableCreates —— 磁盘临时表数量(个)
  MySQL_Sessions —— 当前活跃连接数与当前总连接数(个)
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:内存使用率、缓存命中相关指标、连接数、临时空间使用。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):错误日志中的内存分配失败、OOM 记录、重启前后日志。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:实例重启、规格变更、参数组变更。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:规格内存、缓冲池与工作内存类参数当前值。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
降级兜底:OOM 事件不一定进系统事件流;事件缺失时以错误日志记录与指标
  断点推断,并把该判定标注为间接证据。
需 DBA 执行并粘贴:缓存命中情况、内存类参数当前值、临时表与排序落盘计数。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 取内存使用率曲线并与重启或 OOM 事件对齐,确认是缓升、阶跃还是尖刺。
  2) 调 OpenAPI 读规格内存与关键内存参数,做静态超配核算(缓冲池占比 + 每连接开销 ×
     峰值连接)。
  3) 查错误日志中的分配失败与 OOM 记录,取首次出现时间。
  4) 从 ARMS 找大结果集或全量导出类接口的调用时间点。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 缓存效果与内存参数(用于核算,不设固定命中率目标):
   MySQL:SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';
   SHOW VARIABLES WHERE Variable_name IN ('innodb_buffer_pool_size',
   'sort_buffer_size','join_buffer_size','read_buffer_size',
   'tmp_table_size','max_connections');
   PostgreSQL:SELECT blks_hit, blks_read FROM pg_stat_database
   WHERE datname = current_database();
   SHOW shared_buffers; SHOW work_mem; SHOW maintenance_work_mem;
2) 临时空间与排序落盘:
   MySQL:SHOW GLOBAL STATUS LIKE 'Created_tmp%';
   PostgreSQL:SELECT temp_files, temp_bytes FROM pg_stat_database
   WHERE datname = current_database();
 
【候选原因与判定补充】
- 缓存配置不合理(缓冲池占比过高或过低)
- 大查询导致内存溢出(大排序、大哈希、未分页导出)
- 连接数过多,每连接开销累积
- 临时表与排序占用超出预期
结论必须写出算式(参数值 × 并发数),区分静态超配与运行期峰值。
  调整缓冲池、工作内存或连接数均属 [变更],需给出计算依据与回滚值。

3.5 解决方案

先限流、停止可取消的批任务、降低高内存查询并发;明确异常会话后再考虑终止。调整缓冲池、work_mem、连接数或实例规格均为 [变更],需按“每操作 × 并发”建模,在预发压测并确认重启要求;过度缩小内存会转化为 IO 故障。终止会话遵循“连接数耗尽”场景的“解决方案”的 [高风险] 前置条件。

3.6 验证与预防

验证无新增 OOM/重启,内存余量能覆盖峰值与故障切换,临时文件和 IO 未异常放大。建立连接与查询并发预算、内存参数评审、批任务错峰、OOM 事件告警和容量压测。

4. I/O 性能异常

4.1 现象描述

读写延迟上升、IOPS/吞吐达到规格边界、队列加深,查询和提交变慢;CPU 可能下降,因为会话在等待存储。

4.2 常见原因

4.2.1 大量随机写入

  • 判断依据:写 IOPS 高但吞吐不高(小块随机写);脏页与刷盘量大;buffers_checkpoint/Innodb_buffer_pool_pages_flushed 增长快;更新分散在大量页上,或使用随机主键(如无序 UUID)导致页分裂。

  • 验证方法:核对写入 SQL 的主键/索引分布与批量提交方式;观察单次事务修改行数与涉及页数。

  • 处置指向:改为有序主键或批量顺序写、合并小事务、降低索引数量以减少写放大;短期错峰批任务。

4.2.2 日志刷盘瓶颈

  • 判断依据:Innodb_os_log_fsyncs/Innodb_log_waits 高、提交延迟上升;PostgreSQL 中 WALWrite/WALSync 等待明显、buffers_backend_fsync 非零;高持久化设置(每次提交 fsync)叠加高频小事务。

  • 验证方法:对比提交速率与日志写入量、fsync 次数;观察 synchronous_commit/innodb_flush_log_at_trx_commit/sync_binlog 当前取值。

  • 处置指向:合并小事务、批量提交;日志缓冲与检查点参数调整属 [变更];持久化级别的任何放宽都必须由业务与合规明确接受 RPO 变化,禁止为性能私自关闭持久化。

4.2.3 磁盘规格不足

  • 判断依据:IOPS/吞吐长期贴住规格上限并出现限流指标;突发额度耗尽后时延阶跃上升;容量型盘因容量小而性能上限低。

  • 验证方法:对照云盘规格承诺与实测曲线;确认是持续贴顶还是仅峰值触顶;核算业务增长后的需求。

  • 处置指向:提升容量/盘型/额度([变更],评估成本与是否需迁移窗口);同时做 SQL 与写入整形,避免只靠扩容。

4.2.4 数据文件碎片化

  • 判断依据:MySQL information_schema.tables.data_free 较大、表重建后大小明显下降(读取该列前需先 SET SESSION information_schema_stats_expiry = 0 或先 ANALYZE TABLE,否则可能是最长 24 小时前的数据字典缓存值);PostgreSQL n_dead_tup 高、表/索引膨胀、顺序扫描成本高于同规模新表;pg_stats.correlation 接近 0 表示物理无序。

  • 验证方法:在副本或采样上做膨胀评估,避免在生产做高代价全量分析;结合长事务与 autovacuum 情况判断成因。

  • 处置指向:优先普通 VACUUM/在线重整能力与索引重建;VACUUM FULL、OPTIMIZE TABLE、表重建为 [高风险](强锁、额外空间、写放大、复制延迟),须在维护窗口并有完整备份与回滚计划;先解决阻碍清理的长事务(“长事务”场景(锁与并发故障))。

4.3 排查步骤

4.3.1 磁盘 I/O 指标分析(IOPS/延迟/吞吐量)

  • 目的:先用设备层指标确认是否触达规格边界或突发额度耗尽,再看数据库内部视角。

  • 云控制台/OS 方法(厂商中立)[只读]:查看云盘读写 IOPS、吞吐、平均/尾时延、队列深度、突发额度余量与限流指标;有主机权限时用 iostat -xz 1、vmstat 1 观察 await、%util、aqu-sz。

  • MySQL 8.0 [只读]

SHOW GLOBAL STATUS WHERE Variable_name IN
('Innodb_data_reads','Innodb_data_writes','Innodb_data_read','Innodb_data_written',
 'Innodb_os_log_written','Innodb_log_waits','Innodb_buffer_pool_wait_free');
SELECT EVENT_NAME, COUNT_STAR,
       ROUND(SUM_TIMER_WAIT/1e12,3) AS total_s
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE EVENT_NAME LIKE 'wait/io/%' AND COUNT_STAR > 0
ORDER BY SUM_TIMER_WAIT DESC LIMIT 20;
  • PostgreSQL 14+ [只读]
SHOW track_io_timing;
SELECT datname, blks_read, blks_hit, blk_read_time, blk_write_time,
       temp_files, temp_bytes
FROM pg_stat_database WHERE datname='<DB>';
SELECT wait_event_type, wait_event, count(*) AS sessions
FROM pg_stat_activity WHERE wait_event_type = 'IO'
GROUP BY wait_event_type, wait_event ORDER BY sessions DESC;
  • 关键判断字段:设备 await/云盘时延与规格承诺的关系;IOPS/吞吐是否贴住上限(贴住即限流);Innodb_log_waits 非零说明日志缓冲不足;blk_read_time/blk_write_time 需 track_io_timing=on(开启属 [变更],有一定开销);读、写、临时、日志 IO 各自占比。

  • 下一步:设备贴限→提升规格/额度或整形写入([变更]);读放大为主→“慢 I/O 相关 SQL 定位”;日志写为主→“日志写入与数据写入分离分析”;临时 IO 为主→“内存压力或 OOM”场景;空间紧张→“磁盘空间不足”场景(存储与日志故障)。

4.3.2 慢 I/O 相关 SQL 定位

  • 目的:把 IO 压力归因到具体 SQL 指纹与对象,避免只做规格扩容。

  • MySQL 8.0 [只读]

SELECT FILE_NAME, EVENT_NAME, COUNT_READ, SUM_NUMBER_OF_BYTES_READ,
       COUNT_WRITE, SUM_NUMBER_OF_BYTES_WRITE,
       ROUND(SUM_TIMER_READ/1e12,3) AS read_s, ROUND(SUM_TIMER_WRITE/1e12,3) AS write_s
FROM performance_schema.file_summary_by_instance
ORDER BY (SUM_TIMER_READ + SUM_TIMER_WRITE) DESC LIMIT 20;
SELECT OBJECT_SCHEMA, OBJECT_NAME, COUNT_READ, COUNT_WRITE, SUM_TIMER_WAIT
FROM performance_schema.table_io_waits_summary_by_table
ORDER BY SUM_TIMER_WAIT DESC LIMIT 20;
  • PostgreSQL 14+ [只读]
-- PG14~16 列名如下;PG17 起 blk_read_time/blk_write_time 改名为 shared_blk_read_time/shared_blk_write_time
SELECT queryid, calls, shared_blks_read, shared_blks_written,
       temp_blks_read, temp_blks_written, blk_read_time, blk_write_time, query
FROM pg_stat_statements
ORDER BY (shared_blks_read + temp_blks_written) DESC LIMIT 20;
SELECT relname, heap_blks_read, idx_blks_read, toast_blks_read
FROM pg_statio_user_tables
ORDER BY (heap_blks_read + idx_blks_read) DESC LIMIT 20;
  • 关键判断字段:按对象/文件排序的读写字节与耗时;temp_blks_written 指向排序/哈希落盘;TOAST 读高说明大字段访问;同一对象在故障窗口与基线窗口的差值(用两点差值,不比较不同运行时长的实例)。

  • 下一步:少数对象/指纹贡献主要 IO→“慢查询”场景优化计划与索引;临时 IO 为主→“内存压力或 OOM”场景的“临时表/排序占用”;写入集中在日志文件→“日志写入与数据写入分离分析”;分布均匀且贴设备上限→“磁盘类型与规格确认”。

4.3.3 日志写入与数据写入分离分析

  • 目的:区分“数据页刷盘/检查点”与“事务日志顺序写”的压力来源,二者的优化手段完全不同。

  • MySQL 8.0 [只读]

SHOW GLOBAL STATUS WHERE Variable_name IN
('Innodb_os_log_written','Innodb_os_log_fsyncs','Innodb_log_writes','Innodb_log_waits',
 'Innodb_buffer_pool_pages_dirty','Innodb_buffer_pool_pages_flushed','Innodb_data_fsyncs');
SHOW VARIABLES WHERE Variable_name IN
('innodb_flush_log_at_trx_commit','sync_binlog','innodb_io_capacity','innodb_io_capacity_max',
 'innodb_flush_method','innodb_redo_log_capacity');
-- innodb_redo_log_capacity 为 8.0.30+;更早版本 redo 容量由
-- innodb_log_file_size × innodb_log_files_in_group 决定:
--   SHOW VARIABLES WHERE Variable_name IN ('innodb_log_file_size','innodb_log_files_in_group');
  • PostgreSQL 14+ [只读]
-- PostgreSQL 14~16:checkpoint 相关列与 buffers_backend* 都在 pg_stat_bgwriter
SELECT checkpoints_timed, checkpoints_req, checkpoint_write_time, checkpoint_sync_time,
       buffers_checkpoint, buffers_clean, maxwritten_clean, buffers_backend, buffers_backend_fsync
FROM pg_stat_bgwriter;
-- PostgreSQL 17 起:checkpoint 相关列迁到 pg_stat_checkpointer 并改名,
-- buffers_backend/buffers_backend_fsync 已移除(后端刷写量改由 pg_stat_io 观察),17/18 上请改用:
--   SELECT num_timed, num_requested, write_time, sync_time, buffers_written FROM pg_stat_checkpointer;
--   SELECT buffers_clean, maxwritten_clean FROM pg_stat_bgwriter;
SELECT name, setting, unit FROM pg_settings
WHERE name IN ('wal_buffers','wal_writer_delay','synchronous_commit','max_wal_size',
               'checkpoint_timeout','checkpoint_completion_target','wal_compression');
  • 关键判断字段:Innodb_os_log_written 增速代表 redo 顺序写量;Innodb_log_waits 非零代表日志缓冲不足;checkpoints_req 明显多于 checkpoints_timed 说明写入量迫使请求型检查点(IO 尖峰来源);buffers_backend/buffers_backend_fsync 高说明后端进程被迫自己刷盘;持久化参数(innodb_flush_log_at_trx_commit、sync_binlog、synchronous_commit)决定 fsync 频率。

  • 版本分支:本文的“PostgreSQL 14+”不代表列名在各大版本不变。PG17 起 pg_stat_bgwriter 的 checkpoint 相关列迁移到 pg_stat_checkpointer 并改名(checkpoints_timed→num_timed、checkpoints_req→num_requested、checkpoint_write_time→write_time、checkpoint_sync_time→sync_time、buffers_checkpoint→buffers_written),buffers_backend/buffers_backend_fsync 被移除(后端刷写与 fsync 改由 pg_stat_io 观察);pg_stat_statements 的 blk_read_time/blk_write_time 在 PG17 改名为 shared_blk_read_time/shared_blk_write_time。在 17/18 上执行本文 SQL 前请按上述映射替换列名。

  • 下一步:检查点密集→评估 max_wal_size/checkpoint_timeout/innodb_io_capacity([变更],须权衡恢复时间目标);日志顺序写贴设备上限→拆分批量写入或提升规格;日志量本身异常→“binlog/WAL 异常增长或保留”场景(存储与日志故障);不得以关闭持久化换性能。

4.3.4 磁盘类型与规格确认

  • 目的:确认当前云盘类型、容量与 IOPS/吞吐上限是否匹配工作负载,并确认数据库侧的存储假设参数与实际硬件一致。

  • 云控制台方法(厂商中立)[只读]:核对云盘类型(SSD/ESSD/高效云盘等)、容量、基准与突发 IOPS/吞吐上限、是否按容量线性给性能、突发额度模型、是否与其他实例共享;核对数据、日志、临时空间是否同盘。

  • MySQL 8.0 [只读]

SHOW VARIABLES WHERE Variable_name IN
('innodb_io_capacity','innodb_io_capacity_max','innodb_flush_neighbors',
 'innodb_page_size','innodb_read_io_threads','innodb_write_io_threads','innodb_use_native_aio');
  • PostgreSQL 14+ [只读]
SELECT name, setting, unit FROM pg_settings
WHERE name IN ('random_page_cost','seq_page_cost','effective_io_concurrency',
               'maintenance_io_concurrency','effective_cache_size','block_size');
SELECT pg_size_pretty(sum(pg_database_size(datname))) AS all_db_size FROM pg_database;
  • 关键判断字段:innodb_io_capacity/effective_io_concurrency/random_page_cost 是否与实际盘型相符(SSD 上沿用机械盘假设会让优化器与刷盘节奏失真);innodb_flush_neighbors 在 SSD 上通常不必要;数据库总大小与云盘容量、性能上限的关系。

  • 下一步:参数与盘型不符→按 [变更] 流程校准并观察计划变化;容量决定性能且已贴顶→扩容或更换盘型([变更],评估成本与迁移窗口);数据/日志/临时同盘互相干扰→评估分盘或分层布局;规格合理仍瓶颈→回到“慢 I/O 相关 SQL 定位”做 SQL 侧治理。

4.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"I/O 性能异常"场景(性能类故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
磁盘类型与规格:<VALUE>(含标称 IOPS 与吞吐上限)
 
【前置检查补充】
0.2 需确认的日志:慢日志与实例错误日志。
0.7 磁盘标称 IOPS 与吞吐上限是否已确认;未确认则无法判断是否触顶。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_IOPS —— 实例每秒 IO 请求数(个/秒)
  MySQL_MBPS —— 实例读写吞吐(Byte/秒)
  MySQL_InnoDBDataReadWriten —— 每秒数据读写量(KB)
  MySQL_InnoDBLogWrites —— 每秒日志写请求、物理写与 fsync 完成次数(个/秒)
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:IOPS、吞吐、磁盘延迟、队列深度、磁盘使用率、(若暴露)突发额度。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):错误日志中的 checkpoint 警告与 IO 等待相关记录;慢
  日志中的高扫描量指纹。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:备份、批量导入、扩容、索引重建、规格变更。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:磁盘类型规格与标称上限、参数组当前值。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
降级兜底:磁盘标称上限与突发额度需从实例规格获取;拿不到上限时只能
  给出相对基线的偏离,不得断言触顶。
需 DBA 执行并粘贴:IO 计数与等待、redo 或 WAL 写入量、checkpoint 频率。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 取 IOPS、吞吐、延迟、队列深度曲线,与标称上限和基线比对,确认是否触顶或额度耗尽。
  2) 查事件:备份、批量导入、扩容、索引重建的执行窗口。
  3) 查错误日志中的 checkpoint 警告与 IO 相关记录。
  4) 从 ARMS 确认应用侧耗时抬升与 IO 抬升是否同步发生。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 引擎侧 IO 计数:
   MySQL:SHOW GLOBAL STATUS WHERE Variable_name IN ('Innodb_data_reads',
   'Innodb_data_writes','Innodb_os_log_written',
   'Innodb_buffer_pool_wait_free','Innodb_row_lock_waits');
   PostgreSQL:SHOW track_io_timing;   -- 未开启则无 blk_read_time(开启属 [变更])
   SELECT blks_read, blks_hit, temp_bytes FROM pg_stat_database
   WHERE datname = current_database();
2) 日志写与检查点:
   MySQL:SHOW VARIABLES LIKE 'innodb_redo_log_capacity';  -- 8.0.30+
   SHOW ENGINE INNODB STATUS;   -- 取 LOG 段的写入与 checkpoint 位点
   PostgreSQL:SELECT * FROM pg_stat_bgwriter;
   -- PostgreSQL 17 起 checkpoint 相关列移至 pg_stat_checkpointer
   SELECT pg_size_pretty(sum(size)) FROM pg_ls_waldir();  -- 需相应权限
 
【候选原因与判定补充】
- 大量随机写入
- 日志刷盘成为瓶颈
- 磁盘规格不足或突发额度耗尽
- 数据文件碎片化
触顶判定必须引用规格标称上限,不得用经验阈值。
  区分数据库放大 IO(可优化)与底层存储受限(需扩容或转托管侧);
  若同时磁盘将满,先按磁盘空间方向处置。

4.5 解决方案

错峰或暂停可取消的批任务,优化高 IO SQL,平滑写入,必要时提升存储规格/容量。检查点、刷脏、日志和存储规格调整是 [变更],需确认恢复时间目标、持久性、成本和回滚;不得以关闭持久化换性能。

4.6 验证与预防

验证 SLO、设备时延/队列、查询物理读、临时 IO 和日志写回归基线,并覆盖一次检查点周期。建立存储性能预算、突发额度告警、批任务编排和变更前后 IO 对比。

三、锁与并发故障

1. 锁等待

1.1 现象描述

SQL 长时间等待、请求超时、吞吐下降;等待会话增加,但 CPU 可能不高。应区分行锁、表锁、元数据锁、关系锁和轻量级内部等待。

1.2 常见原因

1.2.1 事务未及时提交

  • 判断依据:会话处于 idle in transaction(PostgreSQL)或 MySQL 中存在 trx_state='RUNNING' 但无当前 SQL 的事务;事务年龄远大于业务预期;连接池归还了带未结束事务的连接。

  • 验证方法:按“阻塞源(持锁会话)定位”找到根阻塞者并检查其状态与年龄;核对应用是否在异常路径漏掉提交/回滚。

  • 处置指向:应用层用 try/finally 保证提交或回滚;设置与业务匹配的空闲事务超时([变更],需灰度);详见“长事务”场景。

1.2.2 长事务持有锁

  • 判断依据:单个事务长时间持有排他锁并阻塞大量会话;伴随 undo 历史增长、死元组堆积或复制延迟。

  • 验证方法:结合“锁类型与持有时间分析”的锁模式与“长事务”场景的“事务对系统的影响评估(undo 膨胀、主从延迟等)”的系统影响评估;确认事务是否仍在正常推进。

  • 处置指向:优先等待或让应用主动结束事务;拆分长批处理为小事务;强制终止属 [高风险],需评估回滚时间与 IO 峰值。

1.2.3 批量更新范围过大

  • 判断依据:单条 DML 的 trx_rows_locked/rows 极大;锁定范围覆盖大量记录或整段索引区间;批任务与在线业务时间重叠。

  • 验证方法:查看阻塞事务的语句与影响行数;用 EXPLAIN 确认谓词扫描范围。

  • 处置指向:按主键有序分批、每批提交、控制批大小与速率;为批任务安排低峰窗口并支持断点续跑。

1.2.4 缺少索引导致锁范围扩大

  • 判断依据:UPDATE/DELETE 的谓词无可用索引,导致扫描并锁定远超目标的记录(MySQL 中还可能产生额外间隙锁);LOCK_DATA 显示的键范围远大于业务意图。

  • 验证方法:对 DML 谓词做 EXPLAIN,核对访问路径与预计扫描行数;检查索引是否因类型转换而失效(“慢查询”场景的“索引失效”)。

  • 处置指向:为 DML 谓词补充合适索引([变更],评估锁、空间、写放大与复制延迟);同时改写谓词以缩小锁范围。

1.3 排查步骤

1.3.1 锁等待队列查看

  • 目的:先取得“谁在等、等多久、等什么对象”的全局快照,确定影响面与紧急度。

  • MySQL 8.0 [只读]

SELECT * FROM sys.innodb_lock_waits ORDER BY wait_age_secs DESC LIMIT 50;
SELECT REQUESTING_ENGINE_TRANSACTION_ID, BLOCKING_ENGINE_TRANSACTION_ID,
       REQUESTING_THREAD_ID, BLOCKING_THREAD_ID
FROM performance_schema.data_lock_waits;
SHOW GLOBAL STATUS WHERE Variable_name IN
('Innodb_row_lock_current_waits','Innodb_row_lock_time_avg','Innodb_row_lock_time_max','Table_locks_waited');
  • PostgreSQL 14+ [只读]
SELECT a.pid, a.usename, a.application_name, a.state,
       a.wait_event_type, a.wait_event,
       now()-a.query_start AS wait_age, now()-a.xact_start AS xact_age,
       pg_blocking_pids(a.pid) AS blockers, left(a.query,120) AS q
FROM pg_stat_activity a
WHERE cardinality(pg_blocking_pids(a.pid)) > 0
ORDER BY a.xact_start LIMIT 50;
  • 关键判断字段:等待会话数量与 wait_age_secs/wait_age 分布;Innodb_row_lock_current_waits 与 Innodb_row_lock_time_max;等待是否集中在单一对象/单一阻塞者(说明有明确根因)还是弥散(说明整体过载)。锁数据瞬时变化,应记录采集时间并做多次短采样。

  • 下一步:存在明确阻塞者→“阻塞源(持锁会话)定位”;等待弥散且无长阻塞者→检查整体负载(性能类故障一章);出现循环依赖或数据库已自动回滚→“死锁”场景;等待对象为元数据/DDL→“锁类型与持有时间分析”。

1.3.2 阻塞源(持锁会话)定位

  • 目的:沿等待链找到根阻塞者(自身不等待任何人),避免误杀中间的受害会话。

  • MySQL 8.0 [只读]

SELECT waiting_pid, waiting_query, blocking_pid, blocking_query,
       wait_age_secs, locked_table, locked_index, locked_type
FROM sys.innodb_lock_waits ORDER BY wait_age_secs DESC;
SELECT trx_id, trx_mysql_thread_id, trx_state, trx_started,
       trx_rows_locked, trx_rows_modified, trx_isolation_level, trx_query
FROM information_schema.innodb_trx ORDER BY trx_started LIMIT 20;
  • PostgreSQL 14+ [只读]
WITH RECURSIVE chain AS (
  SELECT pid, unnest(pg_blocking_pids(pid)) AS blocker, 1 AS depth
  FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid)) > 0
  UNION ALL
  SELECT c.blocker, unnest(pg_blocking_pids(c.blocker)), c.depth+1
  FROM chain c
  -- depth 必须用于收敛:等待成环(死锁前兆)时否则会无界递归
  WHERE cardinality(pg_blocking_pids(c.blocker)) > 0 AND c.depth < 10
)
SELECT DISTINCT c.blocker AS root_candidate, a.usename, a.state,
       now()-a.xact_start AS xact_age, left(a.query,120) AS q
FROM chain c JOIN pg_stat_activity a ON a.pid = c.blocker
WHERE cardinality(pg_blocking_pids(c.blocker)) = 0;
  • 关键判断字段:根阻塞者的 trx_state/state(RUNNING 且有 SQL、还是空闲事务)、事务年龄、已修改行数、阻塞链扇出(被它挡住多少会话)、所属应用与客户端来源。

  • 下一步:根阻塞者仍在正常推进→等待或限流新流量;根阻塞者为空闲事务→“长事务”场景并联系应用所有者;根阻塞者为 DDL→“锁类型与持有时间分析”;需要强制解除→按本场景“解决方案”的 [高风险] 前置条件评估。

1.3.3 锁类型与持有时间分析

  • 目的:判断锁的粒度与模式(行锁/间隙锁/表锁/元数据锁/关系锁),因为不同锁类型的解法与影响面完全不同。

  • MySQL 8.0 [只读]

SELECT ENGINE_TRANSACTION_ID, THREAD_ID, OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME,
       LOCK_TYPE, LOCK_MODE, LOCK_STATUS, LOCK_DATA
FROM performance_schema.data_locks
ORDER BY OBJECT_SCHEMA, OBJECT_NAME LIMIT 100;
SELECT OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, OWNER_THREAD_ID
FROM performance_schema.metadata_locks
WHERE LOCK_STATUS IN ('PENDING','GRANTED') ORDER BY LOCK_STATUS;
  • PostgreSQL 14+ [只读]
SELECT l.pid, l.locktype, l.mode, l.granted,
       COALESCE(l.relation::regclass::text, l.locktype) AS object,
       now()-a.xact_start AS xact_age, a.state, left(a.query,100) AS q
FROM pg_locks l JOIN pg_stat_activity a ON a.pid = l.pid
WHERE NOT l.granted OR l.mode LIKE 'Access%Exclusive%'
ORDER BY l.granted, xact_age DESC NULLS LAST LIMIT 100;
  • 关键判断字段:MySQL LOCK_TYPE(RECORD/TABLE)、LOCK_MODE(X/S/X,GAP/IX)、LOCK_STATUS(WAITING/GRANTED)与 LOCK_DATA 指向的键范围;metadata_locks 中 PENDING 的 DDL;PostgreSQL locktype(relation/transactionid/tuple/advisory)、mode(AccessExclusiveLock 会阻塞读)与 granted=false 的等待项。

  • 下一步:间隙锁/范围锁过大→检查索引与谓词选择性(“慢查询”场景(性能类故障));元数据锁/AccessExclusiveLock 挂起→取消 DDL 或安排窗口;锁持有时间长→“长事务”场景;确认锁语义正常但并发高→本场景“解决方案”的批次与顺序治理。

1.3.4 相关 SQL 与事务追溯

  • 目的:把锁现场还原为“哪段业务代码、哪个事务、哪些语句”,形成可修复的结论而不仅是止损。

  • MySQL 8.0 [只读]

SELECT t.THREAD_ID, t.PROCESSLIST_ID, t.PROCESSLIST_USER, t.PROCESSLIST_HOST,
       t.PROCESSLIST_DB, t.PROCESSLIST_TIME, t.PROCESSLIST_STATE
FROM performance_schema.threads t WHERE t.PROCESSLIST_ID = <PID>;
SELECT EVENT_ID, SQL_TEXT, TIMER_WAIT, ROWS_AFFECTED, ROWS_EXAMINED, ERRORS
FROM performance_schema.events_statements_history
WHERE THREAD_ID = (SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_ID = <PID>)
ORDER BY EVENT_ID DESC LIMIT 20;
  • PostgreSQL 14+ [只读]
SELECT pid, usename, application_name, client_addr, backend_start,
       xact_start, query_start, state_change, state,
       wait_event_type, wait_event, backend_xid, backend_xmin, query
FROM pg_stat_activity WHERE pid = <PID>;
SELECT queryid, calls, mean_exec_time, rows, query
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 20;
  • 关键判断字段:events_statements_history 展示的事务内语句序列(需相应 consumer 已启用);application_name、client_addr 指向的服务与代码路径;backend_xid/backend_xmin 反映事务可见性影响;语句序列中对象的访问顺序是死锁与热点分析的关键证据。

  • 下一步:定位到代码路径→按本场景“解决方案”缩短事务、统一访问顺序、拆分批次;事务包含外部调用/人工等待→“长事务”场景;同一批对象被多路径反序访问→“死锁”场景;证据不足→在审批下短时增强日志采样并复现。

1.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"锁等待"场景(锁与并发故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
受影响对象:<TABLE>;是否伴随应用超时重试:是 / 否
 
【前置检查补充】
0.2 需确认的日志:实例错误日志(含锁等待超时记录)。
0.7 锁链路证据只能由 DBA 在阻塞持续期间采集;先确认有人可随时执行。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_ROW_LOCK —— 行锁最大等待时间、平均等待时间(毫秒)与等待次数
  MySQL_ThreadStatus —— 活跃线程数与线程连接数(个)
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:活跃会话数、QPS 下跌幅度、(若暴露)等待类会话数。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):错误日志中的锁等待超时与语句超时记录,按指纹与时间聚合。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:DDL 发布、批量任务、发版。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:锁与超时相关参数当前值。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
降级兜底:锁等待的对象与阻塞链只能在阻塞持续期间由 DBA 采集:
  错过窗口就标注为不可得,并给出下次复现时的采集清单,不要事后用指标猜测。
需 DBA 执行并粘贴:等待队列与阻塞源、锁类型与持有时长、双方事务的 SQL 与开始时间。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 取活跃会话与 QPS 曲线,定位阻塞开始与恢复的时间点。
  2) 查错误日志的锁等待超时与语句超时计数,按语句指纹与时间分布聚合。
  3) 从 ARMS 找被阻塞的接口与 SQL 指纹,估算影响面。
  4) 查事件:窗口内是否有 DDL、批量任务或发版。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 等待队列与阻塞源(必须在阻塞持续期间执行):
   MySQL:SELECT * FROM sys.innodb_lock_waits;
   SELECT * FROM performance_schema.data_lock_waits;
   PostgreSQL:SELECT pid, pg_blocking_pids(pid) AS blocked_by, wait_event_type,
   wait_event, xact_start, left(query,120) AS q
   FROM pg_stat_activity
   WHERE cardinality(pg_blocking_pids(pid)) > 0;
2) 锁类型、对象与持有时长:
   MySQL:SELECT trx_id, trx_started, trx_state, trx_rows_locked, trx_query
   FROM information_schema.innodb_trx ORDER BY trx_started;
   SELECT * FROM performance_schema.metadata_locks;   -- 元数据锁 / DDL 排队
   PostgreSQL:SELECT locktype, relation::regclass, mode, granted, pid
   FROM pg_locks WHERE NOT granted;
   -- log_lock_waits 只记录超过 deadlock_timeout 的等待,日志空不等于无等待
 
【候选原因与判定补充】
- 事务未及时提交
- 长事务持有锁
- 批量更新范围过大
- 缺少索引导致锁范围扩大
阻塞源与等待者必须由 DBA 输出区分,Agent 不得按指标猜测谁在持锁。
  阻塞源为长事务时改按长事务方向处置;终止会话属 [变更] 或 [高风险],只输出建议。

1.5 解决方案

优先让根事务正常结束、暂停新流量、拆小批次并统一对象访问顺序。KILL <PID>、pg_cancel_backend(<PID>)、pg_terminate_backend(<PID>) 是 [高风险]:先确认根阻塞者而非误杀等待者,评估回滚时间、数据一致性和应用重试;先取消语句,再在必要时终止会话。建索引须遵守“慢查询”场景的“解决方案”的风险要求。

1.6 验证与预防

确认阻塞链消失、等待时长和业务延迟恢复,且回滚完成、无复制积压。配置与 SLO 匹配的锁/语句/空闲事务超时;应用事务短小、固定访问顺序;对 DDL 使用变更平台和超时保护。

2. 死锁

2.1 现象描述

数据库检测到循环等待并主动回滚一个事务;MySQL 常报 Deadlock found when trying to get lock,PostgreSQL 报 deadlock detected。死锁是并发设计问题,不是简单“把超时调大”可解决。

2.2 常见原因

2.2.1 事务加锁顺序不一致

  • 判断依据:死锁图显示两个事务以相反顺序请求同一组对象/记录;不同代码路径(如“先更新订单再更新库存”与“先更新库存再更新订单”)并发执行;批量更新未按稳定键排序。

  • 验证方法:按“死锁涉及的事务与 SQL 还原”还原两侧事务的加锁序列;检查批量语句是否有确定性的 ORDER BY 或按主键分批。

  • 处置指向:在设计与代码评审层面统一全局对象访问顺序;批量操作按主键有序分批;对可安全重试的事务实施有界指数退避。

2.2.2 并发更新同一批数据

  • 判断依据:热点行/热点区间被高并发更新(计数器、库存、状态机、队列表);死锁与锁等待同时增长;同一指纹的调用量在故障窗口显著上升。

  • 验证方法:统计热点对象的更新频率与冲突次数;观察并发度与死锁计数的相关性。

  • 处置指向:降低热点冲突(分片计数、队列化串行处理、乐观并发控制加重试);缩短事务持有时间;必要时在应用层排队与限流。

2.2.3 索引缺失导致锁范围扩大

  • 判断依据:DML 谓词无可用索引,扫描并锁定大量记录(MySQL 中还可能产生间隙锁),使原本不相交的事务产生交集;LOCK_DATA 显示锁定范围远超目标行。

  • 验证方法:对涉及的 DML 做 EXPLAIN,核对访问路径与锁定范围;检查是否存在隐式类型转换导致索引失效。

  • 处置指向:补充或修正索引使锁精确到目标行([变更],评估锁、空间、写放大与复制延迟);同时缩小谓词范围与批次大小。

2.3 排查步骤

2.3.1 死锁日志/错误信息提取

  • 目的:取得完整死锁图原文(参与事务、锁、对象、受害者),这是唯一可靠的还原依据。

  • 云控制台/日志方法(厂商中立)[只读]:从平台日志服务导出数据库错误日志中的死锁段落,记录 UTC 时间;MySQL 的 SHOW ENGINE INNODB STATUS 仅保留最近一次死锁,需及时保存。

  • MySQL 8.0 [只读]

SHOW ENGINE INNODB STATUS;
-- Innodb_deadlocks 不是上游 Oracle MySQL 的状态变量(属 MariaDB/Percona),在 MySQL 8.0 上查询返回空集;
-- 死锁计数改用 innodb_metrics 的 lock_deadlocks
SELECT NAME, COUNT, STATUS FROM information_schema.innodb_metrics WHERE NAME='lock_deadlocks';
SHOW VARIABLES WHERE Variable_name IN ('innodb_print_all_deadlocks','innodb_lock_wait_timeout','innodb_deadlock_detect');
  • PostgreSQL 14+ [只读]
SELECT datname, deadlocks, stats_reset FROM pg_stat_database WHERE datname='<DB>';
SELECT name, setting FROM pg_settings
WHERE name IN ('deadlock_timeout','log_lock_waits','log_min_error_statement','lock_timeout');
  • 关键判断字段:information_schema.innodb_metrics 中 lock_deadlocks 的 COUNT 增量与 pg_stat_database.deadlocks 的增量(偶发一次还是持续增长);死锁图中的事务编号、等待与持有的锁模式、涉及对象与索引、被回滚的受害事务;innodb_deadlock_detect 是否开启(关闭后表现为锁等待超时而非死锁)。

  • 注意:lock_deadlocks 的 STATUS 若为 disabled,COUNT 不会累加,需先 SET GLOBAL innodb_monitor_enable='lock_deadlocks'(属 [变更]);启用后计数自启用或上次重置起算,因此不能与历史绝对值直接比较,只能看启用后的增量。作为替代或补充,可开启 innodb_print_all_deadlocks 把每次死锁完整写入错误日志(属 [变更],会增加日志量,应限时并设自动到期)。

  • 注意:不要高频执行 SHOW ENGINE INNODB STATUS,其输出很大且在高负载实例上会加重压力;innodb_print_all_deadlocks、log_lock_waits 的开启属 [变更],应限时并设自动到期。log_lock_waits 只记录等待时间超过 deadlock_timeout 的锁等待(默认 1s),短于该值的等待不会留下日志,因此“日志里没有锁等待”不等于没有发生锁等待;需要更细粒度时应配合 pg_stat_activity/pg_locks 采样,而不是把 deadlock_timeout 无依据调小(调小会增加死锁检测开销)。

  • 下一步:拿到死锁图→“死锁涉及的事务与 SQL 还原”;日志被截断或平台不暴露→按审批短时增强日志并在预发复现;死锁计数持续增长→按本场景“解决方案”优先做并发设计修复。

2.3.2 死锁涉及的事务与 SQL 还原

  • 目的:把死锁图中的事务映射回具体 SQL 指纹、表/索引与业务代码路径,确认每个事务的加锁序列。

  • MySQL 8.0 [只读]

SELECT DIGEST_TEXT, COUNT_STAR, SUM_ROWS_AFFECTED, SUM_ROWS_EXAMINED,
       SUM_ERRORS, SUM_LOCK_TIME, FIRST_SEEN, LAST_SEEN
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST_TEXT LIKE '%<TABLE>%'
ORDER BY SUM_LOCK_TIME DESC LIMIT 20;
EXPLAIN FORMAT=JSON UPDATE <SCHEMA>.<TABLE> SET <COLUMN>=<VALUE> WHERE <COLUMN>=<VALUE>;
-- 亦可用传统 EXPLAIN,或分析等价 SELECT;FORMAT=TREE 对单表 UPDATE/DELETE 在 8.0 不可靠
EXPLAIN UPDATE <SCHEMA>.<TABLE> SET <COLUMN>=<VALUE> WHERE <COLUMN>=<VALUE>;
  • PostgreSQL 14+ [只读]
SELECT queryid, calls, rows, mean_exec_time, query
FROM pg_stat_statements
WHERE query ILIKE '%<TABLE>%'
ORDER BY calls DESC LIMIT 20;
EXPLAIN (COSTS, VERBOSE)
UPDATE <SCHEMA>.<TABLE> SET <COLUMN>=<VALUE> WHERE <COLUMN>=<VALUE>;
  • 关键判断字段:两个事务各自锁定的对象与索引、加锁先后顺序、谓词实际扫描/锁定范围、是否存在外键检查、触发器或唯一约束引入的隐式访问;SUM_LOCK_TIME 指向锁成本高的指纹。

  • 注意:普通 EXPLAIN(含 FORMAT=JSON)不执行 DML;禁止在生产对 DML 直接加 ANALYZE(风险见“慢查询”场景的“执行计划分析(EXPLAIN)”的高风险提示),且 MySQL 8.0 的 EXPLAIN ANALYZE 本身只支持 SELECT、TABLE 与多表 UPDATE/DELETE。分析单表 UPDATE/DELETE 时不要用 FORMAT=TREE(8.0 下不可靠),改用 FORMAT=JSON、传统 EXPLAIN 或等价 SELECT。

  • 下一步:两事务加锁顺序相反→“事务加锁顺序不一致”;锁定范围明显超出业务意图→“索引缺失导致锁范围扩大”;隐式锁来自约束/触发器→评估约束与触发器设计;顺序一致但仍死锁→“锁等待链路分析”。

2.3.3 锁等待链路分析

  • 目的:还原循环等待的完整链路,并判断当前是否仍有同类等待环在形成(死锁往往是锁等待问题的极端表现)。

  • MySQL 8.0 [只读]

SELECT w.REQUESTING_ENGINE_TRANSACTION_ID AS req_trx,
       w.BLOCKING_ENGINE_TRANSACTION_ID  AS blk_trx,
       lr.OBJECT_SCHEMA, lr.OBJECT_NAME, lr.INDEX_NAME,
       lr.LOCK_TYPE, lr.LOCK_MODE, lr.LOCK_STATUS, lr.LOCK_DATA
FROM performance_schema.data_lock_waits w
JOIN performance_schema.data_locks lr
  ON lr.ENGINE_LOCK_ID = w.REQUESTING_ENGINE_LOCK_ID;
SELECT * FROM sys.innodb_lock_waits ORDER BY wait_age_secs DESC LIMIT 50;
  • PostgreSQL 14+ [只读]
SELECT a.pid, pg_blocking_pids(a.pid) AS blockers,
       a.wait_event_type, a.wait_event, now()-a.xact_start AS xact_age,
       l.locktype, l.mode, l.granted, l.relation::regclass AS object
FROM pg_stat_activity a
LEFT JOIN pg_locks l ON l.pid = a.pid AND NOT l.granted
WHERE cardinality(pg_blocking_pids(a.pid)) > 0
ORDER BY xact_age DESC LIMIT 50;
  • 关键判断字段:等待边的方向(A 等 B 的哪个对象、B 等 A 的哪个对象)是否构成环;同一对象上并发排他请求的数量;LOCK_DATA 与 locktype/mode 指示的粒度是行、间隙还是关系级;同类等待是否仍在持续出现。

  • 下一步:仍在形成等待环→先按“锁等待”场景止损限流;环涉及两张表的反序访问→统一访问顺序;环由范围锁引起→优化索引与批次;已无等待环但需修复历史死锁→“应用逻辑回溯”。

2.3.4 应用逻辑回溯

  • 目的:确认业务代码中形成反序加锁的路径、事务边界与重试逻辑,给出代码级修复与安全重试策略。

  • 应用侧方法(厂商中立)[只读]:按 application_name、客户端来源、SQL 指纹反查代码路径与发布版本;核对 ORM 的隐式事务边界、批量保存顺序、缓存回写与消息消费并发度;确认重试是否幂等且有界。

  • MySQL 8.0 [只读]

SELECT PROCESSLIST_ID, PROCESSLIST_USER, PROCESSLIST_HOST, PROCESSLIST_DB,
       PROCESSLIST_STATE, PROCESSLIST_TIME
FROM performance_schema.threads WHERE TYPE='FOREGROUND'
ORDER BY PROCESSLIST_TIME DESC LIMIT 30;
-- events_statements_history_long 的 consumer 默认关闭(events_statements_history 默认开启),
-- 未启用时下面这条查询返回空集。先确认状态:
--   SELECT NAME, ENABLED FROM performance_schema.setup_consumers WHERE NAME='events_statements_history_long';
-- 需要时启用(属 [变更],占用额外内存、应限时并设自动到期):
--   UPDATE performance_schema.setup_consumers SET ENABLED='YES' WHERE NAME='events_statements_history_long';
SELECT EVENT_ID, SQL_TEXT, ERRORS, ROWS_AFFECTED
FROM performance_schema.events_statements_history_long
WHERE ERRORS > 0 ORDER BY EVENT_ID DESC LIMIT 30;
  • PostgreSQL 14+ [只读]
SELECT application_name, client_addr, count(*) AS sessions,
       count(*) FILTER (WHERE state='idle in transaction') AS idle_in_xact
FROM pg_stat_activity GROUP BY application_name, client_addr
ORDER BY sessions DESC LIMIT 30;
SELECT datname, xact_commit, xact_rollback, deadlocks
FROM pg_stat_database WHERE datname='<DB>';
  • 关键判断字段:application_name/客户端来源对应的服务与版本;xact_rollback 相对 xact_commit 的比例(回滚多说明重试或失败频繁);idle in transaction 会话说明事务边界过宽;错误语句历史中是否有重复失败的同一指纹(读取 events_statements_history_long 前需确认其 consumer 已启用,默认关闭,启用属 [变更]);发布时间与死锁起始时间的关系。

  • 下一步:确认反序路径→“事务加锁顺序不一致”并纳入代码评审;事务边界过宽→缩短事务、移出事务内的外部调用;重试不幂等→改造为幂等并使用有界指数退避;无法归因→在预发用并发测试复现并保留死锁图。

2.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"死锁"场景(锁与并发故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
发生频率:<VALUE>(次数 / 时间段);集中的接口或批处理:<VALUE>
 
【前置检查补充】
0.2 需确认的日志:实例错误日志。
0.7 innodb_print_all_deadlocks 是否开启(用 OpenAPI 读参数);未开启则死锁图可能不全。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_ROW_LOCK —— 行锁等待时间与等待次数(间接侧证)
  死锁次数没有对应的性能 Key:以错误日志与 innodb_metrics 的 lock_deadlocks 为准。
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:(若暴露)死锁计数指标。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):错误日志中的死锁记录:PostgreSQL 的 deadlock
  detected 与 DETAIL、MySQL 死锁条目。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:发版、批量任务上线时间。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:innodb_print_all_deadlocks、deadlock_timeout 等参数当前值。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
降级兜底:innodb_print_all_deadlocks 未开启时日志只保留最近一次死锁:
  此时频率结论标注为下限值,不得当作完整计数。
需 DBA 执行并粘贴:最近死锁图、死锁累计计数、两侧事务的 SQL 与索引定义。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 从错误日志提取死锁记录,按时间、接口与对象聚合频率。
  2) 从 ARMS 取受影响接口的错误率与重试次数,评估业务影响。
  3) 查参数:innodb_print_all_deadlocks 是否开启(未开启则日志可能不全,开启属 [变更])。
  4) 查事件:死锁起始时间是否与发版或批任务对齐。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 死锁图与计数:
   MySQL:SHOW ENGINE INNODB STATUS;    -- 取 LATEST DETECTED DEADLOCK 段
   SELECT name, count FROM information_schema.innodb_metrics
   WHERE name = 'lock_deadlocks';
   -- 状态变量 Innodb_deadlocks 仅 MariaDB 与 Percona 提供,官方 MySQL 没有
   PostgreSQL:从错误日志导出 deadlock detected 及其 DETAIL 段(含双方进程与语句)
2) 加锁顺序还原:
   提供两侧事务的完整语句序列,并补 SHOW CREATE TABLE <TABLE>;(MySQL)
   或 \d+ <TABLE>(PostgreSQL psql)以确认索引与约束
 
【候选原因与判定补充】
- 事务加锁顺序不一致
- 并发更新同一批数据
- 索引缺失导致锁范围扩大
死锁被自动回滚是正常保护机制,评估重点是频率与业务影响,不是把数量降到零。
  建议须落到应用改造(统一加锁顺序、缩短事务、幂等重试),仅调参数通常不解决根因。

2.5 解决方案

统一锁顺序,缩短事务并按主键稳定排序分批;补充必要索引;应用只对可安全重试的事务做有限次指数退避。修改索引/约束为 [变更],需评估锁、空间和复制。不要关闭死锁检测或仅增大超时来掩盖根因——关闭检测会把死锁变成长时间锁等待,影响面更大。

2.6 验证与预防

用并发测试复现原场景并确认死锁计数不再增长、重试无重复副作用。代码评审纳入事务顺序和幂等性;按 SQL 指纹监控死锁频率,并保留数据库日志中的死锁图。

3. 长事务

3.1 现象描述

事务持续时间远超业务基线,可能阻塞 DDL/更新、占用连接、扩大 undo/WAL、阻碍垃圾回收并增加故障恢复时间;PostgreSQL 还可能出现 idle in transaction。

3.2 常见原因

3.2.1 应用未提交/回滚

  • 判断依据:会话处于 idle in transaction 或 MySQL 中事务无当前 SQL;等待事件指向客户端;连接池归还了带未结束事务的连接;异常分支缺少收尾逻辑。

  • 验证方法:按“事务内 SQL 逐条分析”确认事务在等客户端;核对应用异常路径与 ORM 的事务边界;检查连接池归还时是否回滚。

  • 处置指向:代码层用 try/finally 强制提交或回滚;设置 idle_in_transaction_session_timeout 等与业务匹配的超时([变更],需灰度);连接归还前重置事务状态。

3.2.2 批量操作未拆分

  • 判断依据:单个事务修改行数极大、运行时间与数据量成正比;ETL/清理/迁移任务一次性提交;undo/WAL 与锁等待随任务进度同步增长。

  • 验证方法:查看事务的 trx_rows_modified/影响行数与任务日志进度;核对任务的批大小与提交频率配置。

  • 处置指向:按主键有序分批、每批提交并支持断点续跑;控制批大小与速率;把批任务安排到低峰窗口并与备份/维护错峰。

3.2.3 事务内包含外部调用或人工等待

  • 判断依据:事务打开后长时间无 SQL,等待事件指向客户端;事务跨越 HTTP/RPC 调用、消息发送、文件处理或人工审批;应用侧已超时但数据库事务仍存在。

  • 验证方法:结合“事务内 SQL 逐条分析”的等待事件与应用链路追踪,定位事务边界内的外部调用。

  • 处置指向:把外部调用移出事务边界,采用“先本地事务后异步补偿”或 Saga 模式;设置分层的语句与事务超时;应用超时后必须显式回滚,不能只丢弃连接。

3.3 排查步骤

3.3.1 活跃事务列表查看

  • 目的:取得当前所有未结束事务的清单与状态,区分空闲事务与仍在推进的事务。

  • MySQL 8.0 [只读]

SELECT trx_id, trx_mysql_thread_id, trx_state, trx_started, trx_requested_lock_id,
       trx_rows_locked, trx_rows_modified, trx_isolation_level, trx_query
FROM information_schema.innodb_trx ORDER BY trx_started LIMIT 50;
SELECT trx_state, COUNT(*) AS trx_count FROM information_schema.innodb_trx GROUP BY trx_state;
  • PostgreSQL 14+ [只读]
SELECT pid, usename, application_name, client_addr, state,
       xact_start, query_start, state_change,
       backend_xid, backend_xmin, wait_event_type, wait_event, left(query,120) AS q
FROM pg_stat_activity
WHERE xact_start IS NOT NULL AND pid <> pg_backend_pid()
ORDER BY xact_start LIMIT 50;
SELECT count(*) FILTER (WHERE state='idle in transaction') AS idle_in_xact,
       count(*) FILTER (WHERE state='idle in transaction (aborted)') AS idle_aborted
FROM pg_stat_activity;
  • 关键判断字段:trx_started/xact_start 最早的事务;trx_state(RUNNING/LOCK WAIT)与 PostgreSQL state(active/idle in transaction);trx_rows_modified/backend_xid 是否表示已有实际写入;应用与客户端来源。“长”以业务基线判断,不使用统一分钟阈值。

  • 下一步:空闲事务→“事务内 SQL 逐条分析”并联系应用所有者;仍在推进的批处理→“事务执行时长排序”估算完成时间;阻塞他人→“锁等待”场景;已有大量修改→“事务对系统的影响评估(undo 膨胀、主从延迟等)”先估算回滚成本。

3.3.2 事务执行时长排序

  • 目的:按事务年龄排序,量化最老事务造成的“保留窗口”,并为处置排优先级。

  • MySQL 8.0 [只读]

SELECT trx_id, trx_mysql_thread_id, trx_state,
       TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS xact_age_s,
       trx_rows_locked, trx_rows_modified, LEFT(trx_query, 120) AS q
FROM information_schema.innodb_trx
ORDER BY trx_started ASC LIMIT 20;
  • PostgreSQL 14+ [只读]
SELECT pid, state,
       now()-xact_start   AS xact_age,
       now()-query_start  AS query_age,
       now()-state_change AS in_state,
       application_name, left(query,120) AS q
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
ORDER BY xact_start ASC LIMIT 20;
SELECT max(now()-xact_start) AS oldest_xact_age
FROM pg_stat_activity WHERE xact_start IS NOT NULL;
  • 关键判断字段:最老事务年龄与业务基线/SLO 的比值;query_age 与 xact_age 的差值(差值大说明事务在空闲等待);in_state 停留时长;是否有一批事务同时变老(指向共同的外部依赖或批任务)。

  • 下一步:仍在可接受范围→设置告警并观察;显著超基线→“事务对系统的影响评估(undo 膨胀、主从延迟等)”评估影响后按本场景“解决方案”处置;一批事务同时变老→检查上游依赖、外部调用超时与连接池归还逻辑。

3.3.3 事务内 SQL 逐条分析

  • 目的:还原事务内已执行的语句序列,判断事务是卡在外部等待、已完成大部分工作,还是可安全取消。

  • MySQL 8.0 [只读]

SELECT t.THREAD_ID, t.PROCESSLIST_ID, t.PROCESSLIST_STATE, t.PROCESSLIST_TIME
FROM performance_schema.threads t WHERE t.PROCESSLIST_ID = <PID>;
SELECT EVENT_ID, SQL_TEXT, TIMER_WAIT, LOCK_TIME, ROWS_AFFECTED, ROWS_EXAMINED, ERRORS
FROM performance_schema.events_statements_history
WHERE THREAD_ID = (SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_ID = <PID>)
ORDER BY EVENT_ID DESC LIMIT 30;
  • PostgreSQL 14+ [只读]
SELECT pid, state, xact_start, query_start, state_change,
       wait_event_type, wait_event, backend_xid, backend_xmin, query
FROM pg_stat_activity WHERE pid = <PID>;
SELECT locktype, mode, granted, relation::regclass AS object
FROM pg_locks WHERE pid = <PID> ORDER BY granted;
  • 关键判断字段:语句序列中的写操作数量与影响行数(决定回滚成本);是否处于 wait_event_type='Client'(等应用发下一条语句,典型空闲事务);已持有的锁清单与模式;ERRORS 或 idle in transaction (aborted) 表明事务已进入失败态,只需回滚。

  • 注意:语句文本可能含敏感参数,现场记录使用脱敏摘要;events_statements_history 需相应 consumer 已启用。

  • 下一步:等待客户端且无写入→可较安全地取消(仍需通知所有者);已大量写入→先按“事务对系统的影响评估(undo 膨胀、主从延迟等)”估算回滚成本;处于失败态→请应用回滚;仍在推进→等待完成或按本场景“解决方案”拆批治理。

3.3.4 事务对系统的影响评估(undo 膨胀、主从延迟等)

  • 目的:量化长事务对版本保留、清理能力、日志空间与复制的影响,作为“继续等待还是强制终止”的决策依据。

  • MySQL 8.0 [只读]

SELECT NAME, COUNT, STATUS FROM information_schema.innodb_metrics
WHERE NAME IN ('trx_rseg_history_len','lock_row_lock_current_waits');
SHOW GLOBAL STATUS WHERE Variable_name IN
('Innodb_os_log_written','Innodb_buffer_pool_pages_dirty','Innodb_row_lock_current_waits');
-- SHOW REPLICA STATUS 为 8.0.22+;更早小版本用 SHOW SLAVE STATUS
SHOW REPLICA STATUS FOR CHANNEL '<CHANNEL>';
  • PostgreSQL 14+ [只读]
SELECT datname, age(datfrozenxid) AS xid_age FROM pg_database ORDER BY 2 DESC;
SELECT relname, n_live_tup, n_dead_tup, last_autovacuum, last_autoanalyze
FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 20;
SELECT slot_name, active, wal_status,
       pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS retained_bytes,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM pg_replication_slots
-- 必须按数值排序:pg_size_pretty 是文本,字典序会把 9 MB 排在 10 GB 之前;restart_lsn 可为 NULL
ORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC NULLS LAST;
-- wal_status 的 unreserved/lost 仅在 max_slot_wal_keep_size 非负时才可能出现;该参数默认 -1,此时只会是 reserved/extended
SELECT application_name, state, replay_lsn,
       pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS byte_lag
FROM pg_stat_replication;
  • 关键判断字段:trx_rseg_history_len 的增长趋势(undo 历史长度);n_dead_tup 与 last_autovacuum(清理是否被阻塞);age(datfrozenxid) 反映的事务 ID 年龄风险;binlog/WAL 增长与槽保留字节;复制 byte_lag 是否同步扩大。

  • 下一步:版本保留/undo 持续增长→尽快让根事务结束;日志逼近容量→同时执行“磁盘空间不足”场景(存储与日志故障)与“binlog/WAL 异常增长或保留”场景;清理被阻塞→结束长事务后确认 autovacuum 追平;复制延迟扩大→“主从/流复制延迟”场景。

3.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"长事务"场景(锁与并发故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
最长事务时长:<VALUE>;是否已影响清理或复制:是 / 否
 
【前置检查补充】
0.2 需确认的日志:实例错误日志。
0.7 两个事务超时参数的当前值是否可读;活跃事务列表必须由 DBA 提供。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_ROW_LOCK —— 行锁等待时间与等待次数(毫秒 / 个)
  MySQL_ReplicationDelay —— 备库复制延迟(秒)
  MySQL_DetailedSpaceUsage —— 总空间、数据、日志、临时、系统空间(MB)
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:活跃会话数、复制延迟、磁盘使用率与其增速。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):错误日志中的事务中空闲超时、语句取消或会话终止记录。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:批量任务、发版时间表。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:idle_in_transaction_session_timeout、statement_timeout 等参数当前值。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
降级兜底:活跃事务列表无法从平台侧取得:DBA 未配合时,长事务的存在与
  时长标注为不可得,只能用连带现象(延迟、空间增速)做间接推断并说明。
需 DBA 执行并粘贴:按时长排序的活跃事务、事务内已执行语句、清理与复制受阻证据。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 取复制延迟、磁盘使用率与活跃会话曲线,判断长事务的连带影响是否已出现。
  2) 查参数:两个超时参数是否设置,未设置说明长事务缺少兜底。
  3) 从 ARMS 找事务型接口的长耗时样本与其中的外部调用等待。
  4) 查事件与批任务时间表,定位可疑批处理。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 活跃事务与时长排序:
   MySQL:SELECT trx_id, trx_started,
   TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS age_s,
   trx_state, trx_rows_locked, trx_query
   FROM information_schema.innodb_trx ORDER BY trx_started;
   PostgreSQL:SELECT pid, usename, application_name, state, xact_start,
   now() - xact_start AS age, left(query,120) AS q
   FROM pg_stat_activity WHERE xact_start IS NOT NULL
   ORDER BY xact_start;
2) 连带影响(清理受阻、槽保留、两阶段事务残留):
   PostgreSQL:SELECT backend_xmin FROM pg_stat_activity
   WHERE backend_xmin IS NOT NULL ORDER BY age(backend_xmin) DESC LIMIT 5;
   SELECT * FROM pg_prepared_xacts;
   SELECT slot_name, active, wal_status, restart_lsn
   FROM pg_replication_slots;
   MySQL:SELECT * FROM information_schema.innodb_metrics
   WHERE name LIKE 'trx_rseg%' OR name LIKE '%undo%';
 
【候选原因与判定补充】
- 应用未提交或未回滚
- 批量操作未拆分
- 事务内包含外部调用或人工等待
把后果写成链路:持锁到阻塞、阻塞清理到膨胀、保留日志到空间风险,
  指明当前处在哪一环。
  终止长事务属 [变更] 甚至 [高风险],必须先确认事务内容、回滚代价与业务确认人。

3.5 解决方案

优先让应用提交/回滚,暂停制造新长事务的任务。终止会话是 [高风险]:大事务回滚可能比继续完成更久并制造 IO 峰值;必须评估日志空间、回滚进度、锁影响和应用幂等性。长期应拆批、缩短事务边界,并设置与业务匹配的事务/空闲事务超时;改超时是 [变更],需灰度。

3.6 验证与预防

确认最老事务年龄、undo/WAL 增速、锁等待和清理能力恢复;持续观察回滚完成。监控事务年龄分位数和 idle in transaction,在代码层强制 try/finally 提交/回滚,批任务支持断点和小事务。

4. 主从/流复制延迟

4.1 现象描述

只读节点数据陈旧、读后写不一致,复制延迟增长或回放停止。单一“秒级延迟”指标可能失真,应分解发送、接收、落盘、回放和应用位点。

4.2 常见原因

4.2.1 大事务或批量写入

  • 判断依据:单事务修改行数极大或 Binlog_cache_disk_use 非零;延迟阶跃式上升并在事务提交后集中回放;批任务、DDL、数据迁移与延迟起点吻合。

  • 验证方法:按“主库写入压力分析”定位最大事务与日志生成速率;核对批任务时间表与发布记录。

  • 处置指向:拆分为小事务并按主键有序分批、限速执行、错峰安排;大 DDL 前估算日志量与延迟影响。

4.2.2 从库规格不足

  • 判断依据:从库 CPU/IO/内存饱和而主库正常;回放长期落后但无错误;从库同时承担大量只读查询。

  • 验证方法:对比主从资源曲线与规格;检查从库上的只读查询负载与回放争用。

  • 处置指向:提升从库规格或增加只读节点分担查询([变更]);把重查询移到专用只读实例;必要时临时把关键读路由回主库。

4.2.3 单线程回放瓶颈

  • 判断依据:MySQL 仅个别 applier worker 在推进、并行 worker 数过小或提交顺序约束限制并行;写入集中在单表/单主键区间;PostgreSQL 单一回放进程且 replay_backlog 持续增长。

  • 验证方法:按“从库回放速度分析”查看 worker 状态与参数;分析写入在表/键上的分布是否高度集中。

  • 处置指向:评估并行复制参数与分组提交([变更],须满足一致性要求);从数据模型上分散热点;降低单表写入集中度。

4.2.4 网络带宽或抖动

  • 判断依据:在途字节数大而回放差距小;网络流出带宽贴住上限;RTT/丢包/重传上升;跨地域链路事件与延迟起点吻合。

  • 验证方法:按“网络延迟排查”对照带宽、RTT、重传与心跳连续性。

  • 处置指向:启用传输/日志压缩、限流非关键写入、提升带宽规格([变更]);跨地域复制按 RPO 要求重新设计拓扑。

4.3 排查步骤

4.3.1 复制状态与延迟量查看

  • 目的:先确认复制通道是否健康、有无错误,并用位点/字节差而不是只看秒级延迟来量化落后程度。

  • MySQL 8.0 [只读]

-- SHOW REPLICA STATUS 为 8.0.22+;更早小版本用 SHOW SLAVE STATUS
SHOW REPLICA STATUS FOR CHANNEL '<CHANNEL>';
SELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER, LAST_ERROR_MESSAGE, LAST_ERROR_TIMESTAMP
FROM performance_schema.replication_applier_status_by_worker;
SELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER, LAST_ERROR_MESSAGE
FROM performance_schema.replication_connection_status;
  • PostgreSQL 14+ [只读]
SELECT application_name, client_addr, state, sync_state,
       sent_lsn, write_lsn, flush_lsn, replay_lsn,
       pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS byte_lag,
       write_lag, flush_lag, replay_lag
FROM pg_stat_replication;
-- 以下在备库执行。注意:pg_stat_wal_receiver 在 walreceiver 未运行时返回 0 行,
-- 若把回放信息与它写在同一条查询里,接收中断时回放信息会一起消失,故拆成两条。
SELECT status, latest_end_lsn, latest_end_time, sender_host, sender_port
FROM pg_stat_wal_receiver;
SELECT pg_last_xact_replay_timestamp() AS last_replay_ts,
       now()-pg_last_xact_replay_timestamp() AS replay_delay;
  • 关键判断字段:MySQL Replica_IO_Running/Replica_SQL_Running、Seconds_Behind_Source(空闲期可能失真)、Retrieved_Gtid_Set 与 Executed_Gtid_Set 的差、Last_Error*;PostgreSQL 分阶段 LSN(sent→write→flush→replay)与 byte_lag、*_lag 时间列、sync_state。

  • 版本注记:SHOW REPLICA STATUS、Replica_IO_Running、Replica_SQL_Running、Seconds_Behind_Source 均为 MySQL 8.0.22+ 的新名称;8.0.22 之前请用旧名 SHOW SLAVE STATUS、Slave_IO_Running、Slave_SQL_Running、Seconds_Behind_Master(旧名在 8.0.22+ 仍可用但已弃用)。PostgreSQL 侧 pg_stat_wal_receiver 在 walreceiver 未运行时返回 0 行,空结果本身就是“接收已停止”的证据,不要据此认为回放信息缺失。

  • 下一步:接收停止→“网络延迟排查”与“网络连通性故障”场景(连接类故障);回放停止且有错误→修复数据/DDL 冲突([变更/高风险],须保证一致性);持续推进但落后→“主库写入压力分析”、“从库回放速度分析”;指标为 NULL/空闲→结合角色与写入量判断。

4.3.2 主库写入压力分析

  • 目的:判断延迟是否由主库日志生成速率超过从库消化能力引起,并定位产生大量日志的写入源。

  • MySQL 8.0 [只读]

-- SHOW MASTER STATUS 在 8.2.0 弃用、8.4.0 移除;小版本为 8.4+ 时改用 SHOW BINARY LOG STATUS
SHOW MASTER STATUS;
SHOW BINARY LOGS;
SHOW GLOBAL STATUS WHERE Variable_name IN
('Com_insert','Com_update','Com_delete','Innodb_rows_inserted','Innodb_rows_updated',
 'Innodb_rows_deleted','Innodb_os_log_written','Binlog_cache_use','Binlog_cache_disk_use');
SELECT trx_id, trx_started, trx_rows_modified, LEFT(trx_query,120) AS q
FROM information_schema.innodb_trx ORDER BY trx_rows_modified DESC LIMIT 10;
  • PostgreSQL 14+ [只读]
SELECT pg_current_wal_lsn() AS lsn, now() AS sampled_at;
SELECT datname, xact_commit, tup_inserted, tup_updated, tup_deleted
FROM pg_stat_database WHERE datname='<DB>';
SELECT queryid, calls, rows, shared_blks_dirtied, shared_blks_written, query
FROM pg_stat_statements ORDER BY shared_blks_dirtied DESC LIMIT 20;
  • 关键判断字段:用两次采样的 binlog 文件大小或 pg_current_wal_lsn() 差值算出日志生成速率(字节/秒);Binlog_cache_disk_use 非零说明大事务超出缓存;单事务修改行数最大的事务;写入速率是否与延迟增长同步。

  • 下一步:日志速率突增且与批任务/发布吻合→限流、拆批、错峰(应用侧优先);存在超大事务→“长事务”场景;主库写入正常→“从库回放速度分析”;日志总量本身异常→“binlog/WAL 异常增长或保留”场景(存储与日志故障)。

4.3.3 从库回放速度分析

  • 目的:判断从库是否为消化瓶颈:并行度不足、单表热点、规格不足,或从库上的长查询阻碍回放。

  • MySQL 8.0 [只读]

SELECT CHANNEL_NAME, WORKER_ID, SERVICE_STATE,
       LAST_APPLIED_TRANSACTION, APPLYING_TRANSACTION,
       APPLYING_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP,
       LAST_ERROR_NUMBER, LAST_ERROR_MESSAGE
FROM performance_schema.replication_applier_status_by_worker;
-- replica_parallel_workers / replica_parallel_type / replica_preserve_commit_order 为 8.0.26+ 名称;
-- 8.0.26 之前用旧名 slave_parallel_workers / slave_parallel_type / slave_preserve_commit_order
SHOW VARIABLES WHERE Variable_name IN
('replica_parallel_workers','replica_parallel_type','replica_preserve_commit_order');
SELECT COUNT(*) AS long_reads FROM information_schema.processlist
WHERE COMMAND='Query' AND TIME > 60;
  • PostgreSQL 14+ [只读]
-- 以下在备库执行
SELECT pg_is_in_recovery() AS in_recovery,
       pg_last_wal_receive_lsn() AS received,
       pg_last_wal_replay_lsn()  AS replayed,
       pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn()) AS replay_backlog,
       pg_last_xact_replay_timestamp() AS last_replay_ts;
SELECT name, setting FROM pg_settings
WHERE name IN ('hot_standby_feedback','max_standby_streaming_delay','recovery_min_apply_delay','wal_receiver_timeout');
SELECT pid, now()-query_start AS age, wait_event_type, wait_event, left(query,100) AS q
FROM pg_stat_activity WHERE state='active' ORDER BY query_start LIMIT 20;
  • 关键判断字段:MySQL 是否只有个别 worker 在推进(单线程热点)、APPLYING_TRANSACTION 是否长期不变、并行复制参数;PostgreSQL received 与 replayed 的差(replay_backlog 大说明已落盘但回放慢)、max_standby_streaming_delay 与备库长查询的冲突、从库 CPU/IO 是否饱和。

  • 注意:跳过事务、放宽错误处理、recovery_min_apply_delay 等设置会影响数据一致性,任何调整属 [变更/高风险]。

  • 下一步:从库资源饱和→扩容或下移读流量([变更]);单线程/热点表限制并行→评估并行复制参数与表设计;备库长查询冲突→治理只读查询或调整冲突策略;接收侧异常→“网络延迟排查”。

4.3.4 网络延迟排查

  • 目的:确认主从之间链路的带宽、时延与稳定性是否足以承载日志速率,排除跨可用区/跨地域链路问题。

  • OS/云控制台方法(厂商中立)[只读]:查看实例网络流出带宽是否贴住规格上限、跨可用区/跨地域链路时延与丢包、专线/对等连接状态;有主机权限时用 ping/mtr 观察 RTT 与丢包,用 ss -ti 观察重传。

# [只读] 观察主从之间的 RTT、丢包与 TCP 重传
mtr -r -c 20 <HOST>
ss -ti state established "( dport = :<PORT> )" | head -40
  • MySQL 8.0 [只读]
-- HOST/PORT 属于 replication_connection_configuration,replication_connection_status 没有这两列,
-- 需按 CHANNEL_NAME 关联两表
SELECT c.CHANNEL_NAME, c.HOST, c.PORT,
       s.SERVICE_STATE, s.SOURCE_UUID,
       s.LAST_HEARTBEAT_TIMESTAMP, s.COUNT_RECEIVED_HEARTBEATS
FROM performance_schema.replication_connection_configuration c
JOIN performance_schema.replication_connection_status s USING (CHANNEL_NAME);
-- replica_net_timeout 为 8.0.26+(旧名 slave_net_timeout);binlog_transaction_compression 为 8.0.20+
SHOW VARIABLES WHERE Variable_name IN ('replica_net_timeout','binlog_transaction_compression');
  • PostgreSQL 14+ [只读]
SELECT application_name, client_addr, state, sync_state,
       write_lag, flush_lag, replay_lag,
       pg_wal_lsn_diff(sent_lsn, write_lsn) AS in_flight_bytes
FROM pg_stat_replication;
SELECT name, setting FROM pg_settings
WHERE name IN ('wal_receiver_timeout','wal_sender_timeout','wal_compression','tcp_keepalives_idle');
  • 关键判断字段:RTT 与重传率相对基线的变化;网络流出带宽是否贴住实例上限(贴顶即限流);MySQL 心跳是否连续、LAST_HEARTBEAT_TIMESTAMP 是否停滞;PostgreSQL sent_lsn 与 write_lsn 的在途字节数大而回放差距小,说明链路是瓶颈。

  • 下一步:带宽贴顶→限流写入、启用日志/传输压缩([变更],需评估 CPU 开销)或提升规格;链路抖动/丢包→网络负责人排查 MTU、NAT、拥塞(“网络连通性故障”场景(连接类故障));心跳停滞→检查认证、防火墙与超时参数;网络正常→回到“主库写入压力分析”、“从库回放速度分析”。

4.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"主从/流复制延迟"场景(锁与并发故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
拓扑:<VALUE>(主备、一主多从、跨地域);只读流量是否走从库:是 / 否
 
【前置检查补充】
0.2 需确认的日志:实例错误日志。
0.7 复制延迟指标是否覆盖所有备库与只读实例;缺失的节点单独列出。
  CMS2_WORKSPACE 路由下用 starops observe entity search 枚举同一主库下的只读实例。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_ReplicationDelay —— 备库复制延迟(秒)
  slavestat —— 只读实例延迟(秒)
  MySQL_ReplicationThread —— IO 与 SQL 复制线程状态(1 正常,0 线程丢失)
  MySQL_QPSTPS —— 主库每秒执行与事务数,用于判断写入压力(个/秒)
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:复制延迟(秒与字节)、主库写入量、备库 CPU 与 IO、网络流量。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):错误日志中的复制中断、回放错误、心跳超时记录。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:主备切换、维护、规格变更。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:只读实例清单与延迟、端点权重、复制相关参数当前值。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
UModel 拓扑:哪些应用在读从库或只读端点,用于界定摘除端点的影响面。
  降级兜底:跨地域或自建备库可能不在同一监控视图内:缺失节点逐个列出
  并标注不可得,不要用已覆盖节点的延迟代表全局。
需 DBA 执行并粘贴:复制线程与位点状态、并行回放参数、备库长查询冲突情况。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 取复制延迟曲线与主库写入量、备库 CPU 与 IO 叠加,初判是接收慢、回放慢还是资源不足。
  2) 查事件:切换、维护、规格变更的时间点。
  3) 查错误日志中的复制中断与回放错误。
  4) 从 OpenAPI 取只读实例延迟与端点权重,结合 UModel 列出正在读从库的应用。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 复制状态与延迟量:
   MySQL(备库):SHOW REPLICA STATUS;    -- 8.0.22+;更早版本用 SHOW SLAVE STATUS
   关注 Replica_IO_Running、Replica_SQL_Running、Seconds_Behind_Source、
   Retrieved_Gtid_Set 与 Executed_Gtid_Set 的差值、Last_Error
   PostgreSQL(主库):SELECT application_name, state, sent_lsn, write_lsn,
   flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag FROM pg_stat_replication;
   PostgreSQL(备库):SELECT pg_is_in_recovery(), pg_last_xact_replay_timestamp(),
   now() - pg_last_xact_replay_timestamp() AS replay_delay;
2) 回放能力与冲突:
   MySQL:SHOW VARIABLES WHERE Variable_name IN ('replica_parallel_workers',
   'replica_parallel_type','replica_preserve_commit_order');
   -- 8.0.26+ 为新名,更早版本用 slave_* 旧名
   PostgreSQL(备库):SELECT count(*) FROM pg_stat_activity WHERE state='active';
   SHOW hot_standby_feedback; SHOW max_standby_streaming_delay;
 
【候选原因与判定补充】
- 大事务或批量写入
- 从库规格不足
- 单线程回放成为瓶颈
- 网络带宽不足或链路抖动
延迟期间从库读的一致性风险必须显式说明;摘除只读端点或调权重属 [变更]。
  若延迟由复制槽或消费停滞引起,改按 binlog/WAL 保留方向处置消费侧。

4.5 解决方案

暂停非关键批写、拆小事务、修复复制错误、提升从库资源;按业务一致性要求将关键读临时路由主库。改并行复制、同步级别、故障切换或重建副本为 [变更/高风险],必须确认 RPO/RTO、数据完整性、容量和回切方案。不得仅清除错误或跳过事务继续复制而不验证数据一致性。

4.6 验证与预防

确认位点持续推进、字节/时间延迟回归业务 SLO,主从抽样或校验工具无差异,读路由恢复前完成一致性验证。监控复制各阶段、日志生成速率和槽保留;大变更前估算日志量,定期演练故障切换与重建。

四、存储与日志故障

1. 磁盘空间不足

1.1 现象描述

可用空间快速下降,写入失败、实例进入只读/保护状态、日志或临时文件无法扩展,备份/DDL 失败。云平台可能在达到保护线前自动扩容或限制操作。

1.2 常见原因

1.2.1 业务数据自然增长

  • 判断依据:数据与索引空间随业务量平稳增长,无异常阶跃;表行数与业务指标同步;缺少分区与归档设计。

  • 验证方法:对照业务量指标与表增长速率;检查是否存在历史数据保留策略。

  • 处置指向:建立分区/归档/保留策略与容量规划;按增长外推提前扩容,避免每次都在告警时被动处理。

1.2.2 日志文件未清理

  • 判断依据:binlog/WAL 文件数量或文本日志目录持续增大;SHOW BINARY LOGS 累积很多文件;pg_ls_waldir() 文件数远超配置预期;归档失败或过期策略未生效。

  • 验证方法:按“日志文件过大”场景的“日志清理策略检查”与“binlog/WAL 异常增长或保留”场景的“binlog/WAL 生成与清理状态检查”检查保留与清理策略、归档状态、复制槽保留。

  • 处置指向:修复归档/复制消费者并使用数据库支持的机制清理;严禁手工删除 MySQL redo/binlog/undo 文件或 PostgreSQL pg_wal 文件。

1.2.3 临时文件/排序文件堆积

  • 判断依据:Created_tmp_disk_tables、temp_files/temp_bytes 在故障窗口激增;空间随大查询运行涨落;临时空间与数据同盘导致互相挤压。

  • 验证方法:定位产生临时对象的 SQL 指纹(“慢查询”场景的“慢查询日志定位与提取”、“日志文件过大”场景的“日志生成速率分析”);确认临时目录位置与容量。

  • 处置指向:优化排序/分组/连接以消除落盘、限制并发、控制单查询工作内存;必要时把临时空间与数据分离([变更])。

1.2.4 无归档与保留策略

  • 判断依据:表中存在远超业务需要的历史数据;无分区、无冷热分层、无归档任务;备份与日志保留期限未与合规要求对齐。

  • 验证方法:核对数据保留要求与实际最早数据时间;检查是否存在已停用但未清理的表与索引。

  • 处置指向:制定并落地保留/归档策略与分区方案([变更],需合规确认);注意删除数据未必立即归还文件系统空间,需配合重整或分区裁剪。

1.3 排查步骤

1.3.1 磁盘使用率趋势分析

  • 目的:确认剩余空间、增长形态(阶跃/持续/周期)与自动扩容上限,估算“还剩多久”。

  • 云控制台/OS 方法(厂商中立)[只读]:查看实例存储使用率曲线、自动扩容配置与上限、配额与保护线策略、最近的 DDL/备份/导入事件;有主机权限时用 df -h、du -sh <PATH>/* 查看文件系统与目录占用。

  • MySQL 8.0 [只读]

-- data_length/data_free 取自数据字典缓存,information_schema_stats_expiry 默认 86400 秒;
-- 需要实时值(尤其是要做两点差值)时先在本会话关闭缓存,有额外开销
SET SESSION information_schema_stats_expiry = 0;
SELECT ROUND(SUM(data_length+index_length)/1024/1024/1024,2) AS total_gb,
       ROUND(SUM(data_free)/1024/1024/1024,2) AS reusable_gb
FROM information_schema.tables;
-- 空间盘点优先用 innodb_tablespaces 的 FILE_SIZE/ALLOCATED_SIZE(字节,来自 InnoDB 自身元数据)
SELECT NAME, FILE_SIZE, ALLOCATED_SIZE, STATE, SPACE_TYPE
FROM information_schema.innodb_tablespaces
ORDER BY FILE_SIZE DESC LIMIT 20;
-- information_schema.files 仅供参考:字节值为近似(TOTAL_EXTENTS 不计文件末尾的部分区),
-- 8.0.21 起查询需 PROCESS 权限,且不包含 redo 与 binlog
SELECT FILE_NAME, TABLESPACE_NAME,
       ROUND(TOTAL_EXTENTS*EXTENT_SIZE/1024/1024,2) AS approx_size_mb
FROM information_schema.files LIMIT 20;
  • PostgreSQL 14+ [只读]
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database ORDER BY pg_database_size(datname) DESC;
SELECT pg_size_pretty(sum(pg_database_size(datname))) AS all_db_size FROM pg_database;
  • 关键判断字段:数据库对象总量与文件系统占用的差额(差额通常是日志、临时文件、备份与未回收空间);reusable_gb/data_free 表示可复用但未归还的空间;剩余时间按近期增长速率与已计划的变更估算,而不是套用固定百分比阈值。

  • 注意:information_schema.tables 的 data_length/data_free 等动态列受数据字典缓存影响(information_schema_stats_expiry 默认 86400 秒),做差值前必须先 SET SESSION information_schema_stats_expiry = 0 或先 ANALYZE TABLE([变更])。information_schema.files 的字节值仅为近似(TOTAL_EXTENTS 不计文件末尾的部分区),8.0.21 起查询该表需 PROCESS 权限,且不包含 redo 与 binlog;因此空间盘点应改用 information_schema.innodb_tablespaces 的 FILE_SIZE(文件表观大小)与 ALLOCATED_SIZE(实际分配大小),并关注 STATE;redo/binlog 量另按“日志文件过大”场景与“binlog/WAL 异常增长或保留”场景统计。

  • 下一步:日志占主导→“日志文件过大”场景、“binlog/WAL 异常增长或保留”场景;数据占主导→“各类型文件空间占用拆解(数据/日志/临时/索引)”、“大表与大文件识别”;突发阶跃→关联 DDL、批导入、长事务与复制槽;接近保护线→先按本场景“解决方案”扩容止损。

1.3.2 各类型文件空间占用拆解(数据/日志/临时/索引)

  • 目的:把总占用拆成数据、索引、事务日志、文本日志、临时文件、备份等类别,避免对错误的对象做治理。

  • 云控制台/OS 方法(厂商中立)[只读]:查看控制台的空间使用拆解(数据空间/日志空间/临时空间/备份空间);有主机权限时按目录统计(数据目录、日志目录、pg_wal、临时目录)。

  • MySQL 8.0 [只读]

-- data_length/index_length/data_free 来自数据字典缓存,需要实时值时先关闭本会话缓存(有额外开销)
SET SESSION information_schema_stats_expiry = 0;
SELECT table_schema,
       ROUND(SUM(data_length)/1024/1024,2)  AS data_mb,
       ROUND(SUM(index_length)/1024/1024,2) AS index_mb,
       ROUND(SUM(data_free)/1024/1024,2)    AS free_mb
FROM information_schema.tables
GROUP BY table_schema ORDER BY (SUM(data_length)+SUM(index_length)) DESC;
SHOW BINARY LOGS;
SHOW GLOBAL STATUS WHERE Variable_name IN ('Created_tmp_disk_tables','Innodb_os_log_written');
-- innodb_redo_log_capacity 为 8.0.30+;更早版本看 innodb_log_file_size × innodb_log_files_in_group
SHOW VARIABLES WHERE Variable_name IN ('innodb_redo_log_capacity','tmpdir');
-- innodb_undo_tablespaces 自 8.0.14 起已弃用且不可配置,不要再当作 undo 容量配置项查看;
-- undo 表空间数量看状态变量,实际文件大小与状态看 innodb_tablespaces(STATE 可为 active/inactive/empty)
SHOW STATUS LIKE 'Innodb_undo_tablespaces%';
SELECT NAME, FILE_SIZE, ALLOCATED_SIZE, STATE, SPACE_TYPE
FROM information_schema.innodb_tablespaces
WHERE SPACE_TYPE = 'Undo' ORDER BY FILE_SIZE DESC;
SHOW VARIABLES WHERE Variable_name IN ('innodb_undo_log_truncate','innodb_max_undo_log_size');
  • PostgreSQL 14+ [只读]
SELECT schemaname,
       pg_size_pretty(sum(pg_relation_size(relid)))       AS heap,
       pg_size_pretty(sum(pg_indexes_size(relid)))        AS indexes,
       pg_size_pretty(sum(pg_total_relation_size(relid))) AS total
FROM pg_stat_user_tables GROUP BY schemaname
ORDER BY sum(pg_total_relation_size(relid)) DESC;
SELECT count(*) AS wal_files, pg_size_pretty(sum(size)) AS wal_size FROM pg_ls_waldir();
SELECT datname, temp_files, pg_size_pretty(temp_bytes) AS temp_bytes
FROM pg_stat_database WHERE datname='<DB>';
  • 关键判断字段:索引占比是否异常高于数据(冗余索引);binlog/WAL 文件数量与总大小;temp_files/temp_bytes 与 Created_tmp_disk_tables 指示的临时空间;redo 容量看 innodb_redo_log_capacity(8.0.30+)或更早版本的 innodb_log_file_size × innodb_log_files_in_group;undo 看 Innodb_undo_tablespaces_* 状态变量与 innodb_tablespaces 中 undo 表空间的 FILE_SIZE/ALLOCATED_SIZE/STATE,能否回缩由 innodb_undo_log_truncate 与 innodb_max_undo_log_size 决定(innodb_undo_tablespaces 自 8.0.14 起弃用且不可配置,不能作为容量判据);free_mb/data_free 表示的碎片空间(受数据字典缓存影响,见上文前提)。

  • 注意:pg_ls_waldir() 需相应权限(通常 pg_monitor);托管实例可能不暴露文件层,此时以控制台拆解为准。

  • 下一步:数据/索引为主→“大表与大文件识别”;WAL/binlog 为主→“binlog/WAL 异常增长或保留”场景;文本日志为主→“日志文件过大”场景;临时文件为主→“内存压力或 OOM”场景(性能类故障)与本章“临时文件/排序文件堆积”。

1.3.3 大表与大文件识别

  • 目的:定位贡献最大的表、索引与大对象,为归档、分区、清理或重建排定顺序。

  • MySQL 8.0 [只读]

-- table_rows/data_length/data_free 取自数据字典缓存(information_schema_stats_expiry 默认 86400 秒);
-- 需要实时值时先关闭本会话缓存(有额外开销),或先对目标表 ANALYZE TABLE([变更])
SET SESSION information_schema_stats_expiry = 0;
SELECT table_schema, table_name, engine, table_rows,
       ROUND(data_length/1024/1024,2)  AS data_mb,
       ROUND(index_length/1024/1024,2) AS index_mb,
       ROUND(data_free/1024/1024,2)    AS free_mb
FROM information_schema.tables
WHERE table_type='BASE TABLE'
ORDER BY (data_length+index_length) DESC LIMIT 20;
  • PostgreSQL 14+ [只读]
SELECT schemaname, relname,
       pg_size_pretty(pg_total_relation_size(relid)) AS total,
       pg_size_pretty(pg_relation_size(relid))       AS heap,
       pg_size_pretty(pg_indexes_size(relid))        AS indexes,
       n_live_tup, n_dead_tup, last_autovacuum
FROM pg_stat_user_tables
ORDER BY pg_total_relation_size(relid) DESC LIMIT 20;
SELECT indexrelname, pg_size_pretty(pg_relation_size(indexrelid)) AS idx_size, idx_scan
FROM pg_stat_user_indexes ORDER BY pg_relation_size(indexrelid) DESC LIMIT 20;
  • 关键判断字段:TOP 表的总大小、数据/索引比例、table_rows/n_live_tup 与业务预期是否相符;n_dead_tup 与 last_autovacuum 指示的膨胀;idx_scan=0 的大索引是候选下线对象(需足够观察周期);行数与死元组均为估算值。

  • 下一步:可归档的历史数据→按合规保留策略归档/分区([变更]);膨胀明显→先解决长事务(“长事务”场景(锁与并发故障))再评估重整;冗余大索引→纳入索引审计后再下线;需量化增速→“空间增长速率评估”。

1.3.4 空间增长速率评估

  • 目的:用两个时间点的差值量化增长速率,估算耗尽时间,并为扩容规模与治理优先级提供依据。

  • 云控制台方法(厂商中立)[只读]:导出存储使用率时间序列,分别对数据空间、日志空间、备份空间做速率与趋势外推;核对自动扩容上限与配额是否足以覆盖预测增长。

  • MySQL 8.0 [只读]

-- 间隔固定时间执行两次并比较差值;避免高频执行以减少开销
-- 前提(必须):information_schema.tables 的动态列来自数据字典缓存,information_schema_stats_expiry
-- 默认 86400 秒,不关闭缓存时两次采样可能完全相同、差值恒为 0。
-- 每个采样会话都要先执行下面这行(绕过缓存直读存储引擎、有额外开销),
-- 或在两次采样前分别对目标表 ANALYZE TABLE(属 [变更])。
SET SESSION information_schema_stats_expiry = 0;
SELECT NOW() AS sampled_at,
       ROUND(SUM(data_length+index_length)/1024/1024,2) AS size_mb
FROM information_schema.tables;
-- 日志类增速不要用 information_schema.tables 推算,改用下面两者的差值
SHOW BINARY LOGS;
SHOW GLOBAL STATUS LIKE 'Innodb_os_log_written';
  • PostgreSQL 14+ [只读]
-- 间隔固定时间执行两次并比较差值
SELECT now() AS sampled_at,
       pg_database_size('<DB>') AS db_bytes,
       pg_current_wal_lsn() AS wal_lsn;
SELECT count(*) AS wal_files, sum(size) AS wal_bytes FROM pg_ls_waldir();
  • 关键判断字段:单位时间的数据字节增量、WAL/binlog 字节增量(用 pg_wal_lsn_diff 或文件大小差)、临时文件峰值;预计耗尽时间 = 剩余空间 ÷ 当前净增速率,并计入已计划的 DDL、导入、备份与保留策略。

  • 注意:MySQL 8.0 用 information_schema.tables 做两点差值算数据增长速率默认不成立——table_rows/data_length/data_free/update_time 来自数据字典缓存,information_schema_stats_expiry 默认 86400 秒,缓存未过期时两次采样可能完全相同、差值恒为 0。必须在每个采样会话先 SET SESSION information_schema_stats_expiry = 0(绕过缓存直读存储引擎,有额外开销),或在采样前对目标表执行 ANALYZE TABLE([变更])。日志类增速不要依赖这些列,应改用 SHOW BINARY LOGS 的文件大小差值或 Innodb_os_log_written 等状态变量差值;PostgreSQL 侧用 pg_current_wal_lsn() 的 pg_wal_lsn_diff 差值。

  • 下一步:耗尽时间紧迫→立即按本场景“解决方案”扩容并暂停高增长任务;日志增速为主因→“binlog/WAL 异常增长或保留”场景;数据增速为主因→归档/分区治理;速率异常且无业务解释→检查“日志文件未清理”、“临时文件/排序文件堆积”与复制槽保留。

1.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"磁盘空间不足"场景(存储与日志故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
当前使用率与剩余空间:<VALUE>;是否已触发实例只读:是 / 否
 
【前置检查补充】
0.2 需确认的日志:实例错误日志。
0.7 平台是否提供空间分项统计;不提供时表级与文件级占用只能由 DBA 提供。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_DetailedSpaceUsage —— 总空间使用、数据空间、日志空间、临时空间、系统空间(MB)
  MySQL_IOPS —— 每秒 IO 请求数,用于判断清理或扩容动作带来的 IO 抖动(个/秒)
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:磁盘使用率与增速、(若平台暴露)数据/日志/临时空间分项。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):错误日志中的空间告警、写入失败、只读锁定记录。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:自动扩容记录、空间告警、锁定只读事件。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:磁盘规格与上限、是否支持在线扩容、空间分项统计接口。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
降级兜底:平台若不提供空间分项,只能得到总量曲线:此时占用构成标注为
  不可得,必须由 DBA 提供表级与文件级明细后才能判定主因。
需 DBA 执行并粘贴:大表与大对象占用、死元组与清理滞后、日志与临时空间占用。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 取磁盘使用率与增速曲线,按近 7 天斜率外推耗尽时间。
  2) 调 OpenAPI 取磁盘规格上限、扩容能力与平台提供的空间分项统计。
  3) 查事件:自动扩容记录、空间告警、是否已触发实例只读。
  4) 从错误日志取空间相关记录,定位增长阶跃的时间点。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 大对象占用排名:
   MySQL:SET SESSION information_schema_stats_expiry = 0;   -- 否则可能取到缓存值
   SELECT table_schema, table_name,
   ROUND((data_length+index_length)/1024/1024) AS mb
   FROM information_schema.tables ORDER BY mb DESC LIMIT 20;  -- 估算值
   SELECT name, file_size, allocated_size, state
   FROM information_schema.innodb_tablespaces ORDER BY file_size DESC LIMIT 20;
   PostgreSQL:SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS sz
   FROM pg_catalog.pg_statio_user_tables
   ORDER BY pg_total_relation_size(relid) DESC LIMIT 20;
2) 膨胀与清理滞后、日志与临时空间:
   PostgreSQL:SELECT relname, n_live_tup, n_dead_tup, last_autovacuum
   FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 20;
   SELECT pg_size_pretty(sum(size)) FROM pg_ls_waldir();
   MySQL:SHOW BINARY LOGS;   -- binlog 总量
   SELECT * FROM information_schema.innodb_tablespaces
   WHERE name LIKE '%undo%' OR name LIKE '%temp%';
 
【候选原因与判定补充】
- 业务数据自然增长
- 日志文件未清理
- 临时文件与排序文件堆积
- 缺少归档与保留策略
止血顺序固定:先清可安全回收项,再扩容,最后治根因;
  因空间满转只读时优先恢复可写。
  清理与扩容分别标注 [变更] 或 [高风险],删数据与收缩表空间属 [高风险]。

1.5 解决方案

最快且最可逆的止损通常是扩容并暂停高增长任务;随后按合规策略归档/删除数据。删除数据未必立即归还文件系统空间。VACUUM FULL <SCHEMA>.<TABLE> 是 [高风险]:需要强锁、额外临时空间并重写表,仅在容量充足、维护窗口、完整备份和回滚计划下执行;优先普通 VACUUM、在线重整能力或逻辑迁移。MySQL 表重建/OPTIMIZE TABLE 同样需评估锁、空间、IO 和复制延迟。

严禁手工删除 MySQL redo/binlog/undo 文件或 PostgreSQL pg_wal 文件。 这可能导致实例无法启动、复制断裂或不可恢复的数据损坏。

1.6 验证与预防

确认写入恢复、剩余空间可覆盖预计增长、备份和维护峰值,且自动扩容上限/配额有效。按数据、索引、日志、临时空间分别监控增长速率和预计耗尽时间;建立分区/归档/保留策略及大 DDL 空间评审。

2. 日志文件过大

2.1 现象描述

错误日志、慢查询日志、审计日志或 PostgreSQL 文本日志增长异常,挤占存储、增加日志采集费用或影响 IO;日志量大不等同于 binlog/WAL 保留,后者见“binlog/WAL 异常增长或保留”场景。

2.2 常见原因

2.2.1 日志级别或开关配置不当

  • 判断依据:general_log、log_statement='all'、log_min_duration_statement=0、log_connections/log_disconnections 或过低的 long_query_time 被临时开启后未关闭;日志量与某次排障时间点吻合。

  • 验证方法:核对参数当前值与 source(是否为动态设置)、变更记录与开启人。

  • 处置指向:按 [变更] 流程恢复基线配置,并为临时诊断开关设置自动到期与责任人;保留已采集样本作为证据。

2.2.2 错误/慢查询风暴

  • 判断依据:同一错误或同一慢 SQL 指纹在短时间内海量重复;伴随连接失败、死锁或超时激增;日志量峰值与故障窗口完全重合。

  • 验证方法:按“日志生成速率分析”做指纹去重统计并定位 TOP 消息;关联对应故障场景。

  • 处置指向:先修复根因(连接、认证、慢查询、锁),日志量随之回落;对重复消息启用采样/去重,不要用关闭日志来“止损”。

2.2.3 日志轮转与保留策略缺失

  • 判断依据:单个日志文件持续增长无轮转;保留天数未设置或过长;归档/上传失败导致本地堆积;多个采集通道重复保存同一份日志。

  • 验证方法:按“日志清理策略检查”检查轮转参数、归档状态与采集代理;核对合规要求的最短保留期。

  • 处置指向:启用合理轮转、压缩与保留([变更],先导出证据再缩短保留);统一采集通道避免重复计费;建立轮转失败与上传失败告警。

2.3 排查步骤

2.3.1 日志文件类型与大小确认(redo/undo/binlog/WAL/error log)

  • 目的:先分清是哪一类日志在膨胀——事务日志(redo/undo/binlog/WAL)与诊断日志(error/slow/general/audit)的治理方式完全不同。

  • 云控制台方法(厂商中立)[只读]:在日志服务中查看各类日志的大小、条数、保留周期与采集规则;托管实例通常不允许直接读取或轮转文件。

  • MySQL 8.0 [只读]

-- innodb_redo_log_capacity 为 8.0.30+;更早版本 redo 容量由 innodb_log_file_size × innodb_log_files_in_group 决定。
-- innodb_undo_tablespaces 自 8.0.14 起已弃用且不可配置,不再作为 undo 容量配置项,改用下面的状态变量与视图。
SHOW VARIABLES WHERE Variable_name IN
('log_error','log_error_verbosity','slow_query_log','slow_query_log_file','long_query_time',
 'log_output','general_log','general_log_file','log_queries_not_using_indexes',
 'innodb_redo_log_capacity','innodb_undo_log_truncate','innodb_max_undo_log_size',
 'log_bin','binlog_expire_logs_seconds');
SHOW STATUS LIKE 'Innodb_undo_tablespaces%';
SELECT NAME, FILE_SIZE, ALLOCATED_SIZE, STATE, SPACE_TYPE
FROM information_schema.innodb_tablespaces
WHERE SPACE_TYPE = 'Undo' ORDER BY FILE_SIZE DESC;
SHOW BINARY LOGS;
SELECT NAME, COUNT FROM information_schema.innodb_metrics WHERE NAME IN ('trx_rseg_history_len');
  • PostgreSQL 14+ [只读]
SELECT name, setting, unit, source FROM pg_settings
WHERE name IN ('logging_collector','log_destination','log_directory','log_filename',
               'log_min_messages','log_min_error_statement','log_statement',
               'log_min_duration_statement','log_lock_waits','log_checkpoints','log_connections');
SELECT count(*) AS wal_files, pg_size_pretty(sum(size)) AS wal_size FROM pg_ls_waldir();
  • 关键判断字段:诊断开关是否被临时打开后忘记关闭(general_log、log_statement='all'、log_min_duration_statement=0、log_connections);log_error_verbosity/log_min_messages 级别;binlog/WAL 文件数量与大小;redo 容量看 innodb_redo_log_capacity(8.0.30+,更早版本看 innodb_log_file_size × innodb_log_files_in_group);undo 看 Innodb_undo_tablespaces_* 状态变量与 innodb_tablespaces 中 undo 表空间的 FILE_SIZE/ALLOCATED_SIZE/STATE,以及 innodb_undo_log_truncate/innodb_max_undo_log_size 是否允许截断回缩(innodb_undo_tablespaces 自 8.0.14 起弃用且不可配置);trx_rseg_history_len 反映的 undo 历史长度。

  • 下一步:诊断日志为主→“日志生成速率分析”、“日志清理策略检查”;事务日志为主→“binlog/WAL 异常增长或保留”场景;redo/undo 压力大→“长事务”场景(锁与并发故障)与“I/O 性能异常”场景的“日志写入与数据写入分离分析”;空间已告警→“磁盘空间不足”场景先扩容止损。

2.3.2 日志生成速率分析

  • 目的:量化单位时间的日志产生量,并把它归因到连接失败、慢 SQL、锁等待、检查点或错误风暴等具体事件。

  • 云控制台方法(厂商中立)[只读]:用日志服务的写入量/条数曲线定位起始时间点与峰值形态;对同一错误做去重统计,找出高频消息模板。

  • MySQL 8.0 [只读]

SHOW GLOBAL STATUS WHERE Variable_name IN
('Slow_queries','Aborted_connects','Aborted_clients','Connection_errors_max_connections');
-- 死锁计数用 innodb_metrics:Innodb_deadlocks 属 MariaDB/Percona,MySQL 8.0 无此状态变量(查询返回空集);
-- STATUS 为 disabled 时需先 SET GLOBAL innodb_monitor_enable='lock_deadlocks'(属 [变更]),计数自启用或重置起算
SELECT NAME, COUNT, STATUS FROM information_schema.innodb_metrics WHERE NAME='lock_deadlocks';
SELECT DIGEST_TEXT, COUNT_STAR, SUM_ERRORS, SUM_WARNINGS,
       ROUND(SUM_TIMER_WAIT/1e12,3) AS total_s
FROM performance_schema.events_statements_summary_by_digest
ORDER BY (SUM_ERRORS+SUM_WARNINGS) DESC LIMIT 20;
  • PostgreSQL 14+ [只读]
SELECT datname, sessions, sessions_abandoned, sessions_fatal, sessions_killed,
       deadlocks, temp_files, temp_bytes, stats_reset
FROM pg_stat_database WHERE datname='<DB>';
-- PG14~16:checkpoint 计数在 pg_stat_bgwriter;PG17 起改为
--   SELECT num_timed, num_requested FROM pg_stat_checkpointer;
SELECT checkpoints_timed, checkpoints_req FROM pg_stat_bgwriter;
  • 关键判断字段:Slow_queries、Aborted_connects、deadlocks、sessions_fatal 的增量与日志量峰值的时间相关性;高频错误/告警指纹;checkpoints_req 偏多时 log_checkpoints 会产生更多记录;诊断阈值(long_query_time、log_min_duration_statement)是否过低导致近似全量记录。

  • 下一步:连接/认证风暴→“连接数耗尽”场景(连接类故障)或“认证失败”场景;慢 SQL 过多→“慢查询”场景(性能类故障);死锁多→“死锁”场景(锁与并发故障);阈值不合理→“日志清理策略检查”与本场景“解决方案”调整([变更])。

2.3.3 日志清理策略检查

  • 目的:确认轮转、压缩、保留与上传机制正常,并保证缩短保留不破坏审计合规与在查事件的证据链。

  • 云控制台方法(厂商中立)[只读]:核对日志服务的保留天数、轮转规则、压缩与归档目标、采集代理状态与失败告警;确认审计日志保留满足合规要求;确认应用与数据库没有重复采集同一份日志。

  • MySQL 8.0 [只读]

SHOW VARIABLES WHERE Variable_name IN
('binlog_expire_logs_seconds','max_binlog_size','log_output','general_log','slow_query_log');
SHOW BINARY LOGS;
  • PostgreSQL 14+ [只读]
SELECT name, setting, unit FROM pg_settings
WHERE name IN ('log_rotation_age','log_rotation_size','log_truncate_on_rotation',
               'logging_collector','archive_mode','archive_command');
SELECT archived_count, failed_count, last_archived_wal, last_failed_wal, last_failed_time
FROM pg_stat_archiver;
  • 关键判断字段:log_rotation_age/log_rotation_size 是否有效、log_truncate_on_rotation 与文件名模板是否配合;binlog_expire_logs_seconds/max_binlog_size 是否生效;pg_stat_archiver.failed_count 与 last_failed_time(归档失败会让日志无法回收);采集代理是否堆积未上传文件。

  • 下一步:轮转未生效→按 [变更] 修正配置;归档失败→修复归档目标(同时见“binlog/WAL 异常增长或保留”场景的“binlog/WAL 生成与清理状态检查”);采集堆积→修复代理并清理已上传文件;不要直接删除活动日志文件,使用平台或数据库支持的轮转/清理机制。

2.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"日志文件过大"场景(存储与日志故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
占用最大的日志类型:<VALUE>;平台是否托管轮转:是 / 否
 
【前置检查补充】
0.2 需确认的日志:慢日志、错误日志与审计日志等各类投递库。
0.7 日志是否由平台托管轮转与留存,留存策略当前值是否可读。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_DetailedSpaceUsage —— 日志空间分项与总空间(MB)
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:磁盘使用率与日志空间分项、SLS 写入量与索引量。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):各投递库的写入速率与内容分布:重复错误、慢日志暴增、
  审计量。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:参数组变更(日志开关与阈值)、审计开关变更。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:日志相关参数当前值、日志备份与留存策略、审计配置。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
降级兜底:审计日志与 SQL 洞察若未开启,日志构成只能靠 DBA 侧文件清单:
  缺失时标注为不可得,不要用磁盘总量反推日志类型占比。
需 DBA 执行并粘贴:各类日志体积、日志开关与阈值当前值、轮转与保留设置。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 取各投递库写入速率曲线,定位速率突增时间点与主导日志类型。
  2) 查参数:慢日志阈值、general 与审计开关、PostgreSQL 的 log_statement 与
     log_min_duration_statement 当前值,排除调试配置遗留。
  3) 查磁盘空间分项与留存策略,确认是生成过多还是清理不及时。
  4) 分析日志内容分布,判断是否为错误风暴或慢查询暴增导致的放大。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 日志体积与开关:
   MySQL:SHOW BINARY LOGS;
   SHOW VARIABLES WHERE Variable_name IN ('binlog_expire_logs_seconds',
   'slow_query_log','slow_query_log_file','long_query_time',
   'general_log','log_error');
   -- expire_logs_days 已在 MySQL 8.2.0 移除,不要再配置
   PostgreSQL:SHOW log_min_duration_statement; SHOW log_statement;
   SHOW logging_collector; SHOW log_rotation_age; SHOW log_rotation_size;
   SELECT pg_size_pretty(sum(size)) FROM pg_ls_waldir();
2) 若日志目录可读,提供各类日志文件的大小与最后写入时间清单。
 
【候选原因与判定补充】
- 日志级别或开关配置不当(调试参数遗留)
- 错误或慢查询风暴导致日志放大
- 日志轮转与保留策略缺失
先判定是配置问题(开关与轮转)还是负载问题(错误或慢查询风暴),
  前者改配置,后者治业务。
  清理 binlog 或 WAL 属 [变更] 或 [高风险],须先确认无消费者与备份恢复点依赖。

2.5 解决方案

先修复错误源,启用合理轮转、压缩、保留和采样;敏感 SQL 日志按合规要求脱敏。修改日志级别、慢日志阈值或审计策略为 [变更],需确保不破坏审计合规和调查证据;先导出证据再缩短保留。不要直接删除活动日志文件,使用云平台或数据库支持的轮转机制。

2.6 验证与预防

确认日志增长速率、重复错误、采集费用和 IO 恢复基线,且轮转与上传成功。对临时诊断配置设置自动到期;监控日志摄入量与轮转失败;定期测试审计日志可检索性和保留合规。

3. binlog/WAL 异常增长或保留

3.1 现象描述

MySQL binary log 或 PostgreSQL WAL 占用持续增长,导致空间告警、备份/复制异常;redo 与 undo 也可能增长或造成恢复/清理压力。日志增长可能是正常写入增加,也可能因副本、复制槽、长事务或归档失败无法回收。

3.2 常见原因

3.2.1 写入量激增或大事务

  • 判断依据:日志生成速率阶跃上升且与批任务、导入、DDL、发布时间吻合;Binlog_cache_disk_use 非零;单事务修改行数极大;binlog_row_image=FULL 或宽表更新放大日志体积。

  • 验证方法:按“日志空间占用趋势分析”用两点差值计算速率;定位最大事务与高写入 SQL 指纹。

  • 处置指向:拆分事务、限速批量写、错峰执行;评估行镜像与日志压缩设置([变更],需确认复制与 CDC 兼容性)。

3.2.2 复制槽/副本消费停滞

  • 判断依据:PostgreSQL 槽 active=false 或 restart_lsn 长期不推进、wal_status 变为 extended/unreserved/lost(unreserved/lost 仅在 max_slot_wal_keep_size 非负时才会出现);MySQL 副本停止或报错但 binlog 仍需保留;CDC 任务下线未清理注册。

  • 验证方法:按“主从同步对日志的依赖分析”核对每个消费者的位点、活跃状态与归属。

  • 处置指向:优先修复或重建消费者使位点推进;确认永久废弃后按本场景“解决方案”的 [高风险] 流程删除槽或清理 binlog;为每个槽/通道登记所有者与到期策略。

3.2.3 保留策略或归档失败

  • 判断依据:pg_stat_archiver.failed_count 持续增长、last_failed_wal 停在同一文件;MySQL binlog_expire_logs_seconds 过长或未生效;PITR/备份保留窗口要求保留大量日志;wal_keep_size 设置过大。

  • 验证方法:按“binlog/WAL 生成与清理状态检查”检查归档状态与保留参数;核对备份与 PITR 策略实际需要的窗口。

  • 处置指向:修复归档目标(权限、容量、网络);按 RPO/RTO 与合规重新设定保留窗口([变更]);禁止手工删除 redo/binlog/undo 与 pg_wal 文件,只能使用数据库或平台支持的机制。

3.3 排查步骤

3.3.1 binlog/WAL 生成与清理状态检查

  • 目的:确认日志配置、当前位点、文件清单、过期与归档策略是否生效,区分“生成太多”与“回收不掉”。

  • MySQL 8.0 [只读]

SHOW BINARY LOGS;
-- SHOW MASTER STATUS 在 8.2.0 弃用、8.4.0 移除;小版本为 8.4+ 时改用 SHOW BINARY LOG STATUS
SHOW MASTER STATUS;
-- binlog_transaction_compression 为 8.0.20+;innodb_redo_log_capacity 为 8.0.30+
-- (更早版本 redo 容量由 innodb_log_file_size × innodb_log_files_in_group 决定)
SHOW VARIABLES WHERE Variable_name IN
('log_bin','binlog_format','binlog_row_image','max_binlog_size',
 'binlog_expire_logs_seconds','binlog_transaction_compression','innodb_redo_log_capacity');
  • PostgreSQL 14+ [只读]
SELECT pg_current_wal_lsn() AS current_lsn, pg_walfile_name(pg_current_wal_lsn()) AS current_file;
SELECT name, setting, unit, source FROM pg_settings
WHERE name IN ('wal_level','max_wal_size','min_wal_size','wal_keep_size',
               'archive_mode','archive_command','archive_timeout','wal_compression');
SELECT archived_count, failed_count, last_archived_wal, last_archived_time,
       last_failed_wal, last_failed_time
FROM pg_stat_archiver;
SELECT count(*) AS wal_files, pg_size_pretty(sum(size)) AS wal_size FROM pg_ls_waldir();
  • 关键判断字段:binlog_expire_logs_seconds 是否生效、最旧 binlog 文件的时间;failed_count/last_failed_wal 表示归档失败(归档失败时 WAL 无法回收);wal_keep_size 与 max_wal_size(后者是检查点软目标,不是磁盘硬上限);binlog_row_image=FULL 会显著放大日志量。

  • 下一步:归档失败→修复归档目标并确认 failed_count 停止增长;文件数远超配置预期→“主从同步对日志的依赖分析”;生成速率异常→“日志空间占用趋势分析”与“主从/流复制延迟”场景的“主库写入压力分析”;redo/undo 压力→“长事务”场景(锁与并发故障)与“I/O 性能异常”场景的“日志写入与数据写入分离分析”。

3.3.2 主从同步对日志的依赖分析

  • 目的:确认所有消费者(副本、CDC、备份/PITR、逻辑订阅)的最旧安全位点,避免清理掉仍被需要的日志。

  • MySQL 8.0 [只读]

-- SHOW REPLICA STATUS 为 8.0.22+;更早小版本用 SHOW SLAVE STATUS
SHOW REPLICA STATUS FOR CHANNEL '<CHANNEL>';
SELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER, LAST_ERROR_MESSAGE
FROM performance_schema.replication_connection_status;
SELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER, LAST_ERROR_MESSAGE
FROM performance_schema.replication_applier_status_by_worker;
SHOW PROCESSLIST;
  • PostgreSQL 14+ [只读]
SELECT slot_name, slot_type, database, active, active_pid,
       restart_lsn, confirmed_flush_lsn, wal_status,
       pg_size_pretty(safe_wal_size) AS safe_wal_size,
       pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS retained_bytes,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM pg_replication_slots
-- 不能按 pg_size_pretty 的文本结果排序(字典序会把 9 MB 排在 10 GB 之前);restart_lsn 可为 NULL
ORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC NULLS LAST;
-- 下面两列均受 max_slot_wal_keep_size 约束:该参数默认 -1 时 safe_wal_size 恒为 NULL,wal_status 也不会出现 unreserved/lost
SHOW max_slot_wal_keep_size;
SELECT application_name, client_addr, state, sent_lsn, flush_lsn, replay_lsn,
       pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS byte_lag
FROM pg_stat_replication;
  • 关键判断字段:MySQL 中通过 SHOW PROCESSLIST 的 Binlog Dump 线程确认仍在拉取的消费者及其位点、副本是否停止或报错;PostgreSQL 槽的 active、wal_status(reserved/extended/unreserved/lost)、restart_lsn 与保留字节、safe_wal_size 余量(这两项都取决于 max_slot_wal_keep_size:该参数默认 -1 时 safe_wal_size 恒为 NULL、wal_status 也只会是 reserved/extended,要把余量和 lost 当判据须先将其设为非负值,属 [变更]);byte_lag 是否持续扩大。

  • 注意:槽 active=false 不代表无用;必须确认消费者归属、RPO 要求与重建成本后才能处置。

  • 下一步:消费者停止但仍需要→修复或重建后推进位点(“主从/流复制延迟”场景(锁与并发故障));确认废弃→按本场景“解决方案”的 [高风险] 流程清理;wal_status='lost'→该消费者已无法续传,需重建并复盘监控缺口;空间紧迫→先扩容再处置。

3.3.3 日志空间占用趋势分析

  • 目的:量化日志生成与回收的平衡关系,判断是持续超产、单次尖峰还是回收受阻,并估算耗尽时间。

  • MySQL 8.0 [只读]

-- 间隔固定时间执行两次并比较差值
SELECT NOW() AS sampled_at;
SHOW BINARY LOGS;
SHOW GLOBAL STATUS WHERE Variable_name IN
('Innodb_os_log_written','Binlog_cache_use','Binlog_cache_disk_use');
SELECT trx_id, trx_started, trx_rows_modified, LEFT(trx_query,120) AS q
FROM information_schema.innodb_trx ORDER BY trx_started LIMIT 20;
  • PostgreSQL 14+ [只读]
-- 间隔固定时间执行两次,用 LSN 差值计算生成速率
SELECT now() AS sampled_at, pg_current_wal_lsn() AS lsn;
SELECT pid, now()-xact_start AS xact_age, state, backend_xmin, left(query,120) AS q
FROM pg_stat_activity WHERE xact_start IS NOT NULL ORDER BY xact_start LIMIT 20;
SELECT datname, age(datfrozenxid) AS xid_age FROM pg_database ORDER BY 2 DESC;
  • 关键判断字段:单位时间生成字节数与回收字节数的差值;Binlog_cache_disk_use 非零指向大事务;最老事务年龄与 backend_xmin(长事务会拖延清理);age(datfrozenxid) 反映的事务 ID 年龄;日志空间剩余量 ÷ 净增速率得到预计耗尽时间。

  • 下一步:生成远大于回收且消费者正常→限流/拆批并评估保留策略([变更]);长事务拖延清理→“长事务”场景(锁与并发故障);回收受阻于槽或归档→“binlog/WAL 生成与清理状态检查”、“主从同步对日志的依赖分析”;耗尽时间紧迫→先扩容并停止高日志任务。

3.4 Agent 诊断提示词

把下面整段复制给 STAROps 数字员工,替换全部占位符后提交。模板是自包含的:候选原因、需要 DBA 执行的只读语句都已写在模板内。

任务:定位"binlog/WAL 异常增长或保留"场景(存储与日志故障)的根因,给出可执行处置建议,并保留完整证据链。
 
【输入补充】
日志目录当前总量:<VALUE>;是否存在 CDC 或逻辑订阅:是 / 否
 
【前置检查补充】
0.2 需确认的日志:实例错误日志。
0.7 下游消费者(只读实例、CDC、逻辑订阅)能否通过 OpenAPI 或 UModel 完整枚举;
  不完整则不得判定可清理。
  CMS2_WORKSPACE 路由下用 starops observe entity topo / entity neighbor 枚举下游依赖。
 
指标 Key(MySQL,已核实可直接用于 DescribeDBInstancePerformance 的 Key 参数):
  MySQL_DetailedSpaceUsage —— 日志空间分项(MB)
  MySQL_ReplicationDelay —— 备库复制延迟(秒),用于判断消费是否停滞
版本分支提示(MySQL 8.0 vs 8.4):
  SHOW MASTER STATUS 在 MySQL 8.4 已移除,改用 SHOW BINARY LOG STATUS。
  SHOW SLAVE STATUS 在 MySQL 8.4 已移除,改用 SHOW REPLICA STATUS。
  expire_logs_days 在 MySQL 8.2.0 已移除,改用 binlog_expire_logs_seconds。
  执行前先用 SELECT VERSION() 确认小版本,选用对应语法。
 
【数据源(本场景取值)】状态与降级规则按契约执行
云监控 2.0 / 托管 Prometheus:磁盘使用率与日志空间分项、复制延迟、(若暴露)归档相关指标。
  CMS2_WORKSPACE 路由:starops observe entity metric-data 或 metric_set query(PromQL)。
  ALIYUN_RDS 路由:aliyun rds DescribeDBInstancePerformance 按 Key 拉序列。
SLS(<SLS_PROJECT> 下对应 Logstore):错误日志中的归档失败、槽保留告警、复制中断记录。
  未投递到 SLS 时改用 OpenAPI:慢日志 DescribeSlowLogs(统计)与
  DescribeSlowLogRecords(明细)、错误日志 DescribeErrorLogs。
系统事件与告警:复制中断、订阅下线、备份任务、切换。
  CMS2_WORKSPACE 路由:starops observe alerts query + changes query +
  acm_basic event system-query。
  ALIYUN_RDS 路由:aliyun rds DescribeEvents 查历史事件。
云平台 OpenAPI:日志保留策略与备份设置、只读实例与订阅任务清单、保留与归档参数当前值。
  常用接口:DescribeDBInstanceAttribute(实例详情)、DescribeParameters(参数当前值)。
UModel 拓扑:正在消费该实例日志的下游:只读实例、CDC 任务、逻辑订阅。
  降级兜底:下游消费者清单若无法从 OpenAPI 与 UModel 完整枚举,一律视为
  存在未知消费者:清理类建议只能停留在 [高风险] 待确认,不得给出可清理结论。
需 DBA 执行并粘贴:日志生成与清理状态、复制槽与位点、归档状态。
 
【采集步骤】
A. Agent 自取(逐项回报数据源、取值、采集时间)
  1) 取日志空间与磁盘使用率曲线,按增速外推耗尽时间。
  2) 调 OpenAPI 取保留策略、备份配置、只读实例与订阅任务清单;结合 UModel 列出全部下游消费者。
  3) 查事件与错误日志:归档失败、复制中断、订阅下线的时间点。
  4) 取复制延迟曲线,判断消费停滞是否与日志堆积同步开始。
B. 需 DBA 执行只读语句后粘贴原始输出(Agent 无权限;语句已给全,按引擎选用)
1) 日志生成与清理状态:
   MySQL:SHOW BINARY LOGS;   -- 文件清单与总量
   SHOW BINARY LOG STATUS;   -- 8.4+;8.0 用 SHOW MASTER STATUS(8.4 已移除)
   SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
   PostgreSQL:SELECT pg_current_wal_lsn();
   SELECT pg_size_pretty(sum(size)) FROM pg_ls_waldir();
   SHOW archive_mode; SELECT * FROM pg_stat_archiver;
2) 消费者与保留余量:
   MySQL:SHOW PROCESSLIST;   -- 找 Binlog Dump 线程,确认仍在拉取的消费者与位点
   PostgreSQL:SHOW max_slot_wal_keep_size;   -- 默认 -1,此时下面 safe_wal_size 恒为 NULL
   SELECT slot_name, slot_type, active, active_pid, restart_lsn,
   wal_status, pg_size_pretty(safe_wal_size) AS safe_wal_size,
   pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS retained_bytes
   FROM pg_replication_slots
   ORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)
   DESC NULLS LAST;
 
【候选原因与判定补充】
- 写入量激增或大事务
- 复制槽或副本消费停滞
- 保留策略不当或归档失败
删除复制槽或清理 binlog 属 [高风险]:必须用 UModel、OpenAPI 与 DBA 输出
  共同证明无任何消费者、备份点与合规留存依赖。
  注意 wal_status 的 unreserved 与 lost 仅在 max_slot_wal_keep_size 非负时才可能出现。

3.5 解决方案

先扩容或暂停日志高增长任务,修复复制/归档消费者,再按恢复策略清理。

  • PURGE BINARY LOGS TO '<BINLOG_FILE>' 是 [高风险]:执行前必须确认所有副本/CDC/备份的最旧所需位点、PITR 保留窗口、目标文件边界和可恢复备份,并导出 SHOW BINARY LOGS 与复制状态;误清理会使消费者无法续传。

  • SELECT pg_drop_replication_slot('<SLOT>') 是 [高风险]:仅在业务所有者确认消费者永久废弃或可全量重建、记录槽位点并具备重建容量后执行。活动槽不能简单删除;逻辑槽删除会丢失尚未消费的变更。

  • 调整 binlog/WAL 保留、归档、redo 容量或检查点参数均为 [变更],需满足 RPO/RTO、备份和复制要求,并验证重启/切换条件。

  • 禁止手工删除 MySQL redo、binlog、undo 文件;禁止手工删除 PostgreSQL pg_wal 文件。 只能使用数据库支持的清理机制或云平台流程。

3.6 验证与预防

确认日志生成与回收达到动态平衡、剩余空间可覆盖写入峰值和最长允许中断,所有副本/CDC/归档连续且 PITR 窗口满足策略。为每个复制槽/通道登记所有者、SLO 和到期策略;监控日志生成速率、最旧消费位点、归档失败和预计耗尽时间;定期做恢复演练。

延展阅读

术语表

术语 含义
SLO 服务级别目标,用于判断性能/可用性是否异常,而非依赖通用固定阈值
RPO / RTO 可接受的数据丢失量 / 可接受的恢复时长
连接池 复用数据库连接并限制并发的客户端组件;池总量必须纳入实例连接预算
Performance Schema MySQL 的运行时监控体系,提供语句、等待、锁和内存等视图
pg_stat_statements PostgreSQL SQL 指纹累计统计扩展,需预加载和授权
MVCC 多版本并发控制;长事务会延长旧版本保留
元数据锁 保护对象定义的锁;DDL 常因业务事务而等待
死锁 多事务形成循环等待;数据库通常回滚其中一个事务解环
redo MySQL InnoDB 用于崩溃恢复的重做日志,不是可手工清理的普通文件
undo MySQL 用于事务回滚和 MVCC 的撤销信息,不得手工删除
binlog MySQL 逻辑变更日志,用于复制、CDC 和时间点恢复
WAL PostgreSQL 预写日志,用于崩溃恢复、复制和归档
LSN PostgreSQL 日志序列号,用于衡量发送、落盘和回放位点
复制槽 PostgreSQL 为消费者保留 WAL/变更的位置状态;废弃槽可导致 WAL 无限保留
检查点 将恢复起点向前推进并协调脏页写出的机制;过密可能产生 IO 峰值
PITR 基于基础备份和日志进行时间点恢复

版本基线与边界

项目 基线
MySQL 8.0,默认 InnoDB;优先使用 Performance Schema、sys 库和复制状态表
PostgreSQL 14 及以上;优先使用 pg_stat_*、pg_locks、pg_settings 等系统视图
云环境 厂商中立;部分参数、日志、文件系统和超级用户权限由云平台托管
判断原则 不使用“CPU 超过 80%”“缓存命中率必须 99%”等无依据固定阈值;以业务 SLO、实例规格、工作负载类型和历史基线判断

版本差异会影响字段和可用功能。执行前应核对目标小版本文档,并在只读副本、预发或回放环境验证 SQL。

另需注意基线自身的生命周期:MySQL 8.0 已于 2026-04-21 进入 Sustaining Support,PostgreSQL 14 的社区支持在 2026-11-12 终止。停留在这两个基线意味着不再获得常规缺陷与安全修复,排查中怀疑命中已知缺陷时应优先确认升级路径(MySQL 8.4 LTS、PostgreSQL 15 及以上),并注意升级会同时引入下表列出的语法与参数变更。

本文出现的以下名称受版本约束,跨小版本/大版本使用前必须对照替换:

名称 版本约束与替代
SHOW REPLICA STATUS、Replica_IO_Running、Replica_SQL_Running、Seconds_Behind_Source MySQL 8.0.22+;更早小版本用 SHOW SLAVE STATUS、Slave_IO_Running、Slave_SQL_Running、Seconds_Behind_Master
replica_parallel_workers、replica_parallel_type、replica_preserve_commit_order、replica_net_timeout MySQL 8.0.26+;更早版本用对应 slave_* 旧名
binlog_transaction_compression MySQL 8.0.20+
innodb_redo_log_capacity MySQL 8.0.30+;更早版本 redo 容量由 innodb_log_file_size × innodb_log_files_in_group 决定
performance_schema.tls_channel_status MySQL 8.0.21+;have_ssl 自 8.0.26 起弃用,不再作为 TLS 判据
innodb_undo_tablespaces MySQL 8.0.14 起弃用且不可配置;undo 盘点改用 Innodb_undo_tablespaces% 状态变量与 information_schema.innodb_tablespaces(含 STATE),配合 innodb_undo_log_truncate、innodb_max_undo_log_size
pg_hba_file_rules.rule_number、.file_name PostgreSQL 16+ 才有;PG14 用 line_number 排序
pg_stat_bgwriter 的 checkpoint 相关列 PG17 起迁至 pg_stat_checkpointer 并改名(num_timed/num_requested/write_time/sync_time/buffers_written),buffers_backend、buffers_backend_fsync 被移除
pg_stat_statements.blk_read_time、.blk_write_time PG17 起改名为 shared_blk_read_time、shared_blk_write_time
SHOW MASTER STATUS MySQL 8.2.0 起弃用、8.4.0 起移除;8.4+ 改用 SHOW BINARY LOG STATUS。同批被移除的还有 SHOW SLAVE STATUS、SHOW SLAVE HOSTS,需分别改用 SHOW REPLICA STATUS、SHOW REPLICAS。托管实例小版本可能已是 8.4 LTS,照抄 8.0 语句会直接报语法错误
pg_replication_slots.safe_wal_size、.wal_status 两列的可用性取决于 max_slot_wal_keep_size:该参数默认为 -1,此时 safe_wal_size 恒为 NULL,wal_status 也只会出现 reserved/extended,不会出现 unreserved/lost。要把“槽还剩多少余量”当判据,必须先把该参数设为非负值(属 [变更])

通用诊断原则

  1. 先定影响,再定层次:区分单用户/单可用区/全实例,读/写,偶发/持续;按客户端→DNS→路由/ACL→代理/负载均衡→数据库监听→认证→SQL/事务→存储逐层缩小范围。

  2. 先读后改、一次一变:优先只读查询;为诊断 SQL 设置合理超时;记录变更前值、审批、回滚条件和负责人。

  3. 时间对齐:应用、数据库、云监控与审计日志统一到 UTC 或明确时区;围绕故障前后窗口对比历史基线和正常实例。

  4. 相关不等于因果:CPU、锁、慢 SQL、IO 等可能互为结果;用时间线、等待事件和可复现实验证伪假设。

  5. 不把探测当结论:ping 失败可能只是 ICMP 被禁,不等于 TCP 不通;telnet/nc 成功仅表示 TCP 握手成功,不代表 TLS、数据库认证、授权或 SQL 可用。

  6. 避免观测放大故障:不要在高负载主库执行无过滤的大表扫描、全量 SHOW ENGINE INNODB STATUS 高频采样或广域日志抓取。

  7. 托管边界优先:无法访问主机、参数只读、日志被截断或缺少超级用户权限时,使用云控制台等价指标/诊断功能并提交工单,不尝试绕过平台限制。

官方参考资料

MySQL 8.0

  • Connection Problems

  • Access Control and Account Management

  • Using Encrypted Connections

  • EXPLAIN Statement

  • ANALYZE TABLE Statement

  • Performance Schema data_locks Table

  • Performance Schema Memory Summary Tables

  • InnoDB Deadlocks

  • Configuring InnoDB Buffer Pool Size

  • Replication

  • Performance Schema Replication Tables

  • InnoDB Redo Log

  • InnoDB Undo Logs

  • The Binary Log

  • PURGE BINARY LOGS Statement

PostgreSQL 14+

  • The pg_hba.conf File

  • Secure TCP/IP Connections with SSL

  • Monitoring Database Activity

  • The Statistics Collector / pg_stat_activity

  • Using EXPLAIN

  • pg_stat_statements

  • Explicit Locking

  • Resource Consumption(内存、WAL、后台写进程)

  • Streaming Replication Slots

  • Write-Ahead Logging (WAL)

  • WAL Configuration

  • Continuous Archiving and Point-in-Time Recovery

  • Routine Vacuuming

  • VACUUM

官方文档链接以指定主版本为基线。生产操作前还应核对目标云服务的参数限制、维护行为、备份/PITR、复制实现和当前小版本发布说明。

文章大纲

推荐文章

超过 2000+ 位开发者正在阅读

给 OpenClaw 加上企业级 Memory

给 OpenClaw 加上企业级 Memory

4646 阅读

阿里云 STAROps 全域智能运维平台发布!

阿里云 STAROps 全域智能运维平台发布!

3453 阅读

阿里云正式发布 RCA Benchmark

阿里云正式发布 RCA Benchmark

2716 阅读

推荐视频

UModel 最佳实践 Vol.1 UModel 数据建模全景解读

UModel 最佳实践 Vol.1 UModel 数据建模全景解读

3854 观看50:35
云监控2.0全景:可观测范式升级与智能运维蓝图

云监控2.0全景:可观测范式升级与智能运维蓝图

2532 观看41:06

推荐工具

精选可观测领域开发者工具

云监

云监控 2.0 沙箱体验

7483 使用

免费

免费网络拨测工具

2746 使用