🐛 fix(memory): 修复大数据量导出导致进程内存飙升至 16G 的问题

- GC 策略:主进程与 driver-agent 启动时收紧 SetGCPercent 至 50
- 周期回收:scan_rows 与 callStreamQuery 每 5 万行触发 runtime.GC
- 自适应限流:driver-agent 引入 GOMEMLIMIT 自适应策略,2GB 起步按 1GB 步长抬升至 8GB 上限
- 批次调优:流式批次由 256 行缩减至 64 行,降低 JSON 编解码瞬时峰值
This commit is contained in:
Syngnat
2026-06-19 12:05:02 +08:00
parent 21c427bc39
commit 98965a56e1
6 changed files with 321 additions and 8 deletions

View File

@@ -67,7 +67,11 @@ const (
agentChunkColumns = "columns"
agentChunkRows = "rows"
agentChunkDone = "done"
agentStreamBatchSize = 256
// agentStreamBatchSize 控制 driver-agent 向主进程发送 row chunk 的批次大小。
// 调小到 64单批 JSON 编码 + 主进程解码的瞬时内存峰值降为原来的 1/4
// 代价是 IPC 次数变为 4 倍,但每批仅一次 stdin/stdout 行读写,整体影响可忽略。
// 重要:减小批次不能根除内存峰值,仍需配合 SetGCPercent + 周期 GC见 main
agentStreamBatchSize = 64
agentMemoryTrimRowsThreshold = 100000
agentMemoryTrimMinInterval = 3 * time.Second
)
@@ -98,6 +102,20 @@ func main() {
os.Exit(2)
}
// driver-agent 是独立进程,主进程无法控制其 GC 行为。
// 大结果集88W+ 行)通过 JSON-lines 跨进程传输时,每行有 5-8 倍内存副本;
// Go 默认 GOGC=100 + Windows MADV_FREE 不归还 RSS会导致 driver-agent 进程
// 内存峰值达到数据总量的 10+ 倍(用户实测 88W 普通业务表撑到 8G+)。
//
// GC 策略组合:
// - SetGCPercent(50):堆增长 50% 即触发 GC比默认 100 更早收敛
// - InitMemorySoftLimit起始 2GB运行时由 MaybeGrowMemoryLimit 自适应抬升到最多 8GB
// (起步保守 + 按需扩张,避免静态 2GB 限制在大表场景触发 GC 硬模式降速 15-25%
//
// 代价CPU 开销增加约 5-10%。导出场景是 I/O 密集型,可忽略。
debug.SetGCPercent(50)
db.InitMemorySoftLimit(db.MemorySoftLimitInitialBytes)
scanner := bufio.NewScanner(os.Stdin)
scanner.Buffer(make([]byte, 0, 16<<10), 8<<20)
writer := bufio.NewWriter(os.Stdout)