SAP-ABAP:性能优化效果验证与回归测试:优化前后性能对比、业务逻辑兼容性校验方案

性能优化效果验证与回归测试:优化前后性能对比、业务逻辑兼容性校验方案

博客简介:讲解性能优化后的验证流程,涵盖优化前后响应时间、内存占用、CPU 使用率的量化对比方法,以及业务逻辑正确性回归测试、边界场景兼容测试的核心要点,梳理优化导致的业务逻辑异常、数据错误等次生问题的排查方案,保障优化效果符合预期且不影响原有业务。


📖 写在前面

性能优化是个"改完代码就想跑"的过程------SAT 显示 DB Time 降了 90%,你迫不及待想上线。

但别急。

你有没有想过这些问题:

  • 把循环内 SELECT 改成了 FOR ALL ENTRIES,但 FATE 空表行为呢?内表为空时它会返回整张表的数据!
  • SELECT * 改成了指定字段,但漏掉了某个业务逻辑依赖的字段,现在跑起来值为空!
  • 加了一个复合索引,新索引的字段顺序和原有其他查询的匹配度怎么样?会不会让别的程序变慢?
  • 优化完 SAT 显示 48 秒,但用户反馈数据和之前不一样了

优化不是"改完代码就完事",而是"改完代码 → 验证性能没坏 → 验证逻辑没坏 → 再考虑上线"。
#mermaid-svg-ahCnZJ5ZSAjx7SBo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ahCnZJ5ZSAjx7SBo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .error-icon{fill:#552222;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .marker.cross{stroke:#333333;}#mermaid-svg-ahCnZJ5ZSAjx7SBo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ahCnZJ5ZSAjx7SBo p{margin:0;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .cluster-label text{fill:#333;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .cluster-label span{color:#333;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .cluster-label span p{background-color:transparent;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .label text,#mermaid-svg-ahCnZJ5ZSAjx7SBo span{fill:#333;color:#333;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .node rect,#mermaid-svg-ahCnZJ5ZSAjx7SBo .node circle,#mermaid-svg-ahCnZJ5ZSAjx7SBo .node ellipse,#mermaid-svg-ahCnZJ5ZSAjx7SBo .node polygon,#mermaid-svg-ahCnZJ5ZSAjx7SBo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .rough-node .label text,#mermaid-svg-ahCnZJ5ZSAjx7SBo .node .label text,#mermaid-svg-ahCnZJ5ZSAjx7SBo .image-shape .label,#mermaid-svg-ahCnZJ5ZSAjx7SBo .icon-shape .label{text-anchor:middle;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .rough-node .label,#mermaid-svg-ahCnZJ5ZSAjx7SBo .node .label,#mermaid-svg-ahCnZJ5ZSAjx7SBo .image-shape .label,#mermaid-svg-ahCnZJ5ZSAjx7SBo .icon-shape .label{text-align:center;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .node.clickable{cursor:pointer;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .arrowheadPath{fill:#333333;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ahCnZJ5ZSAjx7SBo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ahCnZJ5ZSAjx7SBo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ahCnZJ5ZSAjx7SBo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .cluster text{fill:#333;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .cluster span{color:#333;}#mermaid-svg-ahCnZJ5ZSAjx7SBo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ahCnZJ5ZSAjx7SBo rect.text{fill:none;stroke-width:0;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .icon-shape,#mermaid-svg-ahCnZJ5ZSAjx7SBo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .icon-shape p,#mermaid-svg-ahCnZJ5ZSAjx7SBo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .icon-shape .label rect,#mermaid-svg-ahCnZJ5ZSAjx7SBo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ahCnZJ5ZSAjx7SBo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ahCnZJ5ZSAjx7SBo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ahCnZJ5ZSAjx7SBo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 🔧 编码实现
📊 性能量化对比

SAT / STAT / ST05
✅ 业务逻辑回归

结果对比 / 功能测试 / 事务完整性
🔍 边界场景测试

并发 / 锁 / 特殊用户 / 配置差异
📋 次生问题排查

数据不一致 / 其他程序变慢 / 字段缺失
🚀 上线 + 灰度 + 监控

