Starrocks 写入报错 primary key memory usage exceeds the limit

背景

本文基于 StarRocks 3.3.5
单个Starrocks BE配置是 16CU 32GB
在Flink Yaml CDC 任务往 Starrocks写数据的过程中,突然遇到了primary key memory usage exceeds the limit 问题,具体如下:

java.lang.RuntimeException: com.starrocks.data.load.stream.exception.StreamLoadFailException: Transaction prepare failed, db: xxx, table: xxxx, label: flink-960f94fc-6fb1-43e4-aaa1-4c3938241ffa, 
responseBody: {
    "Status": "MEM_LIMIT_EXCEEDED",
    "Message": "primary key memory usage exceeds the limit. tablet_id: 479203, consumption: 15928614825, limit: 15790082457. Memory stats of top five tablets: 4258582(314M)4258578(272M)4258340(230M)2957546(190M)2957590(190M):  be:xxx.xxx.xxx.xxx"
}
errorLog: null
	at com.starrocks.data.load.stream.v2.StreamLoadManagerV2.AssertNotException(StreamLoadManagerV2.java:427)
	at com.starrocks.data.load.stream.v2.StreamLoadManagerV2.write(StreamLoadManagerV2.java:252)
	at com.starrocks.connector.flink.table.sink.v2.StarRocksWriter.write(StarRocksWriter.java:143)
	at org.apache.flink.streaming.runtime.operators.sink.SinkWriterOperator.processElement(SinkWriterOperator.java:182)
	at org.apache.flink.cdc.runtime.operators.sink.DataSinkWriterOperator.processElement(DataSinkWriterOperator.java:178)
	at org.apache.flink.streaming.runtime.tasks.CopyingChainingOutput.pushToOperator(CopyingChainingOutput.java:75)
	at org.apache.flink.streaming.runtime.tasks.CopyingChainingOutput.collect(CopyingChainingOutput.java:50)
	at org.apache.flink.streaming.runtime.tasks.CopyingChainingOutput.collect(CopyingChainingOutput.java:29)
	at org.apache.flink.streaming.api.operators.StreamMap.processElement(StreamMap.java:38)
	at org.apache.flink.streaming.runtime.tasks.OneInputStreamTask$StreamTaskNetworkOutput.emitRecord(OneInputStreamTask.java:245)
	at org.apache.flink.streaming.runtime.io.AbstractStreamTaskNetworkInput.processElement(AbstractStreamTaskNetworkInput.java:217)
	at org.apache.flink.streaming.runtime.io.AbstractStreamTaskNetworkInput.emitNext(AbstractStreamTaskNetworkInput.java:169)
	at org.apache.flink.streaming.runtime.io.StreamOneInputProcessor.processInput(StreamOneInputProcessor.java:68)
	at org.apache.flink.streaming.runtime.tasks.StreamTask.processInput(StreamTask.java:616)
	at org.apache.flink.streaming.runtime.tasks.mailbox.MailboxProcessor.runMailboxLoop(MailboxProcessor.java:231)
	at org.apache.flink.streaming.runtime.tasks.StreamTask.runMailboxLoop(StreamTask.java:1071)
	at org.apache.flink.streaming.runtime.tasks.StreamTask.invoke(StreamTask.java:1020)
	at org.apache.flink.runtime.taskmanager.Task.runWithSystemExitMonitoring(Task.java:959)
	at org.apache.flink.runtime.taskmanager.Task.restoreAndInvoke(Task.java:938)
	at org.apache.flink.runtime.taskmanager.Task.doRun(Task.java:751)
	at org.apache.flink.runtime.taskmanager.Task.run(Task.java:567)
	at java.lang.Thread.run(Thread.java:879)
