在实际项目中,数据库性能问题很少由单一因素造成。一次接口响应变慢,表面上可能只是“SQL 执行时间增加”,继续追查后却可能发现:执行计划发生了变化、统计信息没有及时更新、缓存命中率下降,甚至还有长事务持锁导致阻塞。

本文以一次金仓数据库业务系统性能问题为背景,复盘从问题发现、瓶颈定位到优化验证的全过程,重点讨论 SQL 优化、系统资源调优,以及 KWR、KSH、KDDM 等工具在实战中的使用方式。文中涉及的参数和工具能力可能随 KingbaseES 版本、补丁和部署形态有所差异,生产环境应以对应版本产品手册为准。

一、问题背景:接口变慢只是表象

本次问题发生在一个典型的业务查询场景中。系统每天凌晨执行数据同步,白天由多个业务模块查询订单、客户和组织机构数据。上线初期,核心查询接口平均响应时间约为 300 毫秒,P99 延迟稳定在 1 秒以内。

运行一段时间后,业务人员反馈“订单查询越来越慢”。监控数据显示,核心查询接口平均响应时间从 300 毫秒上升到 2 秒左右,高峰期 P99 延迟超过 8 秒;数据库 CPU 使用率约为 60%~70%,并未持续达到 100%;磁盘 I/O 等待在部分时间段明显升高;数据库连接数和活动会话数量增加,少量会话出现锁等待。

因此,不能简单得出“CPU 不高,所以不是数据库问题”的结论。数据库性能需要同时观察数据库时间、等待事件、执行计划以及操作系统资源。

本次调优遵循以下路径:

业务现象 → 确认影响范围与时间段 → KWR/KSH/KDDM 定位主要等待
        → Top SQL 与执行计划分析 → SQL、索引、统计信息和参数调整
        → 锁与 I/O 问题处理 → 压测、回归和效果验证

二、第一步:先确认到底慢在哪里

1. 通过数据库时间判断主要矛盾

首先使用 KWR 生成指定时间段的性能报告。KWR 是金仓数据库提供的自动负载信息库能力,可以通过周期性快照记录操作系统环境、数据库时间、等待事件和 Top SQL 等指标,用于分析一段时间内的整体负载变化。

KWR 更适合回答以下问题:今天高峰期比昨天慢在哪里;发布新版本后数据库负载是否增加;某个时间区间内 CPU、I/O、锁等待或 SQL 时间谁占主导;调整参数或索引后整体指标是否改善。

KWR 报告显示,问题时段的数据库时间主要消耗在 SQL 执行和 I/O 等待上,而不是连接建立或网络等待。Top SQL 中有一条订单查询语句,执行次数很多,累计耗时占比较高。

随后使用 KSH 对异常时间点进行进一步分析。KSH,即明细会话历史,采用会话级采样方式记录会话、应用、等待事件、命令类型和 QueryId 等信息,适合定位某个瞬间发生了什么异常。采样周期和历史数据保留方式与版本配置有关,使用前需要确认当前环境的采集参数。

KSH 结果发现,慢查询并非一直执行很慢,而是在特定时间段出现大量磁盘读取,同时有部分会话等待其他事务释放锁。这说明问题至少包含两个层面:核心 SQL 本身存在优化空间;部分业务事务过长,放大了锁等待和 I/O 压力。

2. 使用 KDDM 形成初步诊断结论

KDDM 是基于 KWR 快照和数据库时间模型生成的自动诊断与建议报告,可以从等待事件、I/O、网络、内存和 SQL 执行时间等方向给出分析结果。

本次 KDDM 报告给出的方向主要包括:检查高耗时 SQL,关注 I/O 等待,确认统计信息是否及时更新,分析锁等待和长事务,并结合执行计划确认是否存在不合理的扫描或连接方式。

KDDM 不能代替人工判断,但可以帮助快速建立排查顺序,避免一开始就盲目修改共享缓存或单会话工作内存等参数。

三、SQL 优化:从 Top SQL 到执行计划

1. 原始 SQL 的问题

经过脱敏后的 SQL 如下:

SELECT o.order_id,
       o.order_no,
       o.customer_id,
       o.order_status,
       o.created_at,
       c.customer_name,
       d.dept_name
FROM sales_order o
LEFT JOIN customer c ON c.customer_id = o.customer_id
LEFT JOIN department d ON d.dept_id = o.dept_id
WHERE o.tenant_id = :tenant_id
  AND o.order_status IN ('PAID', 'SHIPPED')
  AND CAST(o.created_at AS DATE) >= :start_date
  AND CAST(o.created_at AS DATE) < :end_date
ORDER BY o.created_at DESC;