本篇学习目标

  • 掌握 SAT 并排对比、STAT Time 分解对比、ST05 Top SQL 对比三种性能量化方法
  • 学会设计"等价类 + 边界值"的业务逻辑回归测试用例
  • 了解 FATE 空表、Buffer 绕过、索引顺序等容易引入 Bug 的优化操作
  • 掌握并发、锁、特殊用户、配置差异等边界场景的测试方法
  • 掌握次生问题的快速排查思路
  • 建立从开发到上线的完整验证 Checklist

前置阅读 :强烈建议读完第七篇(全流程排查实战)和第一~六篇专项优化篇。本篇大量引用第七篇 ZORDER_REPORT 的代码改动做案例。

适用版本:SAP NetWeaver 7.51+


一、为什么验证是必须的 ------ 三个真实踩坑案例

在讲方法之前,先分享三个我亲眼见过的"优化反而搞出问题"的案例。这些不是极端情况,都是正常开发中很容易犯的错。

1.1 案例一:FATE 空表 Bug 导致报表多了 3000 行

abap 复制代码
" ❌ 改动前(OK):
LOOP AT gt_orders INTO ls_order.
  SELECT SINGLE * FROM vbap INTO ls_item
    WHERE vbeln = ls_order-vbeln.
  APPEND ls_result TO gt_result.
ENDLOOP.
" 如果 gt_orders 为空 → 循环不执行 → gt_result 也是空的 ✅

" ❌ 改动后(Bug!):
SELECT * FROM vbap INTO TABLE gt_items
  FOR ALL ENTRIES IN gt_orders
  WHERE vbeln = gt_orders-vbeln.
" 💣 如果 gt_orders 为空 → FATE 返回 vbap 表的**所有数据**!
" 那天用户选了一个无数据的日期范围 → 报表拉了全表 50000 行!
" ALV 渲染爆了 → WP 内存爆了 → WP 被 Kill → 连锁反应

根因 :改代码时忘了加 IF gt_orders IS NOT INITIAL. 空表保护。

💡 回归测试怎么发现? 如果有一条测试用例是"选择条件查不到数据",就会发现报表应该显示"无数据"但实际上显示了全表。

1.2 案例二:加了索引反而让另一个查询变慢

加的索引 (ERDAT, KUNNR, VBELN) 本意是优化报表,但另一个高频事务里的查询变慢了:

abap 复制代码
SELECT SINGLE * FROM vbak INTO ls_vbak
  WHERE vbeln = gs_order-vbeln.
" 原来走主键索引 PRIMARY(vbeln) → 很快
" 新索引也覆盖了 VBELN,DB 优化器可能决定走新索引
" 但新索引先按 ERDAT 排序 → VBELN 不是第一排序字段 → 查找效率反而更低
" 结果:5ms → 50ms(单次虽少,但该事务每秒跑上千次)

💡 回归测试怎么发现? 把 TOP 20 高频事务也纳入回归范围,跑一次 STAT 看 Time 分布就能发现某事务的 DB Time 突然涨了。

1.3 案例三:SELECT * 改指定字段 → 漏掉了业务依赖的隐含字段

abap 复制代码
" ❌ 改动前:
SELECT * FROM vbap INTO ls_item
  FOR ALL ENTRIES IN gt_orders WHERE vbeln = gt_orders-vbeln.

" ❌ 改动后(漏了 MEINS 字段!):
SELECT vbeln posnr matnr netwr menge werks chargi
  FROM vbap INTO TABLE gt_items FOR ALL ENTRIES IN gt_orders
  WHERE vbeln = gt_orders-vbeln.

" 后面有这段逻辑:
WRITE: ls_item-netwr UNIT ls_item-meins.
" 💣 ls_item-meins 现在是空的 → 输出的金额没有单位!

💡 回归测试怎么发现? 如果完整跑一遍后人工检查输出格式,就能看到金额后面缺少单位。

1.4 三个案例的共同教训

