
在处理大量数据(如 10 万行文本写入数据库)时,盲目启动等量 goroutine 会导致资源耗尽;应采用固定工作池模式,通过 channel 分发任务并控制并发数(如 100),兼顾性能与稳定性。
在处理大量数据(如 10 万行文本写入数据库)时,盲目启动等量 goroutine 会导致资源耗尽;应采用固定工作池模式,通过 channel 分发任务并控制并发数(如 100),兼顾性能与稳定性。
Go 语言中,无节制地启动数万个 goroutine(例如对每行文本启动一个 go saveToDB(l))看似简单,实则存在严重隐患:它会迅速耗尽内存、压垮数据库连接池、触发操作系统级调度开销,甚至导致程序崩溃或服务不可用。真正的并发控制不在于“多”,而在于“稳”——即维持合理、可控的并发度。
推荐方案是构建一个 固定大小的工作池(Worker Pool),其核心思想是:预启动 N 个长期运行的 worker goroutine,所有任务通过 channel 统一分发,worker 持续从 channel 中接收并执行任务,直到 channel 关闭。
以下是完整、可直接运行的实现示例:
package main
import (
"fmt"
"strings"
"sync"
)
// 模拟实际数据库保存逻辑(请替换为真实 DB 调用)
func actuallySaveToDB(line string) error {
// 示例:简单打印 + 模拟 I/O 延迟
fmt.Printf("Saving: %s\n", line)
return nil
}
// Worker 函数:持续从 channel 读取任务并执行
func saveToDB(ch <p>✅ <strong>关键设计说明:</strong> </p>
- ch 是一个 无缓冲或带缓冲的 channel,作为任务队列;带缓冲(如 make(chan string, 1000))可缓解生产者/消费者速度差异,避免主 goroutine 阻塞。
- saveToDB 使用 for line := range ch 语义:channel 关闭后循环自动终止,无需额外标志位或 select{default:} 判断。
- defer wg.Done() 确保无论函数如何退出(含 panic),worker 都能正确通知 WaitGroup。
- 主流程清晰分离:启动池 → 投递任务 → 关闭通道 → 等待完成,逻辑内聚、易于维护。
⚠️ 注意事项:
- 若 actuallySaveToDB 可能失败,建议在 worker 内增加错误日志或重试机制(如指数退避),避免单点失败导致静默丢失数据。
- 数据库操作需注意连接复用(使用 *sql.DB 的连接池),切勿在每个 goroutine 中新建连接。
- 并发数(concurrency)并非越大越好——应根据目标数据库的连接上限、CPU/IO 密集度、网络延迟等压测调优,通常 10–100 是安全起点。
- 对超大文件(如 GB 级),建议改用流式读取(bufio.Scanner)而非 ioutil.ReadFile,防止内存溢出。
综上,工作池模式是 Go 生产环境中控制并发的标准实践:它简洁、健壮、可扩展,且天然契合 Go 的 CSP 并发哲学——“通过通信共享内存”。