这条语句有几个风险。第一,对 created_at 使用了 CAST,可能使普通字段索引难以直接发挥作用。第二,过滤字段与排序字段缺乏合适的联合索引。第三,前端只展示第一页数据,但 SQL 没有返回行数限制,数据库可能需要读取、排序并传输大量无效数据。

2. 改写时间条件和分页方式

将日期转换改为时间范围条件:

WHERE o.tenant_id = :tenant_id
  AND o.order_status IN ('PAID', 'SHIPPED')
  AND o.created_at >= :start_time
  AND o.created_at < :end_time

同时增加分页限制:

ORDER BY o.created_at DESC, o.order_id DESC
FETCH FIRST :page_size ROWS ONLY;

对于深分页,不建议长期使用很大的 OFFSET。更合适的方式是采用基于游标或键值的分页:

WHERE (o.created_at, o.order_id) < (:last_created_at, :last_order_id)
ORDER BY o.created_at DESC, o.order_id DESC
FETCH FIRST :page_size ROWS ONLY;

具体语法需要结合数据库版本、驱动和兼容模式验证,但原则是一致的:让数据库尽早过滤数据,并避免为深分页重复扫描和丢弃大量记录。

3. 执行计划分析

使用 EXPLAIN 可以查看当前会话下的执行计划,使用 EXPLAIN ANALYZE 可以在实际执行后展示真实耗时、实际返回行数和循环次数。生产环境执行 EXPLAIN ANALYZE 时必须注意,它会真正执行语句,不能对包含修改操作的 SQL 随意使用。

EXPLAIN (ANALYZE, BUFFERS)
SELECT o.order_id, o.order_no, o.customer_id,
       o.order_status, o.created_at
FROM sales_order o
WHERE o.tenant_id = 100
  AND o.order_status IN ('PAID', 'SHIPPED')
  AND o.created_at >= TIMESTAMP '2026-08-01 00:00:00'
  AND o.created_at <  TIMESTAMP '2026-08-02 00:00:00'
ORDER BY o.created_at DESC
FETCH FIRST 50 ROWS ONLY;

分析执行计划时,重点不是只看总成本,而是比较估算行数与实际行数、扫描方式、过滤发生的位置、排序节点耗时、连接方法以及节点的 loops。本次优化前,计划显示数据库扫描了大量订单记录,再执行过滤和排序;实际返回行数与优化器估算值存在较大偏差,说明统计信息已经不能准确反映当前数据分布。

4. 索引、统计信息与 Hint

针对主要访问模式,建立联合索引:

CREATE INDEX idx_sales_order_query
ON sales_order(tenant_id, order_status, created_at DESC, order_id DESC);

ANALYZE sales_order;
ANALYZE customer;
ANALYZE department;

索引列顺序不能机械套用模板,需要结合过滤条件的选择性、排序需求和业务访问模式,通过执行计划验证。新增索引后,还要观察写入和更新性能,确认没有与已有索引重复。

KingbaseES 支持通过 Hint 对优化器生成执行计划进行干预,例如指定连接顺序、连接方法、扫描方法和聚合方式。使用前需要确认当前版本支持的 Hint 类型,并确保已启用 Hint 功能。

/*+ Leading((o c)) Use_NL(c) */
SELECT o.order_id, c.customer_name
FROM sales_order o
JOIN customer c ON c.customer_id = o.customer_id
WHERE o.tenant_id = :tenant_id;

Hint 适合处理经过验证的特殊计划问题,但不应成为第一选择。数据量和数据分布会变化,今天有效的 Hint 可能在几个月后变成负担。上线前必须确认 Hint 是否生效,并记录启用原因、适用版本和回归结果。

5. Query Map 的定向治理

在无法直接修改第三方应用 SQL 的情况下,Query Map 可以用于对特定 SQL 进行定向映射和调优。本次项目中,部分旧应用生成的 SQL 格式固定,短期内无法改动应用代码。我们没有对所有同类 SQL 使用全局规则,而是先精确匹配目标语句,再通过 Query Map 进行范围受控的处理。

使用 Query Map 时应注意匹配规则不能过宽,要区分租户、业务模块和参数场景;映射后的 SQL 必须验证结果一致性;同时建立启用、停用和回退机制,并持续观察映射后的真实执行效果。

四、系统性能调优:内存、I/O 与锁等待

1. 内存不是越大越好

SQL 优化后,CPU 使用率下降,但高峰期 I/O 等待仍然存在。进一步检查发现,部分复杂查询需要排序和哈希聚合,内存不足时会产生临时文件,增加磁盘读写。

