bufio.Scanner单行超长会panic,需显式调大缓冲区:先预分配底层数组,再设最大令牌长度;scanner.Bytes()返回共享切片需拷贝避免内存泄漏;解析JSON/CSV应使用专用Decoder/Reader而非字符串操作;并发channel缓冲需设限防OOM。

bufio.Scanner 单行超长直接 panic?必须显式扩容缓冲区
默认的 bufio.Scanner 只允许单行 ≤ 64KB,日志里嵌一段 base64 或导出的 JSON 行一超就崩: scanner: token too long。这不是 bug,是设计上防 OOM 的硬限制。
- 调大缓冲区不是“随便设大”,而是两步:先预分配底层切片(第一参数),再设最大令牌长度(第二参数),例如
scanner.Buffer(make([]byte, 64*1024), 10*1024*1024)表示底层数组 64KB、单行最多支持 10MB - 别用
math.MaxInt32—— 它会让 runtime 尝试分配几 GB 内存,实际还没读就 OOM - 如果行结构不规则(比如按空行或自定义分隔符切分),
bufio.Scanner的Split方法比硬扩缓冲更可控
scanner.Bytes() 返回的是共享切片,存下来就锁死整块缓冲区
scanner.Bytes() 不做拷贝,返回的是内部缓冲区的子切片。你把它塞进 map 或 []string,等于把整个缓冲区(通常是 4KB~1MB)钉在内存里,GC 清不掉。
- 需要保留内容时,显式拷贝:
data := append([]byte(nil), scanner.Bytes()...)或string(scanner.Bytes()) - 只做流式处理(统计、过滤、转发)时,直接用
scanner.Text()—— 它内部已拷贝,安全但略慢一点 - 别在闭包里捕获
line变量后长期持有,尤其别塞进全局 map 或未清空的 slice
逐行解析 JSON 或 CSV?别自己 split,用专用 Decoder/Reader
用 strings.Split(string(line), ",") 解析 CSV 或 json.Unmarshal([]byte(line), &v) 解析 JSON,会额外触发一次字节拷贝,且无法流式控制内存峰值。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- JSON Lines 场景:用
json.NewDecoder包一层strings.NewReader(line),比json.Unmarshal少一次[]byte → string → []byte转换 - CSV 场景:直接用
encoding/csv.Reader,它从底层io.Reader流式读,字段切分和转义都内置,设r.FieldsPerRecord = -1允许变长列 - 所有这些 Reader 都要求传原始
*os.File或bufio.Reader,别套bytes.Buffer或io.MultiReader—— 否则失去流式意义
并发读块时 channel 缓冲没设上限,内存很快就爆
把读取和处理拆成 goroutine 看似能提速,但 ch := make(chan []byte, N) 的 N 不设上限,等于开了一条内存泄洪道。处理函数含 DB 写入或 HTTP 请求时,未消费的块会在内存里越堆越多。
立即学习“go语言免费学习笔记(深入)”;
-
N建议 ≤ 3~5,比如ch := make(chan []byte, 3),表示最多缓存 3 块待处理数据 - 发送前必须深拷贝:
ch ,否则所有 worker 共享同一底层数组,内容互相覆盖 - worker 处理完立刻丢弃引用,别存进全局结构体或长期存活的 map —— 否则 GC 永远回收不了那块内存
真正卡住性能的不是代码逻辑,而是你是否让缓冲区复用、是否切断了对底层切片的隐式引用、是否在 fallback 场景下还依赖默认 32KB 缓冲——这三个点漏掉任意一个,内存占用就失控。

















