1. SAP HANA 到底是个什么定位的数据库
1.1 从磁盘为中心到内存为中心,变化到底在哪
很多刚接触 SAP HANA 的人会把它理解成"一个更快的数据库",这个理解不算错,但太浅了。真正做过迁移或者调优的人会告诉你,HANA 和传统关系型数据库的差别不在速度上,而在数据组织方式的根本假设变了。传统数据库的整套设计——缓冲池、索引、执行计划缓存、物化视图——本质上都是在和磁盘的随机 I/O 作斗争;HANA 直接把主数据常驻内存,磁盘退化成"持久化保险",读路径上几乎不产生物理 I/O。这个假设一变,很多过去必须做的妥协就不成立了。
举个特别典型的例子:在传统 ECC 系统里,总账相关数据被拆成 BKPF(凭证抬头)、BSEG(凭证行项目)、BSIS(未清项索引)、BSAS(已清项索引)等多张表,还要再和 COEP、ANEP、MLIT 这些成本、资产、物料账的表做关联。为什么拆?因为 BSEG 太大了,全表扫描扛不住,必须靠索引表把常用查询路径"预计算"出来。到了 S/4HANA,这些索引表被直接取消,所有数据落到 ACDOCA 一张通用日记账里,字段超过 400 个。这在行存数据库上几乎是灾难性的设计,但在 HANA 的列存引擎里,查哪列读哪列,不相关的列根本不参与计算,反而比拆成一堆表的连接更快。
所以理解 HANA 的第一个关键,是理解"我们不再需要为了迁就磁盘而扭曲数据模型"。这不是性能优化,是设计哲学的更换。
1.2 内存、列式、并行,这三个词要放在一起看
单独说"内存数据库"其实没意义。早年间的内存数据库不少,但都没有真正在企业核心系统里跑起来,原因就是内存太贵、容量太小。HANA 能成,是因为内存成本降下来之后,它同时把另外两件事也做对了:列式存储和极致的并行。
列式存储带来的直接收益是压缩。同一列数据的类型一致、取值重复度高,字典编码、游程编码(RLE)、簇编码、前缀编码这些手段叠上去,压缩比通常能到 3 到 10 倍。我实测过的几个系统里,主数据表的压缩比一般在 3 到 5 倍,凭证流水类表因为有大段重复的字段(公司代码、年度、货币、科目)反而能压到 6 到 8 倍。压缩不只是省空间,更省的是内存带宽——同样的内存能装下 5 倍的数据,扫描时读的也是压缩后的数据,CPU 解压的代价远小于从内存里搬原始数据。
再说并行。HANA 的执行引擎会把一条 SQL 拆成算子树,然后把列存扫描、聚合、连接这些环节分发到多个 CPU 核心甚至多个节点上并行跑。一条扫 5 亿行的聚合查询,在传统库上可能是分钟级,在 HANA 上经常是亚秒级,靠的就是"读得少(列存)+ 读得快(内存)+ 一起读(并行)"这三件事同时成立。
这三者缺一不可。少了内存,列存的压缩优势发挥不出来;少了列存,内存撑不住数据量;少了并行,单核再快也追不上磁盘阵列的时代。理解了这一点,后面所有的架构设计和调优思路就都有了解释。
1.3 为什么 S/4HANA 和 HANA 是强绑定的关系
经常有人问,S/4HANA 能不能换个数据库跑?技术上,S/4HANA 从 1809 之后确实在部分版本支持了 SAP ASE 和 SAP IQ 的组合,但主流部署里它就是和 HANA 绑定在一起的,原因很实在。
S/4HANA 的大量代码直接使用了 HANA 特有的能力:ABAP CDS 视图里嵌入的计算视图、CDS Table Function、AMDP(ABAP Managed Database Procedure,就是在 ABAP 类里直接写 SQLScript)、以及基于列存特性做的"去索引表"重构。最典型的是物料凭证,原来 MSEG 和 MKPF 两张表在 S/4HANA 里合并成了 MATDOC,物料主数据的 MARD、MARC 里大量的冗余字段被清理,定价条件从 KONV 挪到 PRCD_ELEMENTS。这些改动的共同前提是——底层数据库必须是一张宽表也能扛住、连接能下推、聚合能并行。
把这些设计硬塞回行存数据库,虽然语法上能跑,性能会直接崩掉。所以与其说 S/4HANA 是"用了 HANA 的 ERP",不如说它是"为 HANA 重新设计的 ERP"。理解这个绑定关系,对后面判断哪些场景值得投入调优、哪些参数不能乱改,非常重要。
2. 架构拆解:一个 HANA 实例里到底跑着什么
2.1 Index Server 与持久化层是怎么配合的
打开 HANA 的进程列表,在生产系统里你通常能看到 hdbnameserver、hdbindexserver、hdbcompileserver、hdbwebdispatcher、hdbpreprocessor 这么几个进程。其中真正干活的绝对核心是Index Server,SQL 的解析、优化、执行、数据存储全在这里面。Name Server 负责拓扑管理和元数据,在 HANA 2.0 里 Statistics Server 已经并进 Index Server 了,Preprocessor 负责数据加载时的文本处理(全文索引之类的场景用得上)。
持久化这部分值得单独说。HANA 虽然是内存数据库,但绝不等于"断电就丢"。它的持久化由两块组成:Data Volume存的是主数据(列存的实际数据文件),Log Volume存的是重做日志。写操作进来的时候,先写日志(redo log),内存里同步更新,这叫 WAL(Write Ahead Logging)机制,和传统数据库是一样的思路。数据什么时候真正落到 Data Volume?靠Savepoint机制,默认每 5 分钟触发一次,或者日志量累计到一定阈值(通常配置在 1GB 左右)也会触发。
这意味着一个残酷的现实:如果 Data Volume 所在存储坏了,你恢复数据需要重放日志,而日志能覆盖的时间窗口取决于上一次全量备份。所以生产环境的备份策略必须是全备 + 增量 + 日志备份三层,光做全备是不够的。日志模式(log_mode)一般设成 normal,只有测试系统或者特定数据加载场景才考虑 overwrite 模式,因为后者会放弃日志持久性,生产上绝对不能用。
2.2 MDC 多租户:为什么现在基本见不到单容器了
HANA 1.0 时代是单容器(Single Container)架构,一台机器一套数据库,升级、备份、重启都是一刀切。到了 2.0,多租户数据库容器(MDC,Multi-tenant Database Container)成了默认形态,一个System DB下面挂若干个Tenant DB。
这个设计对运维的意义比想象中大。System DB 只负责管理功能,不存业务数据,它的角色更像"控制平面"。Tenant DB 才是真正的业务库,每个租户有自己独立的用户、schema、备份策略、内存配额。实际收益有几个:
一是升级粒度变了。以前升级必须整台机器停,现在可以先升 System DB,再逐个租户升,窗口能拆开。二是资源隔离。你可以给生产租户配 80% 的内存上限,开发和测试租户加起来只给 20%,某个租户的内存泄漏不会直接拖垮另一个。三是租户迁移。把一个租户从一个 HANA 系统整体挪到另一个系统,用 tenant copy 或者 tenant move 就能做,做系统分拆或者上云迁移时非常省事。
注意:开发、测试、生产共用一个 HANA 实例在技术上是可行的,但生产环境强烈不建议这么做。除了资源争抢,最关键的是备份和恢复会相互影响,一个租户的误操作恢复会牵扯到整个实例。
我见过不少中小项目为了省 License 把三个环境塞在一台机器上,前期没问题,一旦生产出事故要恢复,才发现根本没法只回滚生产租户,只能整实例恢复,停机时间直接翻几倍。这笔账不值。
2.3 运行环境三十年河东:从 XS Classic 到 HDI 与 CAP
在 HANA 上做开发,运行环境的演进路线特别容易让人糊涂,因为三代技术栈还在很多老系统里并存。
最早是XS Classic(XS Engine),用 .xsjs 写服务端脚本,配合 .xsaccess 做权限,本质是 JavaScript 跑在 HANA 里面。这套东西早就停止演进,新项目绝对不要碰。第二代是XSA(XS Advanced),引入了 Node.js 运行时、Java 运行时和 HDI 容器的雏形,但一直比较尴尬,生态没起来。第三代也就是现在真正的主流:HDI 容器 + CAP。
HDI(HANA Deployment Infrastructure)容器可以理解成数据库里的一个"隔离沙箱",里面放的是开发对象——CDS 定义、表、视图、存储过程。部署的时候用设计时描述文件(.hdbtable、.hdbview、.hdbprocedure)声明,通过 hdbcds 或者 CAP 的构建工具一次性推到容器里,不需要手工写 DDL,版本管理交给 Git。应用层则交给 CAP(Cloud Application Programming Model),Node.js 或 Java 都行,前端配 SAPUI5 或 Fiori Elements,部署在 BTP 上。
这套组合的意义在于,开发、测试、生产之间的对象迁移不再是"手工导出导入"这种高危操作,而是真正的流水线。我在实际项目里见过太多因为手工改生产对象导致的版本漂移,HDI 容器算是从机制上把这个坑堵住了。
3. 部署形态怎么选:从一体机到 HANA Cloud
3.1 本地部署:内存和磁盘到底怎么估
硬件规格估算这件事,说复杂也复杂,说简单也有套路。正规做法是跑 SAP Quick Sizer,先算出业务场景需要的 SAPS(SAP Application Performance Standard)值,再折算成 CPU 核数和内存。但实操中很多时候 Quick Sizer 的输入本身就不准,所以经验值更常用。
我一般按这个逻辑估:内存 = 数据压缩后体积 × 2 + 应用侧内存。乘以 2 是因为 HANA 除了存实际数据,还要留出 Delta 存储、临时计算空间、工作内存和系统开销。应用侧内存是给 ABAP 或者 Java 栈留的,如果数据库和应用装在同一台机器上(小系统常见),还要再加一块。数据压缩后体积怎么估?把源系统数据库的净数据量除以 4 到 5,这是比较保守的压缩比。
磁盘容量的估算更容易被忽略。Data Volume 一般是内存的 1.5 到 2 倍,Log Volume 单独放在性能好的盘上,起步 200GB 以上,数据写入量大的系统要按内存的 1/4 来配。备份目录必须独立,至少再留一套全备的空间,加上若干增量。总体下来,磁盘总量通常是内存的 2.5 到 3 倍。
| 压缩后数据量 | 建议内存起点 | 典型场景 |
|---|---|---|
| < 500 GB | 256 GB - 512 GB | 中小型 S/4 生产系统、BW 测试 |
| 500 GB - 2 TB | 1 TB | 中大型 S/4、BW/4HANA |
| 2 TB - 5 TB | 2 TB | 大型集团 S/4、数据仓库 |
| > 5 TB | 4 TB 或 Scale-out | 超大型集团、数据湖 |
提示:这个表是按本地部署、单实例估算的经验值,云上部署因为存储和计算解耦,逻辑完全不一样,不能照搬。
还有就是 Scale-up 和 Scale-out 的取舍。HANA 的 Scale-out 支持是有限制的,不是所有场景都能横向扩。BW 和数据集市类场景支持得最好,S/4HANA 从 1809 起也支持了有限的扩展节点。但实话讲,除非单机内存真的顶到物理上限(比如 6TB 以上),否则优先 Scale-up,架构简单、调优容易、故障面小。
3.2 云上部署:HANA Cloud 和私有云的本质差别
云上的 HANA 主要分两条路。一条是SAP HANA Cloud,跑在 BTP 上的一等公民服务,天然多租户、弹性伸缩、按用量计费,配套的是 HDI 容器、Data Lake、Vector Engine 这些能力。另一条是私有云形态,客户独享一套 HANA,本质上是把本地部署搬到云主机上,好处是可控性强、兼容性好,坏处是弹性差、成本高。
选哪条路,关键看两件事:你有没有改底层参数的需求,以及你的数据量是不是波动很大。
HANA Cloud 的定位是"托管服务",很多底层参数开发者碰不到,Memory Allocation Limit、Savepoint Interval 这类东西你没法直接改,调优空间比本地小很多。如果你的业务对参数级调优依赖很重,或者有大量历史遗留的 Native 对象要迁,私有云或者本地更合适。反过来,如果是个新建的应用或者数据量波动大、想按需付费,HANA Cloud 优势明显,尤其是配合 Datasphere、CAP 这套工具链,开发体验比本地顺畅不少。
还有个容易被忽略的点:HANA Cloud 的版本更新节奏是自动的,抛开了本地部署那种"必须自己规划升级窗口"的麻烦,但代价是你失去了版本选择权。有些团队对某些特定版本的兼容性有硬要求,就得提前确认好。
3.3 版本演进:别再用 1.0 时代的认知看待 2.0
版本这件事值得单独拎出来说,因为很多资料还在讲 HANA 1.0 的架构,实际生产环境早就不是那样了。
HANA 1.0 的最后一个版本是 SPS12,2021 年已经到维护终点,现在还在跑的基本是被迫的遗留系统。HANA 2.0 从 SPS00 开始,SPS07 是个重要节点,之后 SAP 改成了按季度发布 Revision 的模式,不再用 SPS 编号。S/4HANA 2022 之后的主流版本,基本都要求 HANA 2.0 SPS07 以上。
升级前有几件事是必须做的。第一,跑 SAP 提供的 Upgrade Check,它会扫出所有不兼容的对象和参数。第二,确认自定义的存储过程、计算视图、XS 相关对象有没有用到废弃语法,特别是用了 XS Classic 的老系统,升级到 2.0 会遇到大面积不兼容。第三,备份必须完整,而且要验证过能恢复,光有备份文件不算数。
我踩过一次坑,升级前所有检查都过了,升级完发现某个自研的计算视图用了早期版本的函数语法,在新版本上直接报错,而这个视图藏在一条业务链路的末端,测试用例没覆盖到,上线两天后才被财务发现。从那之后我的做法是,升级前把所有自定义对象导出成清单,逐个过一遍语法兼容性,哪怕多花两天时间。
4. 建模与开发:把原始数据变成能用的东西
4.1 三条建模路线,先搞清楚该走哪条
HANA 上的数据建模有三条常见路线,很多人一开始就选错了,后面维护成本高得离谱。
第一条是ABAP CDS 视图。定义写在 ABAP 层,用 @AbapCatalog 之类的注解描述,部署后下推到 HANA 执行。S/4HANA 的标准应用基本都是这条路,你在 SE11 或者 Eclipse 里看到的 I_、C_ 开头的视图都是。它的优势是和 ABAP 权限、传输机制无缝集成,改起来规范。
第二条是HDI 容器里的 CDS。定义写在数据库层,通过 .hdbcds 或者 CAP 项目的 CDS 文件部署,版本管理走 Git。这条路线适合自研应用、旁路分析系统,尤其是要部署到 HANA Cloud 的场景。
第三条是计算视图(Calculation View)。图形化建模,在 HANA Studio 或者 HANA Tools for Eclipse 里拖拽出来,支持多维分析、层级、变量这些高级特性。适合 BI 场景和数据集市,但不适合让 ABAP 直接调用来做事务处理。
简单给个判断标准:业务应用找 ABAP CDS,自研应用找 HDI CDS,分析报表找计算视图。别看这三条路都能出结果,混用之后权限、传输、性能调优全都会出问题。
下面是一个 ABAP CDS 的最小示例,基于 ACDOCA 取总账行项目:
@AbapCatalog.sqlViewName: 'ZVI_JOURNAL_ITEM' @AccessControl.authorizationCheck: #CHECK @EndUserText.label: '总账行项目视图' define view ZI_JournalItem as select from acdoca { key rldnr, // 分类账 key rbukrs, // 公司代码 key gjahr, // 会计年度 key belnr, // 凭证编号 key docln, // 行项目号 buzei, // 行项目 bldat, // 凭证日期 budat, // 过账日期 racct, // 科目 hsl, // 本位币金额 wsl, // 集团货币金额 augdt // 清账日期 } where rldnr = '0L'注意里面的 key 定义。ACDOCA 的行唯一性靠的是分类账 + 公司代码 + 年度 + 凭证号 + 行项目号这个组合,少一个字段就会出现重复行,后面聚合的时候数值翻倍。我在排查数据对不上的问题时,十次里有三次是 CDS 里 key 定义漏字段导致的。
4.2 SQLScript 和 AMDP:什么时候该自己写代码
HANA 的 SQL 方言叫 SQLScript,它比标准 SQL 多了些东西:表变量、流程控制、存储过程、标量函数和表函数。写 SQLScript 的第一原则是能声明式就别命令式,能用一条 SELECT 搞定的事情,别写游标循环。
表变量(Table Variable)是个特别容易被误解的概念。它不是临时表,不落盘,也不进事务日志,本质上是执行计划中间结果的一个引用。好处是零开销,坏处是没法建索引、没法统计信息,数据量大或者要反复访问的时候反而会拖慢。我见过有人把几百万行数据塞进表变量反复查,性能还不如直接读原表。
下面这个存储过程演示一个简单的预算超限检查逻辑:
CREATE PROCEDURE Z_SP_LIMIT_CHECK ( IN IV_BUKRS NVARCHAR(4), OUT EV_TOTAL DECIMAL(23,2) ) LANGUAGE SQLSCRIPT READS SQL DATA AS BEGIN DECLARE lv_total DECIMAL(23,2); SELECT SUM(HSL) INTO lv_total FROM ACDOCA WHERE RBUKRS = :IV_BUKRS AND RLDNR = '0L' AND AUGDT IS NULL; EV_TOTAL = IFNULL(:lv_total, 0); IF :lv_total > 1000000 THEN INSERT INTO ZTB_ALERT VALUES (CURRENT_TIMESTAMP, :IV_BUKRS, :lv_total); END IF; END;AMDP 就是把这段 SQLScript 搬到 ABAP 类里写。好处是能直接引用 ABAP 的 DDIC 类型,参数传递不用做类型映射,而且能享受 ABAP 的传输和权限体系。写法上要注意BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT这行声明,以及USING后面必须列出所有引用的数据库对象,漏了会编译报错。
CLASS zcl_demo_amdp DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES if_amdp_marker_hdb. CLASS-METHODS get_open_items IMPORTING VALUE(iv_bukrs) TYPE bukrs EXPORTING VALUE(et_item) TYPE ztt_open_item. ENDCLASS. CLASS zcl_demo_amdp IMPLEMENTATION. METHOD get_open_items BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT OPTIONS READ-ONLY USING acdoca. et_item = SELECT rbukrs, belnr, gjahr, buzei, racct, hsl FROM acdoca WHERE rbukrs = :iv_bukrs AND rldnr = '0L' AND augdt IS NULL; ENDMETHOD. ENDCLASS.有一点要提醒:AMDP 里的 SQL 是完全下推到数据库执行的,ABAP 侧的权限检查(比如公司代码权限)不会自动生效,必须自己在 SQL 里显式加上过滤条件,或者用 CDS 的权限注解兜住。这是审计里最容易被挑出来的问题。
4.3 和 FICO、MM、SD 这些模块到底怎么对接
HANA 给业务模块带来的变化,最直观的是"批处理变实时"和"多表变一表"。
FICO 侧,ACDOCA 一张表同时承载了总账、应收应付、资产、成本要素、物料分类账的信息,这在以前需要至少五六张表 join 才能拼出来。带来的直接效果是:成本核算和 FI 的集成不再需要跑重型的结算批作业,凭证分割(Document Splitting)的在线计算也扛得住,货币换算支持到 10 个货币位并存。你打开财务凭证的底稿,看 ACDOCA 里的字段就知道,同一个金额在 HSL、WSL、KSL、OSL 等多个货币字段里同时记录,这在过去是不可想象的。
MM 侧,物料凭证合并成 MATDOC,采购订单历史、收货、发票校验的数据关联简化了很多。货源清单、采购计划协议这些业务的底层表结构都做了精简,MARC 里大量冗余字段被清掉。顺带说一句,以前在 ECC 里天天用的 MD07(MRP 清单),在 S/4HANA 里被 Fiori 应用取代了,功能对应的是"Monitor Material Coverage",从列表变成了图形化的覆盖分析,刚开始用不太习惯,但看库存缺口确实比纯列表直观。
SD 侧,交货单和开票与 FI 的集成变成了实时的,VBFA 这类索引表被精简。定价条件从 KONV 挪到了 PRCD_ELEMENTS,字段结构本身没大改,但底层存储和访问路径完全不一样了。
还有一块绕不开的是业务伙伴(BP)主数据。S/4HANA 里客户和供应商不再各自独立维护,统一收敛到 Business Partner,底层表是 BUT000、BUT020 这一系列。FI 侧的客户主数据(KNA1)和供应商主数据(LFA1)通过 CVI 机制和 BP 同步。做过 BP 配置的人应该知道 CVI 的映射规则有多容易出错,尤其是那些既当客户又当供应商的往来单位,配置不当会出现数据不一致。这个问题跟 HANA 本身没关系,但因为它发生在 HANA 平台上,排查的时候经常被误以为是数据库问题。
5. 数据同步与集成:SLT、SDI、BODS 怎么选
5.1 三种复制工具的定位差异
把数据从源系统搬到 HANA,常用的工具有三个,定位差别很大,选错了后面全是麻烦。
SLT(SAP Landscape Transformation Replication Server)是基于触发器的实时复制方案。原理是在源库的相关表上建触发器,任何写操作都会把变更记录到日志表,SLT 再读取日志表推到目标 HANA。优势是延迟低,通常几秒钟,而且支持过滤、转换、初始加载。适合 S/4 到 BW/4HANA 的实时同步、或者从 ECC 往 HANA 做持续复制。缺点是触发器会带来源系统负载,表特别多的时候要分批配置。
SDI(Smart Data Integration)用的是适配器架构,通过 Data Provisioning Agent 连接各种数据源,关系型数据库、文件、消息队列都支持。它既能做批量也能做实时,灵活性比 SLT 高,但对非 SAP 数据源的支持更自然。做数据湖和数据集市的时候这是首选。
BODS(Business Objects Data Services)是最传统的批量 ETL 工具,强项在数据清洗和复杂转换,图形化的作业设计器用起来门槛低。但它不是实时方案,跑批量作业的窗口动辄几小时,适合夜间跑历史数据的场景。
| 工具 | 延迟 | 典型场景 | 主要限制 |
|---|---|---|---|
| SLT | 秒级 | S/4 到 BW/4HANA、系统迁移 | 触发器增加源系统负载 |
| SDI | 秒级到分钟级 | 异构数据源、数据湖 | 配置复杂,需要 Agent |
| BODS | 小时级 | 批量清洗、历史数据初始化 | 非实时,窗口压力大 |
5.2 BW/4HANA 和 BTP 上的链路是怎么搭的
BW 系统在 HANA 上的形态,和传统 BW 差别挺大。BW on HANA 还保留了完整的三层结构(InfoProvider、DSO、InfoCube),只是底层换成了列存。BW/4HANA 则做了大幅精简,只保留 InfoObject、ADSO(Advanced DataStore Object)、CompositeProvider、Open ODS View 这几个对象类型,剩下的全靠 HANA 侧的能力兜。
这里有个很好用的特性叫混合建模(Mixed Scenarios)。你可以在 HANA 里建一个计算视图,然后在 BW 里直接把它当成一个 Provider 来查,查询会整体下推到 HANA 执行,BW 侧不再做二次聚合。做复杂分析的时候这个能力很关键,能省掉大量中间层的数据搬运。前提是两边要保持对象同步,HANA 视图改了 BW 侧要跟着改,否则会出现数据口径不一致。
到了 BTP 上,链路就更现代了。Datasphere(原来的 Data Warehouse Cloud)底层就是 HANA Cloud,做数据建模用的是图形化的 Data Builder 加上 SQL View;配合 Replication Flow 可以直接从 S/4 或者外部数据库同步数据,不再需要单独部署 SLT 或者 SDI。如果你的目标是把数据汇总、建模、出报表一次做完,Datasphere 比传统 BW 路径短不少。但要注意它的建模能力相对受限,特别复杂的转换逻辑还是得回 SQLScript 里写。
6. 性能调优与故障排查实录
6.1 内存、Delta Merge、分区这三板斧
HANA 调优九成的问题都落在这三件事上。
内存要先看总量再看分布。用 M_SERVICE_MEMORY 看整个服务的内存分配,M_HEAP_MEMORY 看堆内存的细分,M_CS_TABLES 看每张列存表实际占用了多少。我遇到最多的情况是某几张日志表或者自定义的中间表疯狂膨胀,占掉几十个 G,主业务表反而没多少。这种时候先去查 M_CS_TABLES 按 MEMORY_SIZE_IN_TOTAL 排序,前二十张表一看就知道问题在哪。
Delta Merge是列存特有的机制。新写入的数据先进 Delta 存储(行存结构,写入快),达到阈值后自动合并到 Main 存储(列存结构,读取快)。这个"自动"容易被误解成定时任务,实际上它是基于写入量和查询代价动态触发的,有时候一次大批量导入之后合并迟迟不触发,查询性能就会很差。可以用ALTER TABLE 表名 MERGE DELTA手动催一次,但要注意它会锁表,生产环境最好在业务低谷做。如果 Delta 已经大到无法合并,极端情况下要用ALTER TABLE 表名 EMERGENCY TRUNCATE DELTA,这个操作会丢数据,只能作为最后手段。
分区的判断标准看数据量,一般超过 2 亿行就值得评估,超过 10 亿行基本必须分。分区方式上,Hash 分区最常用,能把数据均匀打散;Range 分区按年度或者月份做,适合做数据归档和分区裁剪;两者还能组合成多级分区。分区之后最怕的是查询里没带分区键的条件,会导致扫描所有分区,性能反而更差。我一般要求开发写 SQL 时必须带上年度条件,这不是规范问题,是性能红线。
6.2 常见报错速查表
下面这张表是我这几年攒下来的,按出现频率排序:
| 报错或现象 | 可能原因 | 排查入口 | 处理思路 |
|---|---|---|---|
| allocation failed: cannot allocate enough memory | 内存超限,或某张表异常膨胀 | M_SERVICE_MEMORY、M_CS_TABLES | 找大表,卸载冷数据,调 unload priority |
| Delta merge is not possible due to lock | 有长事务占着表锁 | M_BLOCKED_TRANSACTIONS | 找出阻塞会话,评估后 kill |
| Lock wait timeout exceeded | 锁等待超时 | M_BLOCKED_TRANSACTIONS、M_ACTIVE_STATEMENTS | 查长事务来源,检查应用提交逻辑 |
| Column store table is not loaded | 表被卸载出内存 | M_CS_TABLES 的 LOADED 字段 | 首次访问自动加载,持续失败查内存水位 |
| SQL error 2048: column store error | 列存底层错误,原因很多 | indexserver trace 日志 | 结合 trace 定位,必要时联系支持 |
| 日志卷增长异常快 | 长事务不提交、批量作业并发高 | M_VOLUME_IO_TOTAL_STATISTICS | 检查应用的 commit 频率 |
| 备份卡住或者失败 | 备份目录空间不足、Backint 配置问题 | M_BACKUP_CATALOG、M_BACKUP_PROGRESS | 清空间,核对第三方备份工具配置 |
注意:用 M_BLOCKED_TRANSACTIONS 找到阻塞源之后,不要急着 kill 会话。先看清楚那个会话在跑什么,很多情况下它是一个正常的批量作业,直接杀掉会留下数据不一致,后果比等十分钟严重得多。
6.3 备份恢复和日常运维清单
备份这件事,原则只有一个:没演练过的备份等于没有备份。我见过不止一次,真出事的时候拿出备份文件,发现恢复不了,因为少了某个日志段或者备份链断了。
标准配置是全备每周一次,差备每天一次,日志备份每 15 分钟一次。全备的窗口要避开业务高峰,大系统的全备动辄几小时。备份目标要么用文件系统(Backint 之外的方式),要么通过第三方工具走 Backint 接口,后者能做重复数据删除和增量备份,空间上省得多。
租户数据库的备份策略是独立的,System DB 的备份不能代替租户备份,这一点很多人会搞混。做恢复演练的时候要单独测租户的 point-in-time 恢复,把时间点设在某个关键业务操作之前,看能不能准确还原。
日常巡检我一般看这几个地方:内存水位(保持在 80% 以下比较健康)、Delta 存储占比、日志卷使用率、是否有长期未合并的大表、备份是否连续成功。用 HANA Cockpit 或者 DBACOCKPIT 都能看,命令行的话 hdbsql 连上去查系统视图更灵活。我习惯每周跑一次脚本把关键指标导出来存着,出问题的时候有历史数据可以对比,定位起来快很多。
7. 我自己踩过的几个坑
7.1 看起来不像内存问题,其实都是内存问题
有一次生产系统突然报一堆莫名其妙的错,SQL 执行超时、报表打不开、甚至登录都变慢,但监控上 CPU 和磁盘都正常。查了半天才发现是内存水位到了 95% 以上,HANA 开始主动卸载列存表,被卸载的表一旦被访问就要重新加载,加载又抢不到内存,形成恶性循环。
这个教训是:HANA 的内存问题不一定表现为"内存不足"这个报错,它经常伪装成性能问题。所以监控内存水位比监控 CPU 重要得多。后来我加了条规则,内存超过 85% 就告警,超过 90% 就人工介入,不等它自己出问题。
还有一个相关的是 unload priority。被卸载的表加载慢,可以给核心业务表设置高的 unload priority 让它尽量常驻内存,命令是ALTER TABLE 表名 UNLOAD PRIORITY 5(数字越大越不容易被卸载)。但这个操作要谨慎,把太多表设成常驻,等于把内存吃满了,反而更容易触发其他表的卸载。
7.2 升级和迁移里最容易漏掉的检查项
升级前跑官方检查工具是必须的,但工具查不出来的是自定义对象的实际使用情况。我的做法是先把所有自定义的存储过程、计算视图、CDS 视图、XS 对象导一份清单出来,然后逐个确认:还在用吗?谁在用?有没有替代品?确认完毕之后再逐个做语法兼容性测试,特别是那些引用了废弃函数或者老版本语法的对象。
另外一件常被忽略的事是权限对象的迁移。系统升级或者租户迁移之后,角色和权限有时候会对不上,尤其是用了自定义权限对象或者分析权限(Analytic Privilege)的场景。上线前一定要做完整的权限测试,别等到用户登录进来发现什么都看不了。
最后一个是时间窗口。升级的实际耗时经常比计划的长 50% 以上,因为升级过程中会遇到各种预期外的问题,比如某个大表的迁移超时、某个参数校验失败需要回退。我现在的做法是计划窗口按实际估算的两倍来安排,宁可提前结束,也不要卡在半夜进退两难。
真正在 HANA 上做久了会发现,这个平台本身挺稳的,出问题的往往不是数据库,而是我们对它的理解还停留在旧时代——用行存库的思维去设计表、用传统 ETL 的思路去同步数据、用"重启试试"的习惯去处理性能问题。把内存、列存、并行这三个底层假设刻进脑子里,很多看起来玄学的问题就都有迹可循了。