做过 SAP CRM 中间件项目的人,恐怕都见过下面这一幕:SMQ2 里入站队列越积越深,业务同事盯着我说“订单同步怎么又断了”,后台翻了一圈,任务既没报错也没完成,时间戳停在半小时前。最难受的还不是要背锅,而是根本说不清卡点在哪——数据库慢、锁等待、还是外部接口拖了后腿。后来我把这套办法固化下来:只要入站队列再出症状,就用 SAT 对 qRFC 入站任务做一次性能体检,半小时内给出耗时分布和嫌疑结论。这套方法不需要额外装工具,全部基于 SAP 自带能力,你也能直接复制到自己的项目里。
这不是什么高深算法,核心就一句话:把“队列里任务为什么慢”从猜测变成测量。SAT(事务代码 SAT,新版 SE30)能把入站任务执行过程中的 SQL 耗时、锁等待、RFC 外部调用、ABAP 调用堆栈全部记录成一份追踪文件,我再按时间占比往下一层层拆,很快就能锁定瓶颈。这篇文章我把完整思路、实操步骤、高频坑全部写出来,适合 BASIS、ABAP 性能顾问、CRM 中间件运维同学参考,普通开发也能看懂前半部分的原理。
1. 项目背景与整体设计思路
1.1 入站队列堆积背后的三种典型症状
SAP CRM Middleware 的入站消息走 qRFC 队列进来,处理完了任务状态变 DONE,出错了变 ERR,顺序被锁卡住会变成 STOP。我在项目上见到的队列堆积问题,表象通常能归成三类。
第一类是队列深度只增不减,LUW 状态长期停在 START 或者 READY,看起来像“卡死”,但系统没有崩溃。第二类是同步结果一直显示“处理中”,既不失败也不成功,业务侧只能干等。第三类最坑,单看每一条消息处理得都不算慢,平均时间也正常,但整体吞吐就是上不去,队列越积越多,说明存在个别“头部慢任务”拖住了后面的所有消息。
这三种症状的共同点,是问题都具有随机性、偶发性,不盯现场很难抓到。我最早的处理方式很原始——卡住就重启队列,重启之后再观察,但下次卡住照样不知道原因。后来想明白了一个道理:队列只是入口,真正要查的是队列里那个任务执行时做了什么。所以体检的目标就变成了抓住一个具体任务的完整执行路径,而不是反复看队列表面的状态。
1.2 为什么选 SAT 而不是其他工具
围绕 qRFC 性能分析,SAP 生态里有好几把工具,我全部试过一遍之后,固定选 SAT 作为主力。原因很简单:SAT 的追踪粒度正好落在“单任务执行过程”这个层级上。
ST05 能做 SQL 跟踪,但对于入站队列问题有个硬伤——它只记录数据库访问,看不到函数调用链和外部 RFC 调用。SM50 能看实时进程,但那是瞬间快照,任务跑完了就什么都没了,偶发问题根本守不住。SMQ2 只能说明队列当前是什么状态,解答不了“为什么”。静态代码检查工具看的是代码规范问题,入站队列慢往往是运行时数据量、锁冲突、外部依赖造成的,静态分析根本发现不了。
SAT 恰好补齐了这些缺口。它能把一个 ABAP 执行路径里所有 SQL 语句、RFC 调用、函数调用层级、数据库锁事件全部按时间记录下来,并且官方自带统计分析视图,不需要自己去解析底层跟踪文件。相当于给一次入站处理装了个行车记录仪,事后可以一帧一帧回放。
1.3 体检整体思路:先锁定卡点,再对症下药
我给入站队列做性能体检的整体设计分三步走。第一步,在 SMQ2 里做队列状态快照,记录队列深度、卡住任务的时间戳,确定目标和观察窗口。第二步,用 SAT 抓住一个代表性任务,生成追踪文件。第三步,把追踪文件里的耗时按 SQL、锁等待、外部 RFC、CPU 执行拆出占比,找出占比最高的嫌疑项,再针对性优化。
这个流程一开始可能觉得繁琐,但跑过两三次之后效率会很高。我后来在项目里甚至把它做成了半小时内的标准动作:看队列、抓追踪、拆耗时、出结论。整条链路里最关键也是最容易失败的环节,是第二步“用 SAT 抓住目标任务”,这也是我下面要花大篇幅展开的部分。
2. qRFC 入站队列核心机制与卡点模型
2.1 qRFC 协议与“排队处理”的核心设计
想定位性能问题,先要知道入站队列的设计逻辑。普通 RFC 是发出去就不管结果,事务性 RFC 能保证要么执行成功要么回滚,但这两者都不保证顺序。qRFC 全称是 queued Remote Function Call,它在 RFC 外面包了一层队列,保证同一个队列里的消息严格按照入队顺序被处理。
我用打饭窗口来类比:普通 RFC 相当于每个人自己挤到窗口点餐,谁先到谁先吃;qRFC 相当于先取号,号码必须从小到大叫,1 号没处理完,2 号 3 号就算到了也只能坐在旁边等。这个设计对 CRM Middleware 很重要,因为入站消息往往存在前后依赖,比如同一张订单的修改和确认不能交换顺序。但代价也在这里——队列头部一个任务被卡住,后面所有任务全部陪跑。
在 SMQ2 里能看到一个队列下面挂着多个 LUW(Logical Unit of Work,逻辑工作单元),每个 LUW 代表一条待处理的入站业务单元。LUW 常见状态包括 READY(等待调度)、START(正在处理)、DONE(处理完成)、ERR(处理失败)、STOP(因前序任务未释放或锁等待而暂停)。了解这几个状态,是后面读快照的基础。
2.2 入站处理链路与性能指标定义
CRM Middleware 入站消息从到达队列到最终落库,中间会经过一条比较长的链路。大致可以拆成四个环节:
- 外部系统发起 qRFC 调用,消息写入入站队列(SMQ2 可见)。
- 调度机制按顺序取出一个 LUW,交给后台工作进程执行。
- 中间件适配器把入站消息转换成 BDOC 格式,做字段映射和业务校验,再交给 CRM 业务对象处理函数。
- 业务函数执行数据库读写,成功则把 LUW 状态更新为 DONE,失败进入 ERR,遇到前序未完成则进入 STOP。
针对这条链路,性能指标就很好定义了。一个任务的总耗时等于“队列等待时间”加上“处理时间”,其中处理时间又可以拆成四块:SQL 执行时间、CPU 计算时间、锁等待时间、外部 RFC 调用时间。用公式写出来更直观:
任务总耗时 = 队列等待时间 + 处理时间 处理时间 ≈ 数据库 SQL 时间 + CPU 执行时间 + 锁等待时间 + 外部 RFC 时间这个拆解模型是整个体检方法的核心。SAT 追踪文件里恰好能把这四块时间分别统计出来,所以我只需要把追踪结果对应的数字填到这个公式里,瓶颈在哪立刻有结论。
2.3 为什么一个慢 LUW 会拖垮整条队列
队列串行处理的特性决定了,一个慢任务的影响不是“自己慢”这么简单。LUW 1 处理过程中持有资源不释放,LUW 2 到 LUW N 即使完全没问题,也只能一起排队。
我见过一个很典型的例子:队列里有 200 个任务,前面有一条例行消息因为调用的外部系统响应超时,单个任务跑了 15 秒,结果后面 199 条全部堆积。业务看到的表象是“同步彻底断了”,但实际系统没坏,只是头部任务卡住了。这也是为什么我做性能体检时,从来不重点看平均耗时,而是优先找队列头部那几个异常耗时特别高的任务——它们是整条队列的咽喉。
理解了这一层,就不难明白为什么 SMQ2 里的队列深度数字本身没有意义,更有意义的是“最后一个 DONE 任务的时间戳”和“头部卡住任务的持续时长”。这两个指标才是体检前必须记下来的基线数据。
3. 用 SAT 对入站队列做性能追踪的准备工作
3.1 SAT 工具界面与追踪类型选择
SAT 的主界面看着字段多,实际上需要关心的就三块:追踪类型、追踪变体、追踪目标。追踪类型我强烈建议根据入站任务的运行场景来选,选错了直接导致抓不到数据。
SAT 常见的追踪类型有单事务路径、调用程序、后台任务、更新任务这几类。单事务路径适合自己在当前会话里手动执行一个函数,并不适合队列这种由后台调度触发的场景。入站队列的 LUW 是后台工作进程在执行,所以最合适的追踪类型是“后台任务”或者“调用程序”。
| SAT 追踪类型 | 适用场景 | 入站队列适用性 |
|---|---|---|
| 单事务路径 | 手动在当前会话执行功能 | 不适用,队列任务不自带 UI 会话 |
| 后台任务 | 按作业名捕获后台作业执行过程 | 适用,入站队列处理通常由后台作业驱动 |
| 调用程序 | 按程序名捕获指定程序执行 | 适用,可从 SM50 获取正在处理的程序名 |
| 更新任务 | 捕获 V1/V2 更新模块 | 慎用,仅当卡点在更新模块时 |
不同 SAP 版本的按钮名称可能略有差异,但追踪类型的逻辑是通用的。对于入站队列,我最常用的组合是“后台任务 + 作业名过滤”,或者“调用程序 + 当前执行工作进程的程序名过滤”,两者能覆盖绝大多数场景。
3.2 追踪变体配置的取舍:少即是多
追踪变体决定了 SAT 到底记录哪些事件。这里有一个大多数人踩过的坑:什么都勾选,结果生成的追踪文件几十 GB,打开都要半小时,甚至直接把应用服务器磁盘写满。
我在配置变体时,默认只勾选四类内容:SQL 语句(含绑定变量)、RFC 调用、ABAP 调用堆栈、数据库锁请求。这四样已经能完整覆盖性能拆解公式里需要的所有数据。内部表操作、所有变量值快照、SAP 框架内部调用这类细节,除非定位到具体那一步需要深挖,否则一律不勾。
追踪文件的大小限制也要提前设置。我一般设置在 1GB 到 2GB 之间,够抓一个入站任务完整执行,又不会造成管理负担。实际的追踪持续时间取决于任务本身,一次入站处理通常在几百毫秒到几十秒之间,不会太久,文件大小基本可控。
3.3 三种触发追踪的方式与选择
准备工作里最关键的,是怎么让 SAT 在正确的时间点记录到正确的任务。总不能打开追踪然后傻等,入站队列里消息那么多,恰好抓到问题任务需要一点技巧。我总结出三种触发方式,按推荐程度排列。
第一种,测试或沙箱系统重放。从 SMQ2 双击问题 LUW 拿到入站消息对应的业务对象 ID,在沙箱系统里用相同参数重新推送一条消息,然后用“单事务路径”或者“后台任务”追踪这次执行。这种方式最安全,适合复现类问题,也是我遇到偶发慢任务时的首选。
第二种,生产系统后台任务追踪。先确认负责入站处理的作业名,在 SAT 里选“后台任务”,输入作业名并启动追踪,等待该作业下一次运行时自动捕获。但这里有个现实问题:入站处理作业可能是周期性的,追踪窗口内不一定有任务,所以我会配合 SMQ2 观察,在队列明显开始堆积时立刻启动追踪。
第三种,按程序名即时捕获。如果在 SM50 里已经能看到某个工作进程正在处理入站消息,立即记下该进程正在运行的程序名,然后在 SAT 里选“调用程序”,输入这个程序名,该程序下一次被调用时就会自动触发追踪。这个方式最“稳、准、狠”,但需要现场反应够快,适合已经在处理中的卡死任务。
| 触发方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 沙箱重放 | 零生产风险、可反复试验 | 需要测试环境、需构造入站消息 | 偶发慢任务复现 |
| 后台任务追踪 | 不影响现有作业、自动化捕获 | 需等待作业运行窗口 | 周期性堆积问题 |
| 程序名即时捕获 | 当场抓现场、不依赖作业调度 | 需要人工盯 SM50 | 卡死任务、现场分析 |
生产系统上启动 SAT 追踪属于性能影响操作,多多少少会有额外开销。我执行前会先跟项目组确认窗口期,尽量选在业务低谷,而且追踪一个任务在几十秒内就结束,影响有限。这三点准备做到位,追踪成功率会非常高。
4. 入站队列性能体检完整实操步骤
4.1 第一步:SMQ2 队列状态快照
体检开始前,先给队列当前状态拍一张“快照”。进入 SMQ2,切换入站队列视图,重点记录这几个字段:队列名称、队列深度、LUW 状态、任务开始时间。
队列名称看起来乱,但里面是有规律的。CRM Middleware 的入站队列命名往往带接收方或 INBOUND 相关前缀,具体规则随版本和项目配置变化,不用死记,只要你会筛——把那些深度明显大于 0、或者存在非 DONE 状态的队列挑出来即可。我开始做性能体检时容易在这上面耗时间,后来学聪明了,直接按“LUW 状态不是 READY/DONE”反向筛选,几秒钟就能找到问题队列。
还需要记下“最后一个 DONE 任务的时间戳”。这个时间戳越久远,说明队列堵得越严重。比如当前是下午三点,最后一个成功任务停留在上午十一点,中间那四个小时的所有消息都在排队,这个数字直接决定了问题等级。
4.2 第二步:锁定追踪目标对象
快照做好之后,要从队列里找到那个“值得追踪”的任务。我的选择标准很简单:优先选正在 START 状态且持续时间异常长的任务,如果没有,就选头部 READY 状态的第一个任务,因为它的阻塞直接影响后面所有任务。
双击这个 LUW,能看到它正在调用的函数模块、程序名、以及入站消息关联的业务对象 ID。这一串信息就是追踪目标。同时打开 SM50,找到正在运行入站处理程序的工作进程,记录下程序名和进程号,用于 SAT 的“调用程序”追踪。
这里有个很容易忽略的点:如果问题任务是偶发的,SMQ2 里不一定正好有卡住的任务,那就要回到 3.3 里的“沙箱重放”方案,先把业务对象 ID 保存好,到测试环境构造一次相同调用,在 SAT 里追踪那条路径。
4.3 第三步:配置并启动 SAT 追踪
目标确定后,进入事务代码 SAT。我的标准操作流程如下:
- 选择追踪类型:后台任务或调用程序,按第 4.2 步拿到的信息填。
- 选择追踪变体:勾选 SQL 语句、RFC 调用、ABAP 调用堆栈、数据库锁请求,设置文件大小上限。
- 点击“启动追踪”按钮,激活追踪。
- 如果目标是积压任务,此时到 SMQ2 里观察该任务是否很快开始处理;如果是重放任务,到沙箱里推送对应消息。
- 任务执行完毕后,回到 SAT 停止追踪。
追踪激活成功后,系统会在后台生成一条追踪记录。停止追踪后,到 SAT 的历史追踪列表里找到本次追踪文件,确认它确实捕获到了内容——检查点很简单,看文件的记录条数和时间跨度是不是和任务执行时间吻合。如果追踪文件记录时间为 0,说明启动追踪时机不对,没抓到东西,需要换一种触发方式重来。
4.4 第四步:追踪数据解读与耗时分布统计
追踪文件拿到手,最过瘾的环节来了。打开追踪分析视图,系统会把执行路径按层展开,每个节点都有总时间、数据库时间、CPU 时间、生成记录数等字段。我通常不看明细,直接按“数据库时间”排序,把 TOP 5 的节点拉出来,然后再按“总时间”排序,看整体耗时分布。
为了让数据更直观,我会把追踪结果手工整理成一张性能剖析表。以前有个项目,追踪结果显示一条入站消息总耗时 12.6 秒,整理之后长这样:
| 耗时类型 | 耗时(秒) | 占比 | 初步判断 |
|---|---|---|---|
| 数据库 SQL | 8.1 | 64% | 主瓶颈,存在慢查询或循环访问 |
| 锁等待 | 2.2 | 17% | 与其他任务存在锁冲突 |
| 外部 RFC 调用 | 1.4 | 11% | 外部系统响应偏慢 |
| 其他(CPU 等) | 0.9 | 8% | 正常范围 |
这个表一出来,问题基本就定性了。数据库占六成以上,接下来优化方向自然就聚焦到 SQL 层,而不是怀疑服务器配置或者网络。表格不用做得太复杂,能跟业务同事和领导解释清楚就行。
4.5 第五步:输出体检报告
追踪完成之后,我会把数据汇总成一份简短的体检报告。报告不需要写成正式文档那种冗长风格,就三部分内容:队列现状、耗时分布、优化建议。
队列现状写上 SMQ2 快照里的关键数字,比如阻塞队列数、头部 LUW 状态、最后成功时间。耗时分布就是 4.4 里的表格。优化建议则按嫌疑项的权重排序,而且我会明确区分“立即可做”和“需要排期”的优化项。这一步不是为了走流程,而是让后续动作有依据——下次再出现问题,把这份报告拿出来对比,就能知道优化是否真的有效。
5. 三种高频瓶颈定位与优化落地
5.1 数据库层成为主卡点:SQL 慢与循环访问
我梳理过记录,入站队列性能问题里,数据库层占比能排到第一位。SAT 追踪中这类问题的信号非常典型:总耗时里 SQL 时间占据明显大头,而且往往存在同一条 SQL 被反复执行几十上百次。
举个例子,中间件做入站映射时,会对业务主数据逐条做存在性检查,如果写成循环里逐条 SELECT,消息一多时间就指数上升。遇到这种情况,SAT 追踪结果里能清楚看到某条 SELECT 的执行次数和累计时间,顺着代码一找就能定位到具体程序。优化手段也很固定:能加索引的加索引,能改批量操作的改成 FOR ALL ENTRIES 或批量 BAPI,能减少数据量的先过滤再处理。
这里要提醒一句,生产加索引不是想加就加,关注大数据表加索引可能引发短暂的锁操作,所以优化方案里要把风险标注出来,请 DBA 在维护窗口执行。我见过有人直接在高峰期给大数据表加索引,结果操作期间把业务直接拖停,这种教训写下来就是要大家避坑。
5.2 锁等待与并发冲突:多个任务抢同一批数据
第二种高频瓶颈是锁等待。SA T 追踪中如果锁相关时间占比明显,或者 SM50 里看到多个工作进程互相等待,基本就是锁冲突。入站任务处理 CRM 单据时,经常同时更新同一张订单或者同一个数据范围,两个任务一撞车,后面那个就只能等锁释放。
定位锁等待,建议先到 SM12 里看当前锁条目,确认锁持有者是谁。有时候持有者根本不是入站队列任务本身,而是某个后台批量报表程序锁住了大表。优化方向有三个:错峰调度,把入站处理和批量任务的时间错开;减少单次锁持有时间,把大事务拆成小批次;通过调整队列并发参数,降低同时处理的并行度,避免自己人跟自己人抢锁。
有朋友可能觉得降低并发度会让处理更慢,但实际中我有过完全相反的体验——并发太高导致抢锁和死锁重试,整体时间反而更差,调低并发后吞吐反而稳定了。串行化不一定是坏事,关键是保证每个任务都能快速做完释放资源。
5.3 外部系统调用拖慢入站:中间件关键路径上的隐形依赖
第三种情况在 CRM 项目里尤其常见。中间件入站处理经常需要调用外部系统做校验,比如查物料价格、客户信用、库存状态,设计时大家都没太在意,结果外部系统性能一波动,入站队列立刻全线堵塞。
SAT 追踪中这种问题很好认:记录里出现一条 RFC 调用时间特别长,而且它的耗时集中在“等待对方响应”。我当时看到一个项目里,入站处理时间 15 秒,其中 12 秒都在等外部系统返回校验结果,内部逻辑其实只花了 3 秒。
针对外部系统依赖,优化思路要分几个层次:最直接的是给外部调用设置合理超时,避免无限等待;其次是加缓存,把短时间内重复调用的数据缓存住;再次是改架构,把同步校验改为异步通知或批处理预校验。如果外部系统响应已经不可控,那就要考虑把这类调用移出入站关键路径。这属于架构层面的调整,但 SAT 的追踪结果是最好的立项依据——一条 12 秒的 RFC 等待记录拍在桌上,谁都不会反对这个改造。
6. 常见问题与排查技巧实录
6.1 Q1:SAT 追踪打开了,但什么数据都没抓到
这个问题十有八九是追踪类型选错了。入站队列任务是后台进程执行的,你用“单事务路径”追踪,它压根不会经过当前会话,当然什么都记录不到。解决办法是先确认执行场景,再从“后台任务”和“调用程序”两种类型里选一个。
还有一种可能是过滤器写错。追踪类型选了后台任务,但作业名填写不正确,任务执行时就不匹配。建议在 SM37 里先查一下作业的实际名称,复制过来填,别手打。另外也不排除激活追踪的时机太晚,任务已经跑完了,这时要把追踪目标改成下一个即将处理的任务。
6.2 Q2:追踪文件大得打不开
说实话,我自己刚开始用 SAT 时也干过这种事,把能勾的选项全勾上,追踪文件直接十几个 GB。分析界面打开以后直接卡死,最后只能手动清理文件。
现在我的做法是初期只开四类必要事件,先拿到整体耗时分布,如果确认某一步需要深挖,再缩小范围补抓一次。另外文件大小上限一定要设置,宁可一次没抓完,也不要让服务器磁盘爆掉。万一已经生成了超大文件,清理时要注意按时间戳删除对应的物理文件,别把正在用的追踪记录误删了。
6.3 Q3:SMQ2 里 LUW 卡在 START 状态一直不结束
这个状态说明任务已经被调度进程拿走了,但迟迟没有完成。可能是真的在执行长事务,也可能是进程被锁死或者崩溃了。先用 SM50 看这个工作进程是否还活着,如果进程状态正常,就用 SAT 按程序名追踪一次,看它到底卡在哪一步。
如果追踪发现卡在锁等待,那就按照 5.2 的锁冲突排查流程走。如果进程已经不正常,先把工作进程复位,然后到 SMQ2 里对卡住的任务做重置。这里要特别提醒,不能随意删除卡住的 LUW,顺序错乱会把后面所有消息的状态搞乱,先搞清楚原因再动手。
6.4 Q4:队列里有大量 ERR 状态的旧任务
ERR 状态的任务不会自动重试,会一直躺在队列里,而且会影响后续消息的处理。我看到 ERR 的第一步永远是先去日志里看错误原因,区分是数据问题还是代码问题。
数据问题通常直接修正数据后重置任务;代码问题则要先打补丁或者修正程序,再对 ERR 任务做重置重跑。这里有个经验:遇到 ERR 堆积先批量提取错误消息分类,不要一个个点开看,用报表或者日志聚合工具一次性导出,按错误类型分组,处理效率高很多。
6.5 Q5:生产环境没有权限做 SAT 追踪,怎么办
有些客户对生产系统的追踪管控很严格,需要审批流程。这种时候不要死磕,先把 SMQ2 快照和 SM50 的进程状态记录下来,然后申请在测试或沙箱系统用同样的入站消息重放一次,照样能通过 SAT 拿到耗时分布。
关键是重放的消息参数要和现场一致。从一个项目学到的方法是把问题 LUW 对应的入站消息先导出保存,测试环境按这个原始消息重放,得到的结果才有参考价值。泛泛地“模拟一条消息”,很多时候复现不出生产环境的性能问题,白忙活。
6.6 一份小清单:做 qRFC 性能体检前核对这五件事
最后把日常最容易忽视的检查点列成一张速查,每次做体检前过一遍,能省不少返工时间。
| 检查项 | 说明 |
|---|---|
| 队列状态快照是否完整 | 队列深度、头部 LUW 状态、最后成功时间戳 |
| 追踪目标是否明确 | 作业名、程序名、业务对象 ID 必须至少有一项 |
| 追踪变体是否克制 | 只开 SQL、RFC、ABAP、锁,不贪多 |
| 触发方式是否匹配现场 | 优先沙箱重放,生产用后台任务或程序名捕获 |
| 文件大小上限是否设置 | 默认值可能要调大或调小,别让磁盘爆掉 |
我个人在实际项目里最大的体会是,qRFC 入站队列性能问题没有想象中那么玄。大部分问题翻来覆去就是数据库、锁、外部系统这三件事,SAT 存在的意义就是把“我怀疑是数据库慢”变成“追踪记录显示 SQL 耗时占 64%”。数据一摆出来,方案顺理成章,业务和技术之间的沟通成本也会低很多。
再分享一个小技巧:看追踪结果时,先盯单个任务的最大耗时,别急着看平均。队列是串行处理的,决定业务体感的是那个最慢的头部任务,不是所有人的平均值。性能体检的本质就是找 outlier,把队列里拖后腿的那条揪出来优化掉,整条队列的吞吐自然就上来了。这套方法我已经在多个项目的入站堆积问题上验证过,照着步骤走,你也能很快把同样的问题“按方抓药”解决掉。