☰
SAP单SQL语句导致HANA内存溢出:从告警到修复的完整排查实战
2026/9/30 2:31:29 网站建设 项目流程

SAP单SQL语句导致Hana内存溢出警告:从告警到定位再到修复的完整实战手记

上周值班时,SAP生产系统突然弹出HANA内存溢出警告,后台任务大面积挂起,财务用户登录都开始卡顿。打开HANA Studio一看,一个异常SQL把整个实例的内存几乎吃空。这种事在SAP+HANA的架构下并不少见,尤其是FICO、MM、PP这些模块跑月结、MRP、折旧批量作业的时候,一条写得不严谨的SQL就能让内存数据库直接"呛住"。本文想完整还原一次SAP单SQL语句导致Hana内存溢出警告的排查和修复过程,重点说清楚HANA侧、SAP侧分别怎么定位,执行计划怎么看,以及哪些参数能救急、哪些参数千万别乱动。适合ABAP开发、HANA DBA、SAP Basis以及做ERP运维的同行参考,新人看一遍也能知道遇到这个问题该从哪里下手。

1. 问题现象与排查层级判断

1.1 一次典型的HANA内存警告现场

当天上午十点多,监控平台报警,提示HANA实例内存使用率超过95%,紧接着SAP系统里大量RFC调用超时,业务端开始反馈:MIRO过账转圈、MIGO收货报错、FBL3N查询半天不出数据。登录HANA Studio后看到内存视图整体飘红,数据库服务所在的物理节点可用内存在十几分钟内从40%冲到告警线。

这类现象有个明显特征:内存曲线不是缓慢爬坡,而是断崖式暴涨。普通业务高峰期内存使用是逐步抬升的,而单条SQL导致的内存溢出往往是几分钟内就冲到顶,随后HANA开始拒绝新的内存分配请求,已运行的大查询也会被系统强制终止。如果此时你去看HANA的“事件”标签页,通常能找到一条类似“Out-of-memory”或“Allocation limit reached”的记录。

1.2 HANA内存超限的warning与critical有什么区别

HANA的内存管理走的是“先警告、后终止”的路线,理解这一点对判断故障等级很重要。

HANA在global.ini的memorymanager段里维护了一套分配阈值。默认情况下,数据库允许使用的总内存上限由global_allocation_limit控制,通常是物理内存扣掉系统保留后的一定比例。当你接近这条线时,HANA会生成内存分配警告,进入一个紧张状态,此时系统还能运行,但新的大查询可能被挂起或降速,表现出来就是业务变慢、偶发超时。如果再往上冲,突破了另一个更刚性的临界线,HANA就会直接选择终止最耗内存的会话,具体哪个会话倒霉,取决于系统内部的内存优先级评估,这时用户看到的可能就是“Connection terminated”或“Statement execution canceled”。

很多刚接触HANA的同行会把这两个阈值混为一谈,以为看到警告就要立刻加内存、改参数。实际上warning阶段的含义是“系统正在挣扎但还能撑住”,这个窗口恰恰是最宝贵的定位时间。我个人的习惯是:任何内存警告出现后,先别动配置,花十分钟找出占内存最大的SQL,绝大多数情况下问题在这条语句上,而不是在系统容量上。

1.3 第一步先分清是应用层内存还是数据库层内存

这里有一个容易踩坑的地方。SAP系统的内存问题并不一定是HANA数据库的内存问题,还可能出在应用服务器上。ABAP工作进程有自己独立的内存配额,当某个事务码或报表在应用层一次性捞取几十万行数据到内表时,堆内存暴涨,同样会让用户感觉“系统卡死”,但HANA侧内存曲线可能非常平缓。

所以收到“内存溢出警告”后,我的排查顺序是:

  • 先在操作系统层看是哪个进程占用内存飙升。如果是hdbnameserver、hdbindexserver等HANA进程吃满,基本就是数据库层问题;如果是sapstartsrv或disp+work占了大量内存,就要去ABAP层查程序。
  • 再进HANA Cockpit或Studio看实例内存概览,确认是单节点还是全实例耗尽。
  • 最后才去看是哪个SQL、哪个程序引发的。

