StarRocks Stream Load 常见问题排查实战:CSV 表头、数据转换、超大文件与保留字处理
【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks
Stream Load 是 StarRocks 面向本地文件与流式数据源的同步导入通道:提交作业后系统同步执行并直接返回结果,因此作业失败时错误信息往往直接决定了你能否快速定位问题。本文围绕官方 FAQ 中最高频的 5 类问题展开——CSV 首行表头识别与跳过、非标准日期/数值的加载时转换、body exceed max size报错、字符串与NULL的语义区分、保留字列名报错——逐一给出可复制执行的解决方案,并结合仓库源码(stream_load.cpp、config.h、StreamLoadHttpHeader.java 等)解释报错与参数背后的实现原理,让你不仅会修,而且知道为什么。
前提:Stream Load 请求的基本形态
本文所有示例均使用curl发起 PUT 请求,核心骨架如下(完整语法见 STREAM LOAD):
curl --location-trusted -u <username>:<password> -XPUT <table_url> \ -H "Expect:100-continue" \ -H "label:<label_name>" \ -H "columns: ..." \ -H "where: ..." \ -T <file_path>其中table_url形如http://<fe_host>:<fe_http_port>/api/<database>/<table>/_stream_load。需要提醒的两点:建议使用 chunked transfer encoding(curl 会自动处理);请求头中可附带label(作业标签,用于幂等去重)、columns(列映射与转换)、where(过滤条件)、max_filter_ratio(容错率)等参数,本文接下来的每个问题都会落到这些参数上。
一、CSV 文件首行是列名:识别与跳过
问题:Stream Load 是否支持识别 CSV 文件前几行的列名,或在读取数据时跳过前几行?
1.1 结论与版本差异
Stream Load不支持识别CSV 文件前几行的列名——系统把前几行当作普通数据行一样处理。是否支持"跳过"则与版本强相关:
- v2.5 及更早版本:不支持在读取 CSV 时跳过前几行。
- v3.0 起:新增
skip_header参数,可指定跳过的行数。
1.2 v2.5 及更早版本的四种替代方案
如果你的 CSV 文件前几行确实放的是列名,可以任选其一:
方案 A:修改导出工具设置,重新导出不含表头的 CSV。最干净,从源头解决。
方案 B:用sed删除前几行。
sed -i '1d' filename # 删除第 1 行;连续多行可写 '1,3d'方案 C:用where条件过滤掉前几行。
-H "where: <column_name> != '<column_name>'"<column_name>取前几行中任意一个列名即可。但要注意 StarRocks 的加载顺序是先转换、后过滤:如果前几行的列名字符串无法转换为目标列的数据类型,转换结果会是NULL。因此,目标表中不能存在被设置为NOT NULL的对应列,否则这些行会因约束冲突而报错。
方案 D:用max_filter_ratio容忍少量错误行。
-H "max_filter_ratio:0.01"max_filter_ratio表示允许被过滤掉的数据行占全部请求数据的最大比例,取值范围0到1,默认0(StreamLoadTask.java 中定义的DEFAULT_MAX_FILTER_RATIO = 0.0即为该默认值)。将其设为 1% 或更小的值,可以容忍前几行转换失败;此时作业即使返回了ErrorURL(错误行详情地址),也仍能成功。切勿将max_filter_ratio设得过大,否则会掩盖真实的数据质量问题。另需注意:被where条件过滤掉的行不纳入max_filter_ratio的计算范围。
1.3 v3.0 及以后:直接使用skip_header
从 v3.0 起,Stream Load 支持 CSV 参数skip_header,类型为 INTEGER,默认值0,含义为跳过文件开头 N 行:
curl --location-trusted -u <username>:<password> \ -H "Expect:100-continue" \ -H "skip_header: 1" \ -T example.csv -XPUT \ http://<fe_host>:<fe_http_port>/api/test_db/table1/_stream_load注意:被跳过的行必须以你指定的行分隔符(row_delimiter,默认\n)结尾,系统才能正确计数。参数完整说明见 STREAM LOAD 的 CSV 参数。
源码佐证:BE 端在 stream_load.cpp 中解析该请求头并做合法性校验——skip_header必须大于等于 0,否则直接返回InvalidArgument;解析后的值写入 thrift 请求的skipHeader字段。FE 端则通过 StreamLoadHttpHeader.java 定义请求头常量HTTP_SKIP_HEADER = "skip_header",并经 StreamLoadKvParams.java 解析为长整型。在 BE 的 CSV 解析链路中,CSVParseOptions结构体自带skip_header字段(见 csv_reader.h),文件扫描器会从起始偏移为 0 的数据分片处逐行跳过指定行数——从源码结构看,这意味着该参数只在数据流的头部生效,与文档语义一致。
二、非标准日期/数值(如 202106.00)如何加载进分区列
问题:要加载到分区列的数据不是标准 DATE 或 INT 类型,例如格式为202106.00,用 Stream Load 加载时如何转换?
StarRocks 支持加载时数据转换(Transform data at loading),即不需要在上游 ETL 中预先处理,详见 Transform data at loading。
假设要加载的 CSV 文件名为TEST,包含NO、DATE、VERSION、PRICE四列,其中DATE列的数据是非标准格式202106.00,且你希望把它用作分区列。操作分三步:
- 建表:目标表包含
NO、VERSION、PRICE、DATE四列,其中DATE列的数据类型指定为DATE、DATETIME或INT。 - 提交 Stream Load 作业时,通过
columns参数做转换。 - 使用
left()等标量函数对源列取值计算后写入目标列。
-H "columns: NO,DATE_1, VERSION, PRICE, DATE=LEFT(DATE_1,6)"2.1 临时列机制解读
在上面的例子中,DATE_1是给源数据DATE列起的临时列名,它映射目标DATE列;最终写入目标DATE列的值由left(DATE_1, 6)计算得出(即取202106.00的前 6 位得到202106)。使用规则:
- 必须先把源数据所有列按顺序用临时名列出,然后再写形如
目标列 = 表达式的转换项; - 支持的函数为标量函数,包括非聚合函数与窗口函数;
- 转换表达式按顺序求值,后面的表达式可以引用前面已定义的临时列。
2.2 更多转换场景(源自 Etl_in_loading 扩展)
加载时转换不止能处理日期截取,还能做列跳过、行过滤、派生列、Hive 分区字段提取(详见 Transform data at loading)。例如把yyyy-mm-dd hh:mm:ss格式的单列拆成年/月/日三列:
-H "columns: col, year = year(col), month = month(col), day = day(col)"再如把两个源列转换为 HLL / BITMAP 类型:
-H "columns: temp1, temp2, col1=hll_hash(temp1), col2=hll_empty()" -H "columns: temp1, temp2, col1=to_bitmap(temp1), col2=bitmap_empty()"这些能力同样适用于 JSON 数据(jsonpaths+columns组合,见 STREAM LOAD 的 JSON 参数)。
三、报错 "body exceed max size: 10737418240, limit: 10737418240" 怎么办
问题:Stream Load 作业报body exceed max size错误。
3.1 错误含义
10737418240字节恰好等于10 GB。该错误说明请求体(即源数据文件)超过了 Stream Load 支持的最大文件大小。值得指出的是,错误中显示的数值并非硬编码的 10 GB,而是 BE 配置项streaming_load_max_mb的当前值换算出的字节数——v2.5 及更早版本默认值为10240(MB,即 10 GB),因此报错显示10737418240;从 v3.0 起默认值已提升为102400MB(100 GB,见 query_loading.md)。
源码佐证:BE 在接收 Stream Load 请求时,用config::streaming_load_max_mb * 1024 * 1024计算上限,并与Content-Length头比对,超出即返回错误,提示语明确指向该配置项(stream_load.cpp);同时在evhttp层也会调用evhttp_set_max_body_size设置请求体上限(ev_http_server.cpp)。配置项本身定义于 config.h:
CONF_mInt64(streaming_load_max_mb, "102400");该配置为动态参数(Is mutable: Yes),意味着可以不改be.conf直接在线调整。
3.2 解决方案一:拆分源文件
用seq配合split把大文件切成多个小文件逐个加载:
seq -w 0 n | xargs -I{} split ...FAQ 给出的思路是先用seq -w 0 n生成序号,再配合行号范围切分(例如sed -n '1,100000p')。实践中更通用的做法是直接使用 GNUsplit:
split -l 1000000 big.csv part_ # 每 100 万行切一个文件拆分后为每个分片分别提交 Stream Load 作业(务必使用不同label)。
3.3 解决方案二:动态调大 BE 配置
通过 BE 的 HTTP 配置接口在线调整streaming_load_max_mb(单位 MB),无需重启:
curl -XPOST "http://<be_host>:<be_http_port>/api/update_config?streaming_load_max_mb=<file_size>"例如调到 50 GB:
curl -XPOST "http://192.168.0.10:8040/api/update_config?streaming_load_max_mb=51200"注意事项:
- 调大该值会增加 BE 的内存压力(请求体需要缓冲,尤其 JSON 数据在 stream_load.cpp 中会按
body_bytes预分配内存),请结合 BE 实际内存评估; - 若希望永久生效,应把
streaming_load_max_mb写入be.conf后重启 BE; - 相关配置说明见 BE 参数 streaming_load_max_mb(默认
102400MB、类型 Int、单位 MB、可动态修改)。
3.4 关联:JSON 数据还有独立的 100 MB 限制
如果你加载的是 JSON 数据,还需注意:默认情况下单次 HTTP 请求中的 JSON body 不能超过 100 MB,否则报错The size of this batch exceed the max size [104857600] of json type data。此时可在请求头加"ignore_json_size:true"跳过检查(但可能引发较大内存消耗),详见 STREAM LOAD 的 JSON 参数。
四、如何加载真正的 NULL,而不是把 "null" 写进字符串列
问题:通过 Stream Load 加载时,想把源数据中的字符串"null"转成数据库真正的NULL值,而不是把字符串"null"写进列里。
使用replace函数即可:
-H "columns: pk, temp, pd_type=replace(temp,'NULL',NULL)"replace(temp, 'NULL', NULL)的含义是:把源列temp中的字符串'NULL'替换为真正的 SQLNULL,替换结果写入目标列pd_type。前提是columns中已先把源数据列按顺序临时命名为pk、temp。
4.1 补充:CSV 中 NULL 的标准写法\N
与"加载时转换"配套,还需理解 CSV 中空值与 NULL 的区分:在 StarRocks 的 CSV 语义里,NULL 值用\N表示。例如一行有三列,第二列为空,应写成:
a,\N,b而不是a,,b——后者表示第二列是空字符串'',二者语义完全不同(详见 STREAM LOAD 的 CSV 参数)。结合replace转换,你可以灵活地把上游各种"伪空值"(如空串、"NULL"、"null")统一规整为真正的NULL。
五、字段名 "role" 导致 Stream Load 报错
问题:字段名role会导致 Stream Load 报错,列名到底该怎么命名?
role是 SQL 语言中的保留字(reserved keyword)。在 SQL 语句中不能直接使用保留字,如果确实要用,需要用一对反引号(`)包裹:
-H $'columns:k1,`role`'注意这里使用了$'...'引号形式,确保 shell 不会吞掉反引号。该规则同样适用于STREAM LOAD语句、BROKER LOAD等其他加载方式以及建表语句中的列名。完整保留字列表见 Keywords,STREAM LOAD 文档对此也有专门提醒(STREAM_LOAD.md)。
排查建议:如果列名报语法错误,先对照保留字列表确认;对任何保留字列名统一加反引号包裹,是最稳妥的命名习惯。
六、常用参数速查表
把上文涉及的 Stream Load 参数汇总如下,完整参数表见 STREAM LOAD:
| 参数 | 作用 | 取值范围 / 默认值 | 相关 FAQ 场景 |
|---|---|---|---|
columns | 列映射与加载时转换 | 逗号分隔的临时列名与列=表达式 | 问题二、四、五 |
where | 按条件过滤(先转换后过滤) | 任意布尔表达式 | 问题一(方案 C) |
max_filter_ratio | 错误行最大容忍比例 | 0~1,默认0 | 问题一(方案 D) |
skip_header | 跳过 CSV 开头 N 行(v3.0+) | INTEGER,默认0,需 ≥ 0 | 问题一 |
format | 数据文件格式 | CSV/JSON,默认CSV | 通用 |
label | 作业标签(幂等) | 字符串 | 通用 |
timeout | 作业超时 | 1~259200秒,默认600 | 大文件加载 |
streaming_load_max_mb(BE 配置) | 单文件最大大小 | 默认102400MB(v3.0 起),动态可调 | 问题三 |
ignore_json_size | 跳过 JSON body 100 MB 检查 | true/false | 问题三(JSON) |
总结
五个高频问题背后其实是 Stream Load 的五条核心机制:
- CSV 表头问题:旧版本靠
where+max_filter_ratio兜底,v3.0 起直接用skip_header; - 数据类型转换:
columns临时列 + 标量函数,让 ETL 转换下沉到加载阶段; - 大文件限制:受 BE 动态参数
streaming_load_max_mb控制,可拆分文件或在线调参; - NULL 语义:用
replace把字符串"null"规整为真NULL,同时记住 CSV 中\N才表示 NULL; - 保留字列名:一律用反引号包裹。
这些参数均可在官方文档 STREAM LOAD 与 Transform data at loading 中查到完整定义,BE 侧的配置默认值与可动态性可核对 query_loading.md,底层实现则对应 stream_load.cpp 与 StreamLoadHttpHeader.java。建议在实际作业提交前,先在测试表上验证columns与where的组合效果,再推广到生产导入任务。
【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考