教训 对应验证方法
改了查询逻辑 → 必须加边界 Case 测试(空表、单条、极端值) 等价类测试用例
加了索引 → 必须回归 TOP 20 高频事务(防止"按下葫芦浮起瓢") ST05 Top SQL 全局对比
改了 SELECT 字段 → 必须全局搜索所有引用点 SE80 Where-Used List

🚨 性能优化的 Bug 往往是隐蔽的------它不会让程序 Dump,只会让结果不对或让别的地方变慢。


二、性能量化对比方法 ------ 用数据说话

2.1 三种方法总览

#mermaid-svg-81OhAn4LcmtHBCZs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-81OhAn4LcmtHBCZs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-81OhAn4LcmtHBCZs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-81OhAn4LcmtHBCZs .error-icon{fill:#552222;}#mermaid-svg-81OhAn4LcmtHBCZs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-81OhAn4LcmtHBCZs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-81OhAn4LcmtHBCZs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-81OhAn4LcmtHBCZs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-81OhAn4LcmtHBCZs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-81OhAn4LcmtHBCZs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-81OhAn4LcmtHBCZs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-81OhAn4LcmtHBCZs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-81OhAn4LcmtHBCZs .marker.cross{stroke:#333333;}#mermaid-svg-81OhAn4LcmtHBCZs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-81OhAn4LcmtHBCZs p{margin:0;}#mermaid-svg-81OhAn4LcmtHBCZs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-81OhAn4LcmtHBCZs .cluster-label text{fill:#333;}#mermaid-svg-81OhAn4LcmtHBCZs .cluster-label span{color:#333;}#mermaid-svg-81OhAn4LcmtHBCZs .cluster-label span p{background-color:transparent;}#mermaid-svg-81OhAn4LcmtHBCZs .label text,#mermaid-svg-81OhAn4LcmtHBCZs span{fill:#333;color:#333;}#mermaid-svg-81OhAn4LcmtHBCZs .node rect,#mermaid-svg-81OhAn4LcmtHBCZs .node circle,#mermaid-svg-81OhAn4LcmtHBCZs .node ellipse,#mermaid-svg-81OhAn4LcmtHBCZs .node polygon,#mermaid-svg-81OhAn4LcmtHBCZs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-81OhAn4LcmtHBCZs .rough-node .label text,#mermaid-svg-81OhAn4LcmtHBCZs .node .label text,#mermaid-svg-81OhAn4LcmtHBCZs .image-shape .label,#mermaid-svg-81OhAn4LcmtHBCZs .icon-shape .label{text-anchor:middle;}#mermaid-svg-81OhAn4LcmtHBCZs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-81OhAn4LcmtHBCZs .rough-node .label,#mermaid-svg-81OhAn4LcmtHBCZs .node .label,#mermaid-svg-81OhAn4LcmtHBCZs .image-shape .label,#mermaid-svg-81OhAn4LcmtHBCZs .icon-shape .label{text-align:center;}#mermaid-svg-81OhAn4LcmtHBCZs .node.clickable{cursor:pointer;}#mermaid-svg-81OhAn4LcmtHBCZs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-81OhAn4LcmtHBCZs .arrowheadPath{fill:#333333;}#mermaid-svg-81OhAn4LcmtHBCZs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-81OhAn4LcmtHBCZs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-81OhAn4LcmtHBCZs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-81OhAn4LcmtHBCZs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-81OhAn4LcmtHBCZs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-81OhAn4LcmtHBCZs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-81OhAn4LcmtHBCZs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-81OhAn4LcmtHBCZs .cluster text{fill:#333;}#mermaid-svg-81OhAn4LcmtHBCZs .cluster span{color:#333;}#mermaid-svg-81OhAn4LcmtHBCZs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-81OhAn4LcmtHBCZs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-81OhAn4LcmtHBCZs rect.text{fill:none;stroke-width:0;}#mermaid-svg-81OhAn4LcmtHBCZs .icon-shape,#mermaid-svg-81OhAn4LcmtHBCZs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-81OhAn4LcmtHBCZs .icon-shape p,#mermaid-svg-81OhAn4LcmtHBCZs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-81OhAn4LcmtHBCZs .icon-shape .label rect,#mermaid-svg-81OhAn4LcmtHBCZs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-81OhAn4LcmtHBCZs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-81OhAn4LcmtHBCZs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-81OhAn4LcmtHBCZs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} STAT 快速迭代