数据库内存调优需要同时考虑服务器总内存、操作系统占用、数据库共享缓存、单个会话的排序和哈希开销、并发会话数量以及连接池配置。直接盲目提高单会话工作内存,在高并发环境中可能导致内存过度分配。因此,调整前应结合执行计划、临时文件和并发压测确认根因。

2. I/O 瓶颈排查

I/O 问题不能只看磁盘利用率,还要关注读写延迟、吞吐量、随机读比例、临时文件增长和缓存命中情况。本次排查发现,原始 SQL 扫描了大量无效数据,造成大量随机读取。索引和分页优化后,读取数据量明显下降,磁盘等待同步降低。

I/O 优化的优先顺序通常是:先减少不必要的数据访问,再优化索引和执行计划,然后检查临时文件和排序操作,最后评估存储设备和文件系统。若 SQL 一次读取几百万行,仅仅更换更快的磁盘通常只能缓解问题,不能从根本上解决问题。

3. 锁与阻塞问题

KSH 采样显示,部分查询会话被更新事务阻塞。继续检查后发现,业务批处理在事务中执行了大量操作,并且事务提交前还调用外部服务,导致锁持有时间过长。

处理方式包括将外部接口调用移出数据库事务,把大事务拆成多个小批次,缩短事务中的业务逻辑,统一多个业务模块的更新顺序,为高频更新条件建立索引,并设置合理的锁等待超时。

排查阻塞时,不应只终止被阻塞的会话,还要找到持锁根源。否则即使临时结束一个等待会话,问题仍然会反复出现。

五、调优后的效果验证

调优完成后,我们使用与问题发生时相同的数据量和并发模型进行回归测试,避免只比较开发环境中的单次执行时间。

指标 优化前 优化后
核心查询平均耗时 约 2 秒 约 300 毫秒
P99 响应时间 超过 8 秒 约 1.2 秒
单次查询逻辑读取 大幅偏高 下降约 80%
高峰期 I/O 等待 明显 基本稳定
锁等待时长 秒级波动 显著下降
数据库 CPU 60%~70% 约 40%~55%

上述数据是一次项目复盘中的示例化结果,不能直接作为所有环境的预期收益。不同硬件、数据量、并发量和 SQL 结构会导致结果差异很大,性能优化效果必须通过本地基准、业务压测和生产观察共同确认。

验证工作还包括检查查询结果是否一致、验证分页边界和排序稳定性、观察索引创建后的写入性能、检查主备同步、覆盖完整业务高峰,以及对比调优前后的 KWR Diff 报告。

在这里插入图片描述

图 1 调优前后核心性能指标对比。图中数值为本文案例的归一化结果,优化前统一按 100 计算。

从图 1 可以看到,优化收益并不只体现为平均响应时间下降,P99 延迟和逻辑读取也同步改善。这说明本次处理减少了数据库实际访问的数据量,而不是单纯依靠扩大资源配置来掩盖问题。

在这里插入图片描述

图 2 同一高峰时段的数据库时间构成变化。数据用于说明调优方向,不代表所有生产环境的固定比例。

图 2 进一步说明了 SQL 优化与事务治理之间的关系:高耗时 SQL 下降后,数据库执行时间减少;长事务缩短后,锁等待不再持续放大。性能调优的价值,最终体现在多个指标共同改善,而不是某一项指标单独变好。

六、经验总结:性能调优是一条闭环

这次问题给出的最大经验,是不要把性能调优理解成修改几个参数或创建几个索引。完整的调优过程至少包括四个环节:

采集证据 → 定位瓶颈 → 实施变更 → 对比验证

其中最重要的是采集证据。没有 KWR、KSH、执行计划和操作系统监控数据,调优很容易变成经验猜测。

KWR 更适合观察一段时间内的整体负载和趋势;KSH 更适合定位秒级的会话异常、等待和阻塞;KDDM 可以根据 KWR 快照生成自动诊断建议。三者结合后,能够覆盖从宏观趋势到瞬时异常的不同分析层次。

在 SQL 层面,应优先从 SQL 结构、数据访问量、索引和统计信息入手,再考虑 Hint 或 Query Map。真正稳定的性能,来自合理的数据模型、可维护的 SQL、准确的统计信息、适配业务的索引,以及持续的监控和复盘。

结语

金仓数据库性能调优的核心,不是追求某一个指标的极限,而是让数据库在真实业务负载下保持稳定、可预测和可维护。

一次有效的调优,应该能够回答三个问题:性能问题是如何被发现的,造成问题的根因是什么,优化之后是否通过数据证明确实改善。只有把工具报告、执行计划、资源指标和业务体验结合起来,才能完成从“感觉变慢”到“证据定位、方案实施、效果验证”的完整闭环。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