Caused by: com.starrocks.data.load.stream.exception.StreamLoadFailException: Transaction prepare failed, db: xxx, table: xxx, label: flink-960f94fc-6fb1-43e4-aaa1-4c3938241ffa, 
responseBody: {
    "Status": "MEM_LIMIT_EXCEEDED",
    "Message": "primary key memory usage exceeds the limit. tablet_id: 479203, consumption: 15928614825, limit: 15790082457. Memory stats of top five tablets: 4258582(314M)4258578(272M)4258340(230M)2957546(190M)2957590(190M):  be:xxx.xxx.xxx.xxx"
}
errorLog: null
	at com.starrocks.data.load.stream.TransactionStreamLoader.prepare(TransactionStreamLoader.java:221)
	at com.starrocks.data.load.stream.v2.TransactionTableRegion.commit(TransactionTableRegion.java:247)
	at com.starrocks.data.load.stream.v2.StreamLoadManagerV2.lambda$init$0(StreamLoadManagerV2.java:210)
	... 1 more

除此之外,我们的业务场景就是会 更新 以往 的历史数据,且这样类似的任务有很多。我们的表结构是主键表。

分析

上述报错,其实是BE报出来的,每次进行数据更新的时候,SR都会加载对应的tablet对应的主键索引,导致我们这边的BE占用的内存比较大,如下所示:
在这里插入图片描述

。经过分析我发现我们这边的分区是以月维度划分的,而且bucket的个数为2,这样每次写入数据的时候,就会把一个月的的数据的索引加载到内存中,这样就会导致BE的内存占用越来越大,

PARTITION BY date_trunc("month",created_time)
DISTRIBUTED BY HASH(xxx) BUCKETS 2

所以我们进行了bucket调整,

ALTER TABLE xxxx DISTRIBUTED BY HASH(xx) BUCKETS 50;

调整之后,对比了一下BE所占用的内存,如下:
在这里插入图片描述

内存占用节约了5GB。

其他

可以通过如下命令查看 索引所占用的内存

curl -XGET -s http://BE:8040/metrics | grep  "update_mem_bytes"

curl -XGET -s http://BE:8040/metrics |grep 'update_primary_index_bytes_total'

具体的指标参考:StarRocks metrics

当Doris写入时出现 `Failed to commit kv txn [...] Transaction exceeds byte limit` 错误,意味着事务提交时超出了字节限制。可以从以下几个方面解决: ### 调整事务字节限制参数 可以通过调整 Doris 的相关参数来提高事务的字节限制。在 Doris 的配置文件中找到 `txn_max_bytes` 参数,适当增大该参数的值。例如,在 Doris 的 `fe.conf` 配置文件中添加或修改如下配置: ```plaintext txn_max_bytes = 10737418240 # 这里将限制调整为 10GB,可根据实际情况调整 ``` 修改完成后,重启 Doris 的 FE 节点使配置生效。 ### 拆分大事务 如果数据量较大,可以将一个大的事务拆分成多个小事务进行提交。例如,原本一次性插入大量数据的操作,可以分成多次插入,每次插入的数据量控制在事务字节限制之内。以下是一个 Python 示例代码,使用 `pymysql` 库向 Doris 插入数据: ```python import pymysql # 连接到 Doris conn = pymysql.connect( host='your_host', port=9030, user='your_user', password='your_password', database='your_database' ) cursor = conn.cursor() # 模拟大量数据 data = [ ('value1', 1), ('value2', 2), # 更多数据... ] # 拆分数据,每次插入 100 条 batch_size = 100 for i in range(0, len(data), batch_size): batch = data[i:i + batch_size] insert_sql = "INSERT INTO your_table (col1, col2) VALUES (%s, %s)" cursor.executemany(insert_sql, batch) conn.commit() cursor.close() conn.close() ``` ### 优化数据格式和内容 检查插入的数据是否包含不必要的大字段或冗余信息。例如,如果有文本字段包含大量的长字符串,可以考虑对其进行压缩或简化。另外,避免插入包含大量二进制数据的字段,如果确实需要存储二进制数据,可以考虑使用外部存储系统(如 HDFS),并在 Doris 中存储其引用信息。 ### 检查数据来源和处理逻辑 确保数据来源和处理逻辑没有产生异常大的数据块。例如,在数据采集或处理过程中,可能存在数据重复或错误拼接的情况,需要对数据进行清洗和过滤,去除不必要的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值