5分钟出结果

开发期日常检查
SAT 并排对比

正式验证

给 Code Review 的硬数据
ST05 Top SQL 确认

DB 侧效果

索引是否真的生效

2.2 方法一:SAT Optimization Mode(最完整)

操作步骤:跑优化前的 Trace → Export 为 BEFORE.sat → 跑优化后的 Trace → Ctrl 同时选中两个 Trace → 右键 Compare → Time Analysis。

第七篇 ZORDER_REPORT 的实际对比结果

Metric BEFORE AFTER Delta
Total Runtime 492.3 s 48.1 s 🟢 -444.2 s (-90.2%)
DB Time 421.3 s (85.6%) 22.5 s (46.8%) 🟢 -398.8 s (-94.7%)
ABAP Time 61.2 s (12.4%) 21.5 s (44.7%) 🟢 -39.7 s (-64.9%)
Buffer Time 0.0 s (0.0%) 11.2 s (23.3%) 🔵 缓冲终于生效了
Memory Peak 187 MB 32 MB 🟢 -155 MB (-83%)
Executed Statements 812,456 143,200 🟢 -82%

SAT 对比的关键看点

  • Total Time 降了多少 → 整体优化效果
  • DB Time 占比变化 → After 中 DB 降到 50% 以下就安全了
  • Memory Peak → 有无内存泄漏风险
  • Buffer Time 有无从 0 变有值 → 说明缓冲用上了

🚨 SAT 对比只适用于同一程序、相同输入条件。输入条件不同会导致对比失真。

2.3 方法二:STAT Single Time Analysis(看 Time 分解)

STAT 比 SAT 轻量,跑一次 5 分钟就有结果,适合快速迭代。

Time Component BEFORE AFTER 变化
Total Response Time 95.3 s 23.1 s 🟢 -72s
├── DB Time 82.1 s 10.8 s 🟢 -71s
├── ABAP Processor Time 11.8 s 9.2 s 🟢 -2.6s
└── Other 1.4 s 3.1 s +1.7s

💡 如果 After 中 ABAP Processor Time 占比反而上去了 → DB 瓶颈解除后,原本次要的 ABAP 循环变成了新瓶颈。

2.4 方法三:ST05 Top SQL 对比(看 DB 侧效果)

SQL BEFORE → AFTER 原因
SELECT vbak (主查询) 98s → 3.2s 加了复合索引
SELECT makt (物料描述) 20s → 2.1s 循环内 → FATE 批量 + 缓冲
SELECT vbbe (交货计划) 34s → 1.9s 循环内 SELECT → FATE 批量
合计 218s → 19s 降 91%

必须检查的点

  • Before 是 TABLE ACCESS FULL → After 应变为 TABLE ACCESS BY INDEX RANGE SCAN
  • After 的 Top 10 里有没有冒出新面孔(可能原长查询释放了 DB 连接)

2.5 量化对比报告模板(给管理层/业务同事看)

指标 优化前 优化后 提升
运行时间 492 秒 23 秒 21 倍 🎊
DB Time 占比 85.6% 46.8% 瓶颈已解除
内存峰值 187 MB 32 MB -83%
数据正确性 - - ✅ 8152 行完全一致

💡 给管理层的报告要简洁(重点数字、百分比、结论);给 Code Review 的报告可以更技术一些(加 SAT 截图、ST05 Explain Plan)。


三、业务逻辑正确性回归测试 ------ 确保"快了但没坏"

3.1 回归测试的三层结构

层次 测试什么 怎么测
第一层:结果对比 输出数据是否完全一致 OLD vs NEW 逐行逐字段 VLOOKUP 对比
第二层:功能测试 各分支逻辑是否正常 等价类划分:正常/空/极端值
第三层:事务完整性 更新类操作的 DB 一致性 DB10n Consistency Check

3.2 第一层:结果对比自动化

