SAP基础数据校验工具开发系列博客(共5篇)
第五篇:性能优化与上线运维:保障高并发场景下的工具稳定运行
当校验规则越来越多、主数据量突破十万级、用户并发请求激增时,你的校验工具还能撑得住吗?单次批量校验从几分钟变成几小时,实时校验导致业务操作卡顿,甚至系统资源耗尽......这些都是性能瓶颈的真实写照。本文在前一篇优化基础上,进一步深入分享十万级以上批量数据校验 的性能优化实战技巧,包括并行处理、缓存设计、数据库访问优化 等核心手段,同时系统介绍上线后的监控告警体系、日志排查方案 以及常见运维问题的快速处理方法,确保工具在高并发、大数据量场景下依然稳定、高效运行。
一、性能优化的整体思路
性能优化不能"头痛医头",而应从数据流的角度系统分析。校验工具的性能瓶颈通常出现在以下三个层次:
| 层次 | 典型问题 | 优化方向 |
|---|---|---|
| 数据获取层 | 循环内逐条SELECT、未使用批量读取 | 批量查询、哈希表缓存 |
| 规则执行层 | 每条规则独立查表、重复计算 | 规则预编译、结果复用 |
| 并发调度层 | 单线程处理、资源闲置 | 并行处理、异步任务 |
本文将从这三个层次逐一展开。
二、数据获取层优化:从"逐条查"到"批量读"
2.1 核心原则:能用一次SELECT解决的,绝不使用N次
以物料校验为例,原始代码往往类似:
abap
LOOP AT lt_materials INTO ls_mat.
SELECT SINGLE mtart FROM mara INTO lv_mtart WHERE matnr = ls_mat-matnr. " N次
SELECT SINGLE maktx FROM makt INTO lv_maktx WHERE matnr = ls_mat-matnr AND spras = sy-langu. " N次
" 其他关联表...
ENDLOOP.
优化后:
abap
" 一次读取所有物料基础信息
SELECT matnr mtart FROM mara INTO TABLE lt_mara FOR ALL ENTRIES IN lt_materials
WHERE matnr = lt_materials-matnr.
" 一次读取所有物料描述
SELECT matnr maktx FROM makt INTO TABLE lt_makt FOR ALL ENTRIES IN lt_materials
WHERE matnr = lt_materials-matnr AND spras = sy-langu.
" 转换为哈希表
DATA: lt_mara_hash TYPE HASHED TABLE OF ty_mara WITH UNIQUE KEY matnr,
lt_makt_hash LIKE lt_mara_hash.
lt_mara_hash = lt_mara.
lt_makt_hash = lt_makt.
" 循环中使用 READ TABLE WITH TABLE KEY
LOOP AT lt_materials INTO ls_mat.
READ TABLE lt_mara_hash INTO ls_mara WITH TABLE KEY matnr = ls_mat-matnr.
READ TABLE lt_makt_hash INTO ls_makt WITH TABLE KEY matnr = ls_mat-matnr.
...
ENDLOOP.
效果:查询次数从 2×N 降到 2,性能提升与N成正比。
2.2 FOR ALL ENTRIES 的使用注意事项
- 去重 :如果
lt_materials中有大量重复物料号,应先用SORT和DELETE ADJACENT去重,避免FOR ALL ENTRIES生成冗余的OR条件。 - 空表检查 :如果
lt_materials为空,FOR ALL ENTRIES会被忽略,导致全表扫描。因此使用前必须判断:
abap
IF lt_materials IS NOT INITIAL.
SELECT ... FOR ALL ENTRIES IN lt_materials ...
ENDIF.
- 性能诊断 :使用事务码
ST05查看生成的SQL语句,确认是否合理使用了索引。
2.3 数据库索引设计
即使使用了批量查询,如果查询条件没有索引,数据库仍会全表扫描。对于自定义的校验表(如ZMD_CHECK_RESULT),建议创建以下索引:
| 索引字段 | 用途 |
|---|---|
OBJECT_KEY + RULE_ID |
快速查询某个物料的特定规则错误 |
STATUS + CREATE_DATE |
统计待处理问题、清理旧数据 |
RESPONSIBLE + STATUS |
按责任人查看待办清单 |
对于SAP标准表,尽量利用其已有索引。例如MARC的主键是MANDT+MATNR+WERKS,查询MATNR时走索引;但只查MATNR而不指定WERKS,优化器可能选择全扫描。这时可以考虑创建自定义索引(SE11)仅包含MATNR,但需谨慎评估对插入性能的影响。
三、规则执行层优化:预编译与结果复用
3.1 规则表达式预编译
如果规则使用字符串表达式(如MATNR LENGTH EQ 18),每次校验都要解析字符串,成本较高。可以在程序启动时将规则加载到内存,并预编译为可执行的形式(如生成函数指针或存储解析后的语法树)。
简化方案:将规则类型(字段校验、存在性检查)与参数分开存储,减少运行时解析。
abap
" 规则表结构优化
FIELD_NAME TYPE fieldname, " 字段名
OPERATOR TYPE char10, " EQ, NE, LENGTH, REGEX
EXPECT_VALUE TYPE string, " 期望值
这样,执行时直接使用字段名和操作符,无需解析表达式字符串。
3.2 批量结果缓存
对于同一主数据在同一批次中多次校验(例如多个规则都需查询同一张关联表),可以将中间结果存储在共享内存中。但要注意缓存失效策略。
一个简单的实现:在程序级别使用静态内表缓存已查询过的对象。
abap
CLASS lcl_cache DEFINITION.
PUBLIC SECTION.
CLASS-DATA: BEGIN OF cache_mara,
matnr TYPE matnr,
mtart TYPE mtart,
END OF cache_mara,
mt_cache TYPE HASHED TABLE OF cache_mara WITH UNIQUE KEY matnr.
CLASS-METHODS: get_mtart IMPORTING iv_matnr TYPE matnr
RETURNING VALUE(rv_mtart) TYPE mtart.
ENDCLASS.
METHOD get_mtart.
READ TABLE mt_cache INTO DATA(ls_cache) WITH TABLE KEY matnr = iv_matnr.
IF sy-subrc = 0.
rv_mtart = ls_cache-mtart.
RETURN.
ENDIF.
SELECT SINGLE mtart FROM mara INTO rv_mtart WHERE matnr = iv_matnr.
ls_cache-matnr = iv_matnr.
ls_cache-mtart = rv_mtart.
INSERT ls_cache INTO TABLE mt_cache.
ENDMETHOD.
注意:静态缓存在整个程序生命周期内有效,对于单次批量校验是安全的;但对于常驻服务(如RFC多次调用),需设计缓存超时或手动清理。
四、并发调度层优化:并行处理
4.1 何时使用并行处理?
- 数据量超过5万行。
- 单个物料校验耗时较短(毫秒级),但总行数很大,CPU成为瓶颈。
- 服务器拥有多个应用服务器实例或多个CPU核心。
4.2 SAP并行处理实现方式
方式一:使用异步RFC(STARTING NEW TASK)
将物料表按范围分割,每个工作进程处理一个子集。
abap
DATA: lt_tasks TYPE TABLE OF char8,
lt_results TYPE TABLE OF ty_result.
" 分割物料表
DATA(lv_chunk_size) = 5000.
DATA(lv_chunks) = ceil( lines( lt_materials ) / lv_chunk_size ).
DO lv_chunks TIMES.
DATA(lv_offset) = ( sy-index - 1 ) * lv_chunk_size + 1.
DATA(lv_end) = sy-index * lv_chunk_size.
DATA(lt_chunk) = VALUE #( FOR i = lv_offset TO lv_end ( lt_materials[ i ] ) WHERE ( table_line IS NOT INITIAL ) ).
DATA(lv_task_name) = |TASK_{ sy-index }|.
CALL FUNCTION 'ZMD_CHECK_CHUNK' STARTING NEW TASK lv_task_name
EXPORTING
it_materials = lt_chunk
IMPORTING
et_results = DATA(lt_chunk_results)
EXCEPTIONS
communication_failure = 1 MESSAGE lv_msg
system_failure = 2 MESSAGE lv_msg.
APPEND lv_task_name TO lt_tasks.
ENDDO.
" 等待所有任务完成并收集结果
LOOP AT lt_tasks INTO lv_task_name.
WAIT UNTIL function 'ZMD_CHECK_CHUNK' ON TASK lv_task_name IS FINISHED.
RECEIVE RESULTS FROM FUNCTION 'ZMD_CHECK_CHUNK'
IMPORTING et_results = lt_chunk_results
EXCEPTIONS ...
APPEND LINES OF lt_chunk_results TO lt_results.
ENDLOOP.
方式二:使用SPBT框架(老式并行处理)
事务码SPBT定义服务器组,调用SPBT_INITIALIZE和SPBT_DISPATCH。配置较复杂,但稳定性高,适合超大批量。
4.3 并行度设置原则
- 并行进程数不超过系统可用对话进程数的70%。
- 对数据库敏感的规则(如大量
FOR ALL ENTRIES查询),并行度不宜过高,否则可能锁表或耗尽数据库连接。 - 测试调优:从2开始逐步增加,观察系统负载和完成时间。
五、监控告警体系
5.1 关键性能指标(KPI)采集
在ZMD_CHECK_LOG表中增加以下字段:
| 字段 | 说明 |
|---|---|
SELECT_TIME |
数据读取耗时(微秒) |
RULE_EVAL_TIME |
规则执行耗时 |
PARALLEL_DEGREE |
使用的并行进程数 |
MEMORY_USAGE |
内表占用内存(估计值) |
这些数据可用于性能趋势分析和容量规划。
5.2 实时告警实现
使用SAP标准事务码ALM或自定义后台作业,监控以下阈值:
| 监控项 | 阈值 | 动作 |
|---|---|---|
| 单次批量校验耗时 > 30分钟 | 超时 | 发送邮件给运维,同时作业自动中断 |
| 错误率 > 20% | 比率异常 | 暂停后续校验,通知数据治理团队 |
| 实时校验响应时间 > 2秒 | 慢查询 | 记录慢SQL,触发性能分析 |
邮件告警示例:
abap
DATA: lv_subject TYPE so_obj_des,
lv_text TYPE string.
lv_subject = '【SAP校验工具告警】批量校验超时'.
lv_text = |批次{ lv_run_id } 执行超过30分钟,请检查系统负载和规则配置|.
PERFORM send_mail USING lv_subject lv_text.
六、日志排查方案
6.1 分级日志记录
为便于排查问题,在关键代码点插入日志:
- INFO:批次开始/结束、分块数量、并行度。
- DEBUG:每条规则的匹配过程、临时变量值(仅开发环境)。
- ERROR:数据库错误、RFC通信失败、规则表达式解析异常。
日志表ZMD_SYS_LOG结构:
| 字段 | 类型 | 说明 |
|---|---|---|
| TIMESTAMP | DEC15 | 时间戳(精确到微秒) |
| LEVEL | CHAR1 | I/D/E |
| RUN_ID | CHAR20 | 关联的校验批次(如有) |
| TASK_NAME | CHAR8 | 并行任务名 |
| MSG_TEXT | STRING | 日志内容 |
6.2 错误追踪ID
对于实时校验(RFC/OData),客户端传入一个唯一TRACE_ID(如UUID),工具在处理过程中将此ID写入所有日志和结果记录。用户报错时提供此ID,运维人员可快速过滤出相关日志。
abap
METHOD zif_check~check_material.
DATA(lv_trace_id) = iv_trace_id.
WRITE: / 'Trace ID:', lv_trace_id. " 或写入日志表
ENDMETHOD.
6.3 慢查询定位
使用事务码ST05激活SQL跟踪,执行一次慢速校验,停止跟踪后分析耗时最长的SQL语句。常见优化点:
- 缺少索引 → 创建索引。
FOR ALL ENTRIES驱动表过大 → 先对驱动表去重、减少行数。- 使用了
SELECT *→ 改为只选需要的字段。
七、常见运维问题与解决方案速查表
| 问题现象 | 可能原因 | 排查步骤 | 解决方案 |
|---|---|---|---|
| 批量校验长时间未结束 | 某条规则全表扫描,或并行度太高导致锁等待 | 查看ST05跟踪,检查锁表情况SM12 |
优化慢查询;降低并行度;将大表拆分为多个小批次 |
| 实时校验导致MM01卡死 | 校验代码中有COMMIT WORK或WAIT |
检查校验函数中是否有数据库提交 | 移除COMMIT;将非必要校验移至后台 |
内存不足(MEMORY_NO_MORE) |
一次性加载了过多数据到内表 | 使用AL21查看程序内存占用 |
改为分块处理,或增大abap/heap_area_total参数 |
| 规则修改后不生效 | 未清除全局缓存或程序仍在运行旧代码 | 检查规则版本表中IS_ACTIVE标志 |
重新激活程序;重启批量作业 |
| 结果表中重复记录 | 同一批次被多次执行 | 查看后台作业调度是否重复 | 增加幂等性检查:按RUN_ID+OBJECT_KEY+RULE_ID唯一约束 |
八、上线前最后检查清单(补充篇)
- 在测试环境中使用生产数据量的副本执行压力测试。
- 配置后台作业自动清理3个月前的历史日志和结果表。
- 为关键事务码(如结果报表、规则维护)分配权限角色,避免误操作。
- 准备一份应急预案:当校验工具出现严重故障时,如何临时绕过校验(如修改增强开关变量)。
- 与BASIS团队确认系统资源:对话进程数、内存上限、数据库连接数。
九、总结
性能优化不是一蹴而就的,而是贯穿开发、测试、运维全周期的持续改进过程。本文总结的三大层次优化手段------批量读取、缓存复用、并行处理------可以帮助校验工具承受十万级甚至百万级的数据压力。同时,完善的监控告警和日志排查方案,能够让你在问题发生的第一时间发现并定位根因。
记住一条黄金法则:先让代码正确运行,再让它快速运行,最后让它稳定运行。希望本系列的五篇文章能帮助你在SAP主数据校验工具的建设中少走弯路,构建出高效、可靠的数据质量底座。
💬 你在批量校验中遇到过最离奇的性能问题是什么?最后是如何解决的?欢迎留言讨论。
作者 :你的SAP学习伙伴
版本记录:2026年6月