数据库问答题 02:并发、性能与生产故障排查
021 接口突然变慢时,如何判断是慢 SQL 还是锁等待?
难度: 基础
查看参考答案
结论: 同时查看应用 trace、pg_stat_activity 的 wait_event 和阻塞链,再分析执行计划。
原因: 等待时间不一定消耗 CPU;被锁住的快速 SQL在应用侧也会表现为长耗时。
边界: 只看日志中的 SQL 时长无法区分运行和等待,新增索引也不能修复长事务。
落地建议: 保存 pid 与 trace 关联,定位阻塞者、事务开始时间和持锁语句后再处理。
022 如何定位累计消耗最大的 SQL?
难度: 基础
查看参考答案
结论: 使用 pg_stat_statements 综合 calls、total_exec_time、mean_exec_time、块读取和临时块。
原因: 单次最慢查询不一定占用最多资源,高频小查询的累计成本可能更高。
边界: 统计窗口、重置时间和规范化查询会影响比较,不能混用不同时间段。
落地建议: 固定观察窗口,按总时间和业务影响排序,再对代表参数执行 EXPLAIN。
023 EXPLAIN ANALYZE 显示估算行数严重偏小时怎么办?
难度: 进阶
查看参考答案
结论: 先检查统计信息新鲜度、数据倾斜、相关列和表达式,再决定提高统计目标或建立扩展统计。
原因: 错误估算会使规划器低估循环次数、哈希大小或连接结果。
边界: 强制关闭某种连接算法只是在当前参数下绕过症状,可能伤害其他查询。
落地建议: ANALYZE 后比较估算,针对热点列设置统计目标,并用真实参数验证。
024 排序写入临时文件时,是否应该全局增大 work_mem?
难度: 进阶
查看参考答案
结论: 不应直接全局放大,应先减少输入行、利用排序索引并计算并发内存峰值。
原因: work_mem 可被一条查询的多个节点和并行 worker 分别使用。
边界: 某个报表需要大内存不代表所有连接都需要;过大可能触发系统换页或 OOM。
落地建议: 对受控任务使用 SET LOCAL,限制并发,并监控 temp blocks 与总内存。
025 为什么新增索引后查询仍然选择顺序扫描?
难度: 进阶
查看参考答案
结论: 规划器可能判断返回比例高、表小、统计不准或随机回表成本高。
原因: 索引存在只是候选路径,成本模型会比较读取页数、过滤率和排序需求。
边界: 关闭 enable_seqscan 只适合诊断,不应作为生产修复。
落地建议: 检查条件类型、选择性、统计信息和 BUFFERS,确认索引列顺序是否匹配。
026 如何处理 PostgreSQL 序列化失败?
难度: 进阶
查看参考答案
结论: 把 SQLSTATE 40001 视为正常并发结果,对整个事务执行有界重试。
原因: Serializable 为维持可串行化顺序会主动中止危险事务。
边界: 只重试最后一条 SQL 可能丢失事务内前置读取与决策,外部副作用也需幂等。
落地建议: 封装事务级重试,使用退避和次数上限,并记录冲突率调整访问模式。
027 线上频繁死锁时应该怎样修复?
难度: 进阶
查看参考答案
结论: 从死锁日志还原资源顺序,统一加锁顺序并缩短事务,同时保留安全重试。
原因: 死锁来自循环等待,不是简单的单条 SQL 执行过慢。
边界: 提高 deadlock_timeout 只改变检测时机,不能消除循环;随意杀会话可能扩大失败。
落地建议: 启用合适日志,按业务键排序批量更新,避免事务内外部调用。
028 autovacuum 跟不上更新速度时如何处理?
难度: 进阶
查看参考答案
结论: 按热点表调整触发比例、worker 成本与资源,并先清理阻塞回收的长事务。
原因: 只提高 worker 数不能解决长快照或 I/O 已饱和,且全局激进配置会影响其他表。
边界: n_dead_tup 是估算,需要结合任务日志、表大小、更新率和事务年龄。
落地建议: 设定表级参数,观察每次 Vacuum 时长与死元组斜率,逐步调整。
029 表文件很大但删除了多数数据,为什么空间没有归还?
难度: 实战
查看参考答案
结论: 普通 DELETE 和 VACUUM 主要让页内空间可复用,不会通常缩短操作系统文件。
原因: MVCC 保留旧版本,且文件尾部之前的空页不能直接截断。
边界: VACUUM FULL 能重写并收缩但需要强锁和额外空间,不适合默认操作。
落地建议: 先确认未来是否复用空间;确需收缩时规划 pg_repack、重建或维护窗口。
030 长时间 idle in transaction 会造成什么影响?
难度: 实战
查看参考答案
结论: 它可能持有旧快照与锁,阻止 Vacuum 回收并增加膨胀和锁等待。
原因: 会话没有执行 CPU 工作不代表事务没有数据库影响。
边界: 直接终止前要确认业务所有者;超时设置也必须兼顾合法长事务。
落地建议: 修复应用异常路径,缩短事务,设置 idle_in_transaction_session_timeout 并告警。
031 物化视图刷新影响线上负载时如何改进?
难度: 实战
查看参考答案
结论: 调整刷新频率和时间窗,建立合法唯一索引并评估 CONCURRENTLY 或增量汇总表。
原因: 并发刷新保持读取可用但仍完整计算并消耗资源,同一视图也不能并发刷新多次。
边界: 降低刷新频率会增加数据延迟,必须与业务 SLA 一起决策。
落地建议: 记录刷新耗时、源表读取和延迟水位,增加互斥、失败告警和重建流程。
032 分区表查询仍扫描大量分区,应检查什么?
难度: 实战
查看参考答案
结论: 检查查询是否包含可推导的分区键条件、数据类型是否一致,以及裁剪是否启用。
原因: 对子表加普通索引只能优化分区内部,不能替代分区裁剪。
边界: 函数包装、隐式转换或不匹配的分区策略可能让边界无法推导。
落地建议: 用 EXPLAIN 查看 Subplans Removed 和实际分区,改写半开范围条件。
033 复制槽导致磁盘 WAL 快速增长时怎么办?
难度: 实战
查看参考答案
结论: 定位失联消费者,评估其恢复点与业务价值,再推进、重建或删除槽。
原因: 槽会保留下游需要的 WAL,消费者停滞时主库不能按常规回收。
边界: 贸然删除槽可能让下游必须重新初始化,不能只清理文件系统中的 WAL。
落地建议: 监控 restart_lsn、confirmed_flush_lsn 和 retained bytes,设置容量与升级预案。
034 如何验证备份真正可恢复?
难度: 实战
查看参考答案
结论: 在隔离环境定期执行完整恢复,并验证对象、数据、权限、扩展、时间点和应用读写。
原因: 备份任务成功只证明文件产生,不证明链路完整、密钥可用或恢复时间达标。
边界: 逻辑备份、物理备份和 WAL 归档覆盖范围不同,需要分别演练。
落地建议: 定义 RPO/RTO,自动校验备份,记录恢复步骤和实际耗时。
035 如何安全地在线创建大表索引?
难度: 实战
查看参考答案
结论: 使用 CREATE INDEX CONCURRENTLY 并监控阶段、等待、资源和失败后的无效索引。
原因: 并发创建减少写阻塞但需要多阶段扫描,时间和 I/O 通常更高。
边界: 它不能位于事务块;取消或失败可能留下 INVALID 对象仍带来维护成本。
落地建议: 先在副本估算时间,设置窗口和取消条件,完成后核对 indisvalid 与计划。
036 连接数持续打满时,为什么不应只提高 max_connections?
难度: 实战
查看参考答案
结论: 更多后端会增加内存、调度、锁竞争并让数据库同时接收过多工作。
原因: 瓶颈可能是慢查询、连接泄漏、连接池配置或突发流量,而不是上限太低。
边界: 连接池模式还会影响会话状态、临时表和预备语句,不能盲目切换。
落地建议: 分析连接状态与吞吐,限制应用池、设置排队和超时,必要时引入 PgBouncer。
037 同一 SQL 对不同参数性能差异巨大怎么办?
难度: 实战
查看参考答案
结论: 检查数据倾斜、统计信息和预备语句的通用/自定义计划选择。
原因: 热点租户与普通租户可能需要完全不同的扫描和连接策略。
边界: 用单一参数加索引可能改善热点却伤害多数请求,强制计划也会冻结错误假设。
落地建议: 采样不同参数保存计划,改进统计、拆分查询形态或谨慎控制计划缓存。
038 如何判断一个“未使用索引”能否删除?
难度: 实战
查看参考答案
结论: 需要覆盖完整业务周期,并检查约束、低频任务、故障路径和统计重置时间。
原因: idx_scan 为零只表示当前统计窗口没有记录扫描,不代表索引没有正确性作用。
边界: 删除会降低写入成本,但也可能导致父表删除检查、月末任务或灾备操作退化。
落地建议: 从依赖和查询日志建立候选清单,先在测试环境移除并准备 CONCURRENTLY 重建。
039 数据库 CPU 很高但磁盘读取不高,应如何排查?
难度: 实战
查看参考答案
结论: 检查高频 SQL、表达式计算、排序哈希、JIT、行数放大和缓存命中下的大量扫描。
原因: 数据已在缓存中仍需要 CPU 解析、比较、连接和聚合。
边界: 只扩容存储 IOPS 通常无法改善纯 CPU 工作,也不能用命中率证明查询高效。
落地建议: 按 total_exec_time 与 calls 定位,比较实际处理行数和返回行数后减少工作量。
040 一次数据库优化上线前后应保存哪些证据?
难度: 实战
查看参考答案
结论: 保存业务参数、调用频率、延迟分布、执行计划、块读取、临时文件、锁和写入影响。
原因: 只比较一次最低耗时会受到缓存、并发和采样波动影响,无法证明稳定收益。
边界: 新增索引或参数可能把成本转移到写入、内存、Vacuum 或其他查询。
落地建议: 使用相同数据和参数做基线与回归,定义回滚阈值,并在真实并发下观察完整周期。