归档前必须确认数据分片策略,避免内存溢出或超时;需手动控制批次大小,结合主键做范围分片或游标分页;写入时限制并发、解耦流与写入;保障原子性与断点续传,依赖可靠存储记录进度;强化可观测性,监控耗时、积压与失败率。

归档前必须确认数据分片策略
海量数据不拆分直接处理,必然触发内存溢出或超时。Go 里没有“自动分批”的银弹,sql.Rows 读取、bufio.Scanner 逐行解析、或从 Kafka/文件流拉取数据时,都得自己控制批次大小。
常见错误是用 SELECT * 加 rows.Scan() 一次性加载全部结果——哪怕只有 100 万行,每行 2KB,就占 2GB 内存。实际应结合业务主键(如 created_at 或 id)做范围分片:
- 按时间切片:
WHERE created_at BETWEEN '2024-01-01' AND '2024-01-31',每次查 1 天数据 - 按 ID 分页:
WHERE id > ? ORDER BY id LIMIT 10000,用上一批最后id作为下一批起点 - 避免 OFFSET:大数据量下
LIMIT 10000 OFFSET 1000000效率极低,改用游标式分页
写入归档目标时别硬扛并发
往对象存储(S3/MinIO)、冷备数据库(ClickHouse/Parquet 文件)或压缩包写数据,高并发写容易打满连接数或触发限流。Go 的 sync.Pool 和 chan 控制并发不是万能解法,关键在资源绑定和背压设计。
例如用 io.Pipe 把数据流和归档写入解耦,配合 semaphore 控制同时打开的文件句柄数:
// 限制最多 5 个并发归档任务
sem := semaphore.NewWeighted(5)
for range dataCh {
if err := sem.Acquire(ctx, 1); err != nil {
continue
}
go func() {
defer sem.Release(1)
writeArchiveFile(data)
}()
}
- 写 S3 时别复用同一个
*s3.Client实例而不设MaxIdleConnsPerHost,否则 HTTP 连接池耗尽 - 生成 Parquet 文件用
apache/parquet-go,但注意其Writer不支持并发写,每个文件必须单协程写入 - 压缩归档(zip/tar.gz)时,
archive/zip.Writer必须在写完后调用Close(),否则文件末尾校验失败
归档过程必须带原子性与断点续传
跑一半崩溃,重跑又重复归档,或者部分成功部分失败,是海量归档最常踩的坑。Go 没有内置事务跨存储引擎能力,得靠状态标记 + 幂等写入兜底。
典型做法是在源库加一张 archive_progress 表,记录已归档的最大 id 或时间戳,每次归档前先查、归档后更新:
- 归档前用
SELECT MAX(id) FROM archive_progress WHERE job_id = ?获取断点 - 归档成功后用
INSERT ... ON CONFLICT (job_id) DO UPDATE SET last_id = EXCLUDED.last_id(PostgreSQL)确保幂等 - 不要依赖本地文件记录断点——机器宕机就丢,必须落库或存到 etcd 等可靠存储
- 归档目标如果是对象存储,文件名带上时间戳+哈希(如
archive_20240101_abc123.parquet),避免覆盖或冲突
监控归档延迟与失败率不能只看日志
日志里 “done” 打得多不代表真归档成功。真实生产环境要埋点测三个核心指标:单批次耗时、积压条数、失败重试次数。Go 标准库的 expvar 或集成 prometheus/client_golang 是轻量选择。
- 用
prometheus.NewHistogramVec统计每批处理耗时:archive_duration_seconds{type="mysql_to_parquet"} - 失败时别只
log.Printf,要触发prometheus.CounterVec计数,并设置告警阈值(如 5 分钟失败率 > 1%) - 积压监控靠上游消息队列 offset 差值,或定期查
SELECT COUNT(*) FROM source_table WHERE archived = false,别等 OOM 才发现卡住了
真正难的不是写完代码,而是让归档模块在持续跑三个月后还能准确告诉你:现在落后多少数据、哪天的某批次卡在哪个环节、重试是否真生效了——这些全靠可观测性设计,不是加个 fmt.Println 就能解决的。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











