
在实际项目中数据库性能问题很少由单一因素造成。一次接口响应变慢表面上可能只是“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 等待明显基本稳定锁等待时长秒级波动显著下降数据库 CPU60%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、准确的统计信息、适配业务的索引以及持续的监控和复盘。结语金仓数据库性能调优的核心不是追求某一个指标的极限而是让数据库在真实业务负载下保持稳定、可预测和可维护。一次有效的调优应该能够回答三个问题性能问题是如何被发现的造成问题的根因是什么优化之后是否通过数据证明确实改善。只有把工具报告、执行计划、资源指标和业务体验结合起来才能完成从“感觉变慢”到“证据定位、方案实施、效果验证”的完整闭环。