这套顺序能帮助你快速选择排查工具。如果错误地拿着ABAP程序员视角去查报表代码,而问题根本不在应用层,会浪费大量时间。反过来也一样,数据库内存正常但ABAP工作进程OOM,你去HANA里面翻执行计划,基本一无所获。

2. 用HANA侧工具锁定“吃内存”的那条SQL

2.1 先翻M_EVENTS,确认告警的真实来源

接到内存报警时,第一步不是直接去看SQL,而是先确认HANA内部到底记录了什么事件。HANA的系统视图M_EVENTS保存了实例级的关键事件记录,包括OOM(Out-of-Memory)相关的条目。

可以执行如下SQL去查:

SELECT EVENT_TIME, EVENT_TYPE, DETAIL, USER_NAME, HOST, PORT FROM SYS.M_EVENTS WHERE EVENT_TIME > ADD_SECONDS(CURRENT_TIMESTAMP, -3600) ORDER BY EVENT_TIME DESC;

如果记录里出现事件类型为“OOM”或“Memory Allocation Failure”的条目,说明确实发生了内存分配失败,此时继续往下抓SQL才有意义。

顺便说一句,M_EVENTS的DETAIL字段经常包含被终止会话的详细信息,例如:

Statement memory limit of 209715200 bytes reached for statement ...

如果能看到“Statement memory limit”字样,说明系统已经配置了单语句内存上限,并且确实有语句撞墙了。如果你的HANA环境中这条事件里只有“global allocation limit”相关描述,说明没配单语句内存限制,全局内存被某个会话一步步拖垮,此时你面对的问题通常更棘手。

2.2 SQL Plan Cache里按内存占用排序,找出最贵的语句

HANA会缓存运行过的SQL执行计划,视图M_SQL_PLAN_CACHE就是排查高内存SQL的首选入口。

我常用的查询是这样的:

SELECT STATEMENT_HASH, STATEMENT_STRING, EXECUTIONS, MAX_MEMORY_SIZE, TOTAL_MEMORY_SIZE, TOTAL_DURATION, LAST_EXECUTION_TIMESTAMP FROM SYS.M_SQL_PLAN_CACHE WHERE EXECUTIONS > 0 ORDER BY MAX_MEMORY_SIZE DESC LIMIT 50;

重点看两个字段:

  • MAX_MEMORY_SIZE:某条语句单次执行消耗的最大内存,单位是KB。如果这个值达到几百GB甚至接近TB级,基本就是罪魁。
  • TOTAL_MEMORY_SIZE:这条语句累计消耗的内存总量。如果单次最大内存不大,但累计值很大,说明高频执行但单次开销不高的SQL也在汇聚内存压力,这种属于“温水煮青蛙”式问题。

还有一个实用技巧:用LAST_EXECUTION_TIMESTAMP和告警时间做交叉比对。如果一条SQL的最近执行时间正好落在内存告警发生的时间窗口内,并且MAX_MEMORY_SIZE甩开其他语句一个数量级,那几乎可以锁定它就是肇事者。

拿到SQL文本后,可以通过STATEMENT_HASH在SAP侧反查,也可以直接看文本内容判断属于哪个业务模块。有时候SQL文本带有注释片段,比如“select from MARA where ...”,基本能猜到是物料主数据相关。

2.3 开启Expensive Statement Trace盯梢,抓现场最直接的证据

如果内存告警还在持续,或者你希望在下一次告警发生时自动留下证据,就开启HANA的Expensive Statement Trace。

在HANA Studio中进入“Administration → Trace Configuration”,找到Expensive Statement Trace,设置一个合理的阈值。比如将执行时间超过10秒或内存超过1GB的语句都记下来。也可以直接用ALTER SYSTEM命令设置:

ALTER SYSTEM ALTER CONFIGURATION ('global.ini', 'SYSTEM') SET ('expensive_statement', 'threshold_execution_time') = '10000000'; ALTER SYSTEM ALTER CONFIGURATION ('global.ini', 'SYSTEM') SET ('expensive_statement', 'threshold_memory') = '1000000';

这里threshold_execution_time单位是微秒,threshold_memory单位是KB,设置完成后可以查询M_EXPENSIVE_STATEMENTS视图观察记录。

这个工具的价值在于,它是“提前布置的监控”,就像给系统装了一个行车记录仪。内存告警往往是突发的,当你接到报警再去手工查询时,那个肇事SQL可能已经被HANA终止了,执行计划缓存里也未必能留下痕迹。这时候Expensive Statement Trace里可能早就默默地记录了每一分钟的高耗语句。

3. 反查SAP端:从SQL文本到业务程序

3.1 用ST05把HANA语句映射回ABAP报表

拿到HANA侧的SQL后,问题并没有结束。SQL只是表征,你真正需要知道的是哪个程序、哪个事务码、哪个业务操作发起了这条语句。SAP的事务码ST05(SQL Trace)是连接ABAP端和HANA端的关键桥梁。

操作流程是:

  1. 让用户或后台Job重现问题操作。
  2. 在ST05中激活SQL Trace,选择跟踪类型,通常勾选“SQL”和“Buffer”即可。
  3. 问题场景结束后,停止跟踪。
  4. 在结果显示页面里查找对应的HANA SQL语句,双击条目后可以看到ABAP程序名、include名、行号等信息。

这里有个细节:SAP系统在HANA数据库上执行SQL时,Open SQL语句往往会被改写为大量列存储相关的底层SQL,你在ST05里可能看到一堆带“#”号或内部函数名的复杂语句,不仔细看会眼花。建议先按“操作耗时”或“记录数”排序,找最夸张的那几条,再逐条对应到ABAP语句上去。

ST05给出的程序名定位已经足够精确,但如果程序里SQL是动态拼接的,依然要回到ABAP代码中去查是哪个动态SQL模板被填充成了这段文本。此时启动SAT事务码(ABAP运行时分析)重新重现业务场景,可以拿到更完整的ABAP调用堆栈。

3.2 用ST22和STAD判断当时发生了什么

有时候问题已经发生,现场已经被恢复,用户也没有办法立刻重现操作。此时就要依靠SAP系统自身留下的运行痕迹。

ST22是ABAP Dump查看器。如果ABAP端因为数据库返回超时或连接中断产生了短转储,ST22里会留下完整的错误信息和调用栈。很多情况下,虽然崩溃的是一条Open SQL指令,但堆栈里能清楚看到调用它的函数模块、报表名,以及用户当时的操作事务码。

STAD(系统日志)则能查到某一个用户在某个时间执行过哪些事务码、哪些程序。比如财务用户在上午10点15分运行了FAGLL03(总账行项目查询),紧接着内存告警在10点18分出现,时间线上吻合度就很有说服力了。

类似地,对于后台作业引发的问题,用SM37去查询当时运行的后台Job列表,重点看FICO月结(如折旧、结算)、MM的MRP运行(MD01、MD07)等大事务批处理任务的时间与告警时间是否重叠。

3.3 一次容易误判的后台任务场景

我处理过一个非常典型的案例。每天凌晨两点左右,HANA定时出现内存告警,白天完全正常。当时看M_EVENTS里被终止SQL的文本,里面带有MSEG和MATDOC的联合查询,看起来像是在查物料凭证。

由于文本并没有直接暴露ABAP程序名,一开始以为是无脑全表扫描的报表,后来在SM37里一查,发现凌晨两点运行的是固定资产折旧与物料分类账的集成后台程序,该程序对物料凭证表做了大批量统计,拼接出了一个巨大的动态SQL。