手工对比适用于小数据量。大数据量建议写 ABAP 对比程序,核心逻辑:

abap 复制代码
" 核心:主键 BINARY SEARCH + 逐字段值对比 + 容忍浮点误差
SORT lt_old_data BY key_field.
SORT lt_new_data BY key_field.

LOOP AT lt_old_data INTO DATA(ls_old).
  READ TABLE lt_new_data INTO DATA(ls_new)
    WITH KEY key_field = ls_old-key_field BINARY SEARCH.
  IF sy-subrc = 0.
    " 逐字段对比
    IF ls_old-field_01 <> ls_new-field_01 OR
       ABS( ls_old-field_04 - ls_new-field_04 ) > 0.01.
      APPEND VALUE #( key_field = ls_old-key_field ... ) TO lt_diff_report.
    ENDIF.
  ELSE.
    " NEW 里没有这行 → 丢数据了!
    APPEND VALUE #( key_field = ls_old-key_field
                    field_name = 'ROW_MISSING_IN_NEW' ) TO lt_diff_report.
  ENDIF.
ENDLOOP.

3.3 第二层:功能测试用例设计(等价类划分)

以 ZORDER_REPORT 为例:

Case 输入条件 预期结果 覆盖的逻辑分支
TC01 正常日期范围(3个月) 8152 行正常输出 正常路径 + FATE 批量查询
TC02 🔑 未来日期(没有订单) 无数据输出(空结果) FATE 空表保护分支
TC03 只有 1 天范围 少量数据正常输出 极端小数据集
TC04 超大范围(1 年) 正常输出但运行时间在合理范围 压力测试
TC05 订单类型 Filter 只输出特定类型的订单 IN s_auart 的 WHERE 条件
TC06 选了不存在的订单号 空结果 WHERE 未命中 → 结果为空

💡 最关键的是 TC02(FATE 空表保护)------案例一的 Bug 就是没测这个 Case。

3.4 第三层:事务完整性测试

如果优化的是更新类程序(BAPI 批量写、BDC、财务过账等),必须验证 DB 数据完整性:

优化类型 需要检查的完整性项
BAPI 批量写入 DB10n Consistency Check;BKPF/BSEG 凭证头段能否对上;余额总计能否 RECONCILE
主数据维护 改 SELECT 字段会不会导致 UPDATE 时清空不该清空的字段
数据同步 (RFC/IDoc) 发送方和接收方数据量能否对上;失败重发有无导致重复
Archive 归档 归档后能否正常查询;归档表和原表数据总量能否对上

四、边界场景与兼容性测试

4.1 并发场景测试

为什么重要:单用户跑 15 秒,50 并发最慢可能 2 分 45 秒------因为有锁等待。

并发测试关键指标

指标 正常范围 预警信号
平均响应时间 ≤ 单用户 × 1.5 > 单用户 × 3
响应时间标准差 (CV) < 30% > 60%(有长尾请求)
Enqueue 等待时间 < 总时间的 10% > 30%(锁竞争严重)
DB 连接等待 < 最大值的 70% > 90%(连接池不够)
Work Process 使用率 峰值 < 80% 持续 > 95%

4.2 锁场景测试

性能优化经常涉及锁策略调整。在并发测试期间同时打开 SM12 和 SM66 实时观察,重点关注:

  • SM12 → 有没有大量锁排队(Lock Entries > 100)
  • SM66 → 有没有进程卡在 Enqueue 步骤

4.3 特殊用户场景

特殊场景 原因 测试方法
权限特别多的用户 SAP_ALL 权限的 Auth Check 可能慢几倍 用 SAP_ALL 和普通用户各跑一遍对比
多语言用户 文本读取因语言回退变慢 用英文、日文等非默认语言跑一遍
参数 ID 特殊的用户 硬编码了参数值导致特殊用户走错分支 检查 SY-UNAME 相关的硬编码逻辑

4.4 配置差异检查

