应限制并发度并协调完成信号,用信号量控制goroutine数量、预分配切片或通道收集结果、显式设置连接池上限、捕获panic统一处理错误。

goroutine分页查询为什么不能直接for循环起一堆
直接在for循环里用go启动大量goroutine查数据库,大概率会触发连接池耗尽、SQL注入风险(若页码拼接进SQL)、或结果乱序难聚合。Go的并发不是“越多越快”,而是要控制并发度+协调完成信号。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
semaphore(如golang.org/x/sync/semaphore)限制同时执行的goroutine数量,常见设为runtime.NumCPU()或数据库连接池大小 - 每页查询应封装为独立函数,接收
page和pageSize参数,返回[]T和error - 避免在goroutine里直接操作共享切片——要用
chan []T或带锁的sync.Map收集结果 - 别用
time.Sleep等“假等待”代替同步,必须用sync.WaitGroup或context.WithTimeout收口
如何安全地把分页结果汇总到一个切片
并发查询后合并结果,最易出错的是竞态写入和顺序错乱。页码本身不保证执行先后,但业务常需按page=1,2,3...顺序拼接数据。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
make([][]T, totalPages)预分配结果槽位,每个goroutine写入对应索引:results[page-1] = data - 如果页码可能跳缺(如某页无数据),改用
map[int][]T+sync.RWMutex保护写入 - 通道方式更稳妥:开
ch := make(chan resultItem, totalPages),每个goroutine发resultItem{Page: 2, Data: [...]},主goroutine按Page字段排序后合并 - 切记关闭channel——在
WaitGroup.Done()后关闭,或用defer close(ch)配合sync.Once
数据库查询本身是否支持并发?关键看驱动和连接池
像database/sql的DB对象是并发安全的,但底层连接池大小决定了真实并发上限。默认MaxOpenConns=0(不限制),实际会受OS文件描述符和数据库配置制约。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 显式设置
db.SetMaxOpenConns(20),并确保它 ≥ 你允许的最大goroutine数 - PostgreSQL推荐用
pgx/v5替代lib/pq,它原生支持连接复用和批量查询,QueryRow调用开销更低 - MySQL注意
max_connections服务端配置,避免ERROR 1040 (HY000): Too many connections - 如果分页SQL含
ORDER BY且字段无索引,高并发下容易触发Using filesort,拖慢整体响应——先优化SQL再上并发
超时与错误如何统一处理
一个分页失败不该让整个请求失败,但也不能静默丢弃。goroutine内panic不传回主goroutine,必须显式捕获。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 每个goroutine用
defer func(){ if r:=recover(); r!=nil { errCh - 用
context.WithTimeout(ctx, 30*time.Second)包装所有查询,超时后cancel()通知其他goroutine退出 - 错误通道
errCh := make(chan error, totalPages)带缓冲,避免goroutine阻塞;主流程用select监听errCh和doneCh - 不要用
log.Fatal或os.Exit——这会让整个服务崩掉,只该返回error供上层决策重试或降级
真正麻烦的不是启动goroutine,而是当某页查到一半数据库连接断了,或者第7页超时了但第8页成功了,这时候怎么让结果既完整又及时。这些边界情况比并发本身更消耗调试时间。


