这类问题最坑的地方在于:夜批程序你不可能停掉,业务要求又必须保留数据完整性。此时优先考虑的不是改写程序,而是在HANA层确认表数据量、索引情况和统计信息,实在不行再考虑给特定语句加优化Hint或者调整SAP端的程序配置参数。从这次之后,我在排查内存类问题时多了一个习惯:永远把SM37的后台作业清单和M_EVENTS的告警时间线放在一起看,而不是单看某一条SQL。

4. 根因剖析与SQL修复实战

4.1 用EXPLAIN PLAN看执行计划,找到内存黑洞

锁定SQL文本后,下一步是分析它为什么耗内存。HANA的EXPLAIN PLAN功能可以展示执行计划的底层算子,让你看到哪些环节在“吃内存”。

你可以在HANA Studio选中SQL执行Explain Plan,也可以直接敲:

EXPLAIN PLAN FOR SELECT ... ;

执行计划里最需要关注三类算子:

  • 哈希连接(Hash Join)相关算子,哈希连接的特点是把一侧全量数据构建成哈希表放到内存,如果连接条件选择性差,这个哈希表可能把内存暴涨。
  • 行缓存或中间表落盘的算子,尤其是大表之间做UNION或排序时,临时结果集过大。
  • Tabular临时结果集相关的计算,特别是包含大量DISTINCT、GROUP BY、窗口函数时。

你还需要重点确认是否出现了笛卡尔积。HANA执行计划中,如果两个表没有任何连接条件,会产生一个乘法效应的中间结果集。比如A表200万行、B表50万行,笛卡尔积理论上就是1亿行中间结果,内存爆掉只需几个这样的算子叠加。

4.2 案例一:缺少连接条件导致的笛卡尔积“爆炸”

某个客户系统的生产订单报工查询在周末集中进行时,HANA内存告警。查M_SQL_PLAN_CACHE后,发现一条语句MAX_MEMORY_SIZE超过600GB,而业务正常查询通常只有几十MB。

SQL经过格式化后大致是:

SELECT AUFK.AUFNR, AFPO.POSNR, MSEG.MATNR, MSEG.BUDAT FROM AUFK INNER JOIN AFPO ON AUFK.AUFNR = AFPO.AUFNR LEFT JOIN MSEG ON MSEG.AUFNR = AFPO.AUFNR WHERE AUFK.AUFNR IN (...)

看起来没什么问题,但执行计划里显示MSEG表是通过“Nested Loop Join”与AFPO逐条拼的。问题出在哪里呢?MSEG表在HANA上按移动类型、过账日期设计了很多分区,MSEG.AUFNR上没有合适的分区裁剪条件。当IN清单里的订单号过多时,优化器放弃了分区裁剪,扫描了整张MSEG表,中间的LEFT JOIN结果集又被放大到了百万级别。

在这个场景下,最简单的修复是给MSEG增加一个针对AUFNR的二级索引,并通过HANA计算视图固化常用的过滤条件,避免每次扫描全表。

4.3 案例二:IN条件列表过大加全表扫描

另一种常见场景出现在采购或库存分析类报表。ABAPer在程序中拼动态SQL,按用户选择界面里的多个物料编号、工厂代码等生成条件,硬生生拼出一个上千项的IN列表。

SELECT * FROM MATDOC WHERE WERKS IN ('1000', '1001', '1002', ...) AND BWART IN ('101', '102', '201', ...) AND BUDAT >= '20250101'

这条SQL表面上有过滤条件,实际上IN列表中的值多到接近全表。如果MATDOC是数亿行的日志型表,优化器在统计数据时会认为“IN条件覆盖的扇区太多”,干脆放弃索引,直接全表扫描,同时要把匹配结果送去做后续的GROUP BY,内存被瞬间吃满。