配置项 检查方法 影响
表缓冲设置 SE13 → Technical Settings → Buffering 直接影响 SELECT 性能
数据库统计信息 DB02 → Statistics 统计不准 → 优化器选错执行计划
Profile 参数 RZ11 检查 rsdb/max_blocking_factor 等 影响 FATE 的批量大小
数据量 ST05 Trace 里的 ROWS 栏位 索引选择、执行计划完全不同
索引一致性 SE14 → 比较不同系统的索引 漏索引导致全表扫描

五、次生问题排查 ------ 优化了 A,搞坏了 B?

5.1 次生问题类型一:数据不一致

典型场景:批量更新跳过了逐行检查。

abap 复制代码
" ❌ 优化前(每行都做检查 + 更新,慢但安全)
LOOP AT lt_items INTO ls_item.
  SELECT stock FROM zmaterial INTO lv_stock WHERE matnr = ls_item-matnr.
  IF lv_stock < ls_item-qty. CONTINUE. ENDIF.
  UPDATE zmaterial SET stock = stock - ls_item-qty.
ENDLOOP.

" ❌ 优化后(批量检查 + 批量更新 → Bug!)
SELECT matnr stock INTO TABLE lt_stock FROM zmaterial
  FOR ALL ENTRIES IN lt_items WHERE matnr = lt_items-matnr.
LOOP AT lt_items INTO ls_item.
  " 检查和更新之间,其他用户也在扣减库存 → 库存变负数!
  APPEND ls_item TO lt_batch_update.
ENDLOOP.

修复 :检查与更新之间加 SELECT ... FOR UPDATE,或使用乐观锁。

5.2 次生问题类型二:其他程序变慢了

你给 ZORDER_HEADER 加了索引,ZORDER_REPORT 从 60 秒降到 3 秒。但 ZBILLING_RUN 从 18 秒变成了 2 分 15 秒------因为 DB 优化器选错了索引。

排查方法

  1. STAD 找变慢程序的 Top SQL
  2. ST05 对比新旧执行计划的 Rows Read
  3. 确认后,在代码中用 %_HINTS INDEX 指定原有索引

5.3 次生问题类型三:隐藏字段缺失

abap 复制代码
" 把 SELECT * 改成指定字段后,调用方可能还在依赖被删掉的字段
ASSIGN ls_header TO ls_ref->* CASTING.
" 如果 ls_header 结构不完整,CASTING 会出错

检查方法:SE80 → Where-Used List 全局搜索;SAT Runtime Error 检查;SCI/SCIF 静态检查。

5.4 次生问题排查工具清单

工具 用途 操作要点
SE80 全局搜索被改动对象的使用方 输入表名/函数名 → Where-Used List
SCI/SCIF 静态代码检查 新建检查变体 → 全量扫描
SAT Optimization Mode 新旧代码并排对比 + Runtime Errors 勾选 Collect Runtime Errors
ST03N 系统级负载对比 对比优化前后同一天的时间分布
SM13/DB12 数据库日志 重点看 Deadlock 和 Rollback 条目
DB10n 一致性检查 定时跑,或在优化后立即手动跑

六、验证流程 Checklist ------ 一页纸搞定所有验证环节

6.1 完整版(20 步)

阶段 # 具体动作 输出物 通过标准
① 开发自测 1 STAT 新旧代码各跑 10 次 响应时间对比表 新 < 旧 × 0.7
2 ST05 新旧各 Trace 一次 Top SQL 对比表 DB 时间显著下降,索引走对
3 SAT Optimization Mode 并排对比 CPU/Memory/DB 三栏对比 所有关键指标下降或持平
4 SAT Runtime Error 检查 Error 列表 0 Error / 0 Warning
5 SCI 静态代码检查 SCI 结果 Critical=0, Warning≤3
② 功能回归 6 OLD vs NEW 结果对比 差异报告 差异记录数=0
7 等价类测试用例 测试用例执行表 100% Pass(空表 Case 必测!)
8 DB10n Consistency Check DB10n 报告 0 条不一致
9 单元测试(如有) Unit Test 报告 100% Pass
③ 边界场景 10 并发测试(50 后台作业同时跑) 响应时间分布表 CV<30%, Lock Wait<10%
11 锁冲突注入测试 锁等待时间记录 锁等待增加 < 原时间 50%
12 特殊用户测试 对比报告 性能差异 < 20%
13 配置差异检查 差异对照表 关键配置一致
④ 次生问题 14 SE80 Where-Used List 被影响程序清单 无关键业务程序被影响
15 ST03N 系统负载对比 系统负载图 整体 CPU/DB 无显著上升
16 SM13/DB12 死锁回滚检查 DB 日志摘要 无新增 Deadlock/Rollback
17 DB10n 数据一致性 一致性报告 0 条不一致
⑤ 上线前 18 Code Review CR 记录 至少 2 人 Approve
19 生产灰度发布 灰度观察报告 灰度期无异常
20 上线后 24 小时持续监控 监控记录 无新增告警

