Go 语言虽无内置 ETL 框架,但可用标准库与 sqlx 等包构建健壮 ETL:需显式管理数据库连接与扫描、分页查询防内存溢出、合理配置连接池、自主维护 HTTP 分页状态并指数退避重试。

Go 语言本身没有内置的 ETL 框架,但用标准库 + 少量成熟包就能写出健壮、可控、可监控的 ETL 抽取任务——关键不在“有没有框架”,而在如何组织数据流、错误恢复和资源边界。
用 database/sql + sqlx 做关系型数据库抽取时,必须显式控制连接和扫描行为
很多人直接 rows, err := db.Query("SELECT ...") 然后 for rows.Next(),结果在大数据量下内存暴涨或连接卡死。根本原因是:rows 是懒加载,但默认不设 fetch size,且未关闭时连接一直被占用。
- 用
sqlx.DB.Queryx或原生db.Query后,务必在循环结束后调用rows.Close() - 对大表,改用带
LIMIT+OFFSET的分页查询,或基于自增主键/时间戳的游标分片(例如WHERE id > ? ORDER BY id LIMIT 1000) - 避免用
sqlx.Select一次性加载全部结果到内存;改用sqlx.StructScan配合rows.Next()流式解码 - 连接池要调:设置
db.SetMaxOpenConns(10)和db.SetMaxIdleConns(5),防止源库被打爆
从 HTTP API 分页拉取数据时,别信文档里的 “next_url” 字段
很多 REST API 返回的 next_url 是临时签名链接,过期快;或者压根没实现幂等,重试时可能漏数据或重复拉。更可靠的方式是自己维护分页上下文。
- 优先用查询参数分页(如
?page=2&per_page=100),把page和last_id存进本地状态文件或 SQLite 表 - 每次请求前检查响应状态码和
Content-Range/X-Total-Count头,验证分页完整性 - 对失败请求,按指数退避重试(用
time.Sleep(time.Second ),最多 3 次,超时后记录 <code>failed_fetch: url=..., status=502, retry=3 - 不要把 token 放 query 里传,用
req.Header.Set("Authorization", "Bearer ...")
encoding/csv 解析带引号、换行、BOM 的 CSV 文件容易出错
真实业务 CSV 经常含 Excel 导出的 BOM 头、字段内换行符("foo\nbar")、双引号转义("a""b")。直接 csv.NewReader 会 panic 或截断。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
- 先用
bytes.TrimPrefix(data, []byte("\xef\xbb\xbf"))去 BOM - 创建 reader 时设
reader.Comma = ';'(如果分隔符不是逗号) - 必须调
reader.FieldsPerRecord = -1允许变长字段,否则遇到换行符就报expected 5 fields, got 3 - 用
record, err := reader.Read()而非ReadAll(),防止整文件进内存 - 字段值要用
strings.TrimSpace(record[i])清空首尾空白,Excel 导出常带空格
ETL 抽取任务必须自带 checkpoint 和 resume 能力
没人能保证一次跑完。网络抖动、目标库短暂不可用、进程被 OOM kill——这些都会中断任务。硬编码从头重跑既耗资源又难定位问题。
- 每处理完一批(比如 1000 条),写一条 checkpoint 到本地
checkpoint.json:包含source="mysql://...", table="orders", last_id=123456, timestamp="2024-05-20T10:30:00Z" - 启动时先读该文件,若存在且时间在 1 小时内,就从
last_id继续;否则清空并重新开始 - 别用内存变量存进度,崩溃即丢失;也别依赖数据库自身事务日志——你抽的是别人家的库,没权限
- checkpoint 文件本身要
os.WriteFile+fsync(用file.Sync()),确保落盘
最易被忽略的一点:所有外部 I/O(DB 查询、HTTP 请求、CSV 读取)都要设明确超时,且超时错误必须区分对待——连接超时可重试,读取超时可能意味着数据异常,得告警而不是静默跳过。

