这种问题在ABAP端有几种解法:

  • 将IN列表拆成多次查询,分批取出数据后合并。
  • 改用内表与数据库表进行JOIN。
  • 如果必须保留原逻辑,可以给MATDOC增加针对WERKS、BWART、BUDAT的复合列索引。

需要提示的是,SAP标准报表里也会有类似逻辑,但标准程序通常有WHERE条件控制且经过优化,出现问题的往往是客户自定义开发报表。遇到自定义报表,我通常直接找开发团队按“一次取少量、循环处理”的思路重构,比在HANA里硬调参数有效得多。

4.4 案例三:应用层取数过多导致的双重内存压力

有个同行问过我一个问题:一条SQL在HANA侧跑得并不慢,执行计划也很健康,但系统还是出现内存警告。排查后发现,这条SQL通过Open SQL把整张表几千万行数据全部传到了ABAP应用服务器,HANA侧只是把内存给了结果集,应用服务器侧则把内存给了内表。两边一起涨,压力叠加。

这种问题的典型表现是:HANA数据库内存使用率并不算极高,但应用服务器出现重负载,系统总内存告警。SQL本身在数据库端谈不上“有罪”,真正的问题在于调用程序没有做好数据量控制。

定位方法也很简单:去ST05里看这条SQL返回的记录数,如果返回记录数是几百万行甚至更高,再对应到ABAP内表声明处,基本就能判断程序是否存在全表载入的坏味道。

这类SQL的修复方向是:

  • 在ABAP里添加分页逻辑(如使用UP TO n ROWS或分批取数)。
  • 尽量把聚合计算下推到HANA,而不是取回应用层用LOOP汇总。
  • 对于报表类程序,入口限制用户必须输入足够的选择条件,至少保证一个强过滤字段有值。

4.5 补充建议:HANA计算视图和物化视图的运用

对于长期存在且业务上无法简单改代码的场景,可以考虑用HANA计算视图或物化视图来固化复杂逻辑。

计算视图的好处是把JOIN、聚合的负担放在数据库层,由HANA引擎根据统计信息优化执行,避免ABAP程序端动态拼SQL的不确定性。如果业务场景是“固定条件的月报、周报”,可以进一步创建物化视图(如果需要)来提前算好结果。

但这里要提醒一句:计算视图并不是保险箱,如果视图内部各个节点没有合理设置维度,或者没有执行计划级的分区裁剪,同样可能把内存吃光。创建视图后一定要用EXPLAIN PLAN检查整条链路的算子分布。

5. 常见问题与排查技巧实录

5.1 不要一上来就调global_allocation_limit

这个坑我踩过。HANA内存告警出现后,当时第一反应是“内存不够了,把那几个大内存参数往上提一提”,于是调大了global_allocation_limit。结果呢?单条SQL的内存消耗本来就巨大,给了更多配额后,SQL不但没有优雅地降级,反而把系统彻底拖死在更极端的状态,整个HANA实例直接不可用,被迫重启。

现在的原则很明确:global_allocation_limit是兜底保护,是避免系统彻底僵死的最后一道闸,不到万不得已绝对不动。如果内存真的不够用,优先考虑增加物理内存,而不是在参数层面放水。单条SQL的问题要从SQL本身去解决,靠参数喂养只会养出更大的怪物。

5.2 statement_memory_limit这个参数该怎么用

如果你希望在做SQL优化之前先务实地保护一下系统,建议关注的是statement_memory_limit而不是global_allocation_limit。

这个参数可以限制单条语句能够分配的内存上限。当某条SQL超过这个阈值时,HANA会直接终止它,避免全系统被拖垮。你可以在global.ini中配置:

ALTER SYSTEM ALTER CONFIGURATION ('global.ini', 'SYSTEM') SET ('memorymanager', 'statement_memory_limit') = '2000000';

单位是MB,例如上面这条就是把单条语句的内存上限设为2TB(2024年后的版本单位略有差异,需要根据版本确认)。这里要注意,不要设得太小,否则正常的大报表也可能被误杀。我一般先观察这个系统里“正常业务下最复杂的报表”内存峰值,然后在它的基础上上浮30%到50%作为statement_memory_limit。

设置后当出现内存问题时,系统会报“Statement memory limit reached”,这样可以保留一条干净的终止信息,同时不会影响其他会话。

5.3 内存警告反复出现时,先检查统计信息和数据分布

有些时候SQL本身写得很简单、执行计划看着也“正常”,但内存警告还是反复出现。这时候请先去查HANA表统计信息是否最新。

HANA是列存储数据库,优化器对谓词选择性的判断极度依赖表统计信息。如果某张表的ROW COUNT和实际数据量严重不符,优化器可能判断某个过滤条件只能筛出一部分数据,实际却匹配了海量数据,从而选择错误的连接顺序或Join类型。

刷新统计信息可以使用:

CALL UPDATE_TABLE_STATISTICS('SCHEMANAME', 'TABLENAME');

如果是关键大表,建议设置在夜间定时任务里周期执行,或者用HANA的自动统计信息管理策略覆盖。

另外,对分区表的过滤条件要留意分区裁剪(Partition Pruning)是否生效。如果WHERE条件里的字段和分区键不匹配,优化器会扫描所有分区,内存压力自然成倍增加。这种问题光看SQL文本不一定能发现,必须结合执行计划观察Accessed Partitions。

5.4 告警时间与SQL执行时间对不上怎么办

还有一种脑壳疼的情况:M_SQL_PLAN_CACHE里找出来的高内存SQL,执行时间是凌晨三点,但内存告警是上午十点出现的,完全对不上。

这里有一个容易忽略的点:大SQL执行完后,HANA的内存并不一定会立刻释放给操作系统。列存储中的某些内存池在语句结束后仍然保留,以备后续查询重用。因此从业务视角看,告警峰值可能出现在上一波大任务结束后很久。

如果遇到这种时间错位,你要看的是M_EVENTS里实际记录的内存释放缓慢或内存池占用过大的信息,同时结合全局内存趋势图观察,而不是单独咬住某一条SQL不放。这种情况下,排查重点要转向“系统是否存在内存泄漏、某个服务的内存池是否异常膨胀”,可以在HANA Cockpit的“Memory Overview”中按Service查看各进程的已分配内存和峰值内存。

5.5 SQL审核与预防性手段

经历过一次HANA内存溢出警告之后,我相信大部分团队都会意识到SQL规范的重要性。真正要长期解决这类问题,靠的是把预防做在前面。

常见的建议包括:

  • 在SAP开发阶段对Open SQL做数量管控,严格限制无WHERE条件的全表扫描。
  • 对自定义报表入口做强制选择条件,比如日期范围必填、工厂必填。
  • 建立HANA侧的监控,每日检查M_SQL_PLAN_CACHE中内存消耗TOP N的语句。
  • 对CBO模式下容易歪的执行计划,用HANA的SQL Plan Hint或ABAP端的Open SQL Hint做固定的连接顺序优化。
  • 生产环境变更前,用开发或测试HANA实例执行EXPLAIN PLAN,确认执行计划没有出现哈希连接爆内存的算子。

6. 最后再分享一点个人体会

踩过这么多次坑之后,我的感觉是,SAP单SQL语句导致Hana内存溢出警告这件事,绝大多数时候不是数据库不够强,而是“SQL写得不够谦逊”。HANA是内存数据库,内存就是它一切性能的根基,任何无所顾忌的读法都会被放大成可见的故障。遇到这类问题,保持冷静,用M_EVENTS确认告警,用M_SQL_PLAN_CACHE和Expensive Statement Trace抓到SQL,再用ST05反查程序和业务场景,最后回到执行计划去理解为什么内存会爆,这整套流程下来通常是能在半小时内定下方向的。重点是一开始真的别去动那两个核心内存参数,先让现场闭嘴,再让开发把SQL改对。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询