6.2 紧急修复简化版(红线不能碰)

复制代码
🚨 紧急修复最低验证标准(必做!):

✅ STAT 新旧对比 → 确认性能真的提升了
✅ OLD vs NEW 结果对比 → 至少选 5 组真实数据跑一遍
✅ SAT Runtime Error 检查 → 不能引入新的 Short Dump
✅ SE80 Where-Used → 确认没改到公共接口的调用方
✅ 并发测试 → 至少 10 个并发跑一遍
✅ Code Review → 至少 1 个人看过

❌ 可以临时跳过的(但之后必须补做):
  · SCI 静态检查
  · 完整等价类测试用例
  · 多语言/特殊用户测试
  · DB10n 完整一致性检查(改成只查受影响的几张表)

七、总结与预告

本篇总结:优化不是终点,验证才是

#mermaid-svg-W1XnFXw5SdhcYXAY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-W1XnFXw5SdhcYXAY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-W1XnFXw5SdhcYXAY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-W1XnFXw5SdhcYXAY .error-icon{fill:#552222;}#mermaid-svg-W1XnFXw5SdhcYXAY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-W1XnFXw5SdhcYXAY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-W1XnFXw5SdhcYXAY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-W1XnFXw5SdhcYXAY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-W1XnFXw5SdhcYXAY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-W1XnFXw5SdhcYXAY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-W1XnFXw5SdhcYXAY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-W1XnFXw5SdhcYXAY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-W1XnFXw5SdhcYXAY .marker.cross{stroke:#333333;}#mermaid-svg-W1XnFXw5SdhcYXAY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-W1XnFXw5SdhcYXAY p{margin:0;}#mermaid-svg-W1XnFXw5SdhcYXAY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-W1XnFXw5SdhcYXAY .cluster-label text{fill:#333;}#mermaid-svg-W1XnFXw5SdhcYXAY .cluster-label span{color:#333;}#mermaid-svg-W1XnFXw5SdhcYXAY .cluster-label span p{background-color:transparent;}#mermaid-svg-W1XnFXw5SdhcYXAY .label text,#mermaid-svg-W1XnFXw5SdhcYXAY span{fill:#333;color:#333;}#mermaid-svg-W1XnFXw5SdhcYXAY .node rect,#mermaid-svg-W1XnFXw5SdhcYXAY .node circle,#mermaid-svg-W1XnFXw5SdhcYXAY .node ellipse,#mermaid-svg-W1XnFXw5SdhcYXAY .node polygon,#mermaid-svg-W1XnFXw5SdhcYXAY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-W1XnFXw5SdhcYXAY .rough-node .label text,#mermaid-svg-W1XnFXw5SdhcYXAY .node .label text,#mermaid-svg-W1XnFXw5SdhcYXAY .image-shape .label,#mermaid-svg-W1XnFXw5SdhcYXAY .icon-shape .label{text-anchor:middle;}#mermaid-svg-W1XnFXw5SdhcYXAY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-W1XnFXw5SdhcYXAY .rough-node .label,#mermaid-svg-W1XnFXw5SdhcYXAY .node .label,#mermaid-svg-W1XnFXw5SdhcYXAY .image-shape .label,#mermaid-svg-W1XnFXw5SdhcYXAY .icon-shape .label{text-align:center;}#mermaid-svg-W1XnFXw5SdhcYXAY .node.clickable{cursor:pointer;}#mermaid-svg-W1XnFXw5SdhcYXAY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-W1XnFXw5SdhcYXAY .arrowheadPath{fill:#333333;}#mermaid-svg-W1XnFXw5SdhcYXAY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-W1XnFXw5SdhcYXAY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-W1XnFXw5SdhcYXAY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W1XnFXw5SdhcYXAY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-W1XnFXw5SdhcYXAY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W1XnFXw5SdhcYXAY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-W1XnFXw5SdhcYXAY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-W1XnFXw5SdhcYXAY .cluster text{fill:#333;}#mermaid-svg-W1XnFXw5SdhcYXAY .cluster span{color:#333;}#mermaid-svg-W1XnFXw5SdhcYXAY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-W1XnFXw5SdhcYXAY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-W1XnFXw5SdhcYXAY rect.text{fill:none;stroke-width:0;}#mermaid-svg-W1XnFXw5SdhcYXAY .icon-shape,#mermaid-svg-W1XnFXw5SdhcYXAY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W1XnFXw5SdhcYXAY .icon-shape p,#mermaid-svg-W1XnFXw5SdhcYXAY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-W1XnFXw5SdhcYXAY .icon-shape .label rect,#mermaid-svg-W1XnFXw5SdhcYXAY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W1XnFXw5SdhcYXAY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-W1XnFXw5SdhcYXAY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-W1XnFXw5SdhcYXAY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 识别问题
分析瓶颈
设计方案
编码实现
🔍 验证
上线
监控

没有验证的优化 = 盲飞。你以为解决了问题,实际上可能引入了新 Bug。验证环节投入产出比极高------花 1 天做验证,避免上线后花 3 天救火。

全系列回顾

到第九篇为止,ABAP 性能分析与优化系列核心技术篇 + 专项优化篇 + 实战篇全部完成:

复制代码
核心技术篇(第 1-2 篇)
  ├── 第一篇:运行时性能基础入门
  └── 第二篇:核心工具入门

专项优化篇(第 3-6 篇)
  ├── 第三篇:数据库访问性能优化
  ├── 第四篇:内存优化
  ├── 第五篇:业务逻辑层优化
  └── 第六篇:接口与报表专项优化

实战与运维篇(第 7-9 篇)
  ├── 第七篇:全流程排查实战
  ├── 第八篇:生产系统监控与预警
  └── 第九篇:效果验证与回归测试 ← 本篇

下一篇预告:《ABAP 性能优化常见误区与开发规范:10 类典型优化错误与前置规避方案》------本系列最后一篇,从源头减少 80% 的性能问题。

作者 :爱喝水的鱼丶

版本记录:2026 年 8 月

💬 你在性能优化验证中踩过哪些坑?有没有"测试环境好好的,一上线就崩"的经历?欢迎分享你的验证故事。

相关推荐
Henry-SAP2 小时前
SAP MRP类型如何影响计划订单生成
人工智能·云原生·sap·erp
starzhang3 小时前
06 从 Trace 看 Android 启动流程
性能优化
数据库小学妹6 小时前
空间数据库查询慢怎么排查?索引失效、表膨胀、SQL优化实战(附排查命令)
数据库·性能优化·信创·故障排查·索引调优·空间数据库
爱喝水的鱼丶16 小时前
SAP-ABAP:接口与报表场景专项优化:RFC/ODATA 接口、ALV 报表的性能提升方案
运维·性能优化·接口·sap·abap·rfc·经验交流
这个DBA有点耶1 天前
数据库管理系统选型:从关系型到NewSQL的架构演进与决策框架
数据库·mysql·云原生·性能优化·架构·dba
灯澜忆梦1 天前
【MySQL11】进阶篇 | 索引_#3使用规则
数据库·sql·mysql·性能优化
starzhang1 天前
05 从 Perfetto 看懂 Binder 调用链
性能优化
大龄秃头程序员1 天前
一次关于 LRUCache 的工程化落地:从数据结构到 Feed 图片缓存实践
性能优化
jeffwang1 天前
我把同一个压测做错了三次,第四次才发现真正的瓶颈
分布式·性能优化·rust