背景
- excel批量导入数据功能导入了5w数据,出现内存剧增的现象,最高达288M,服务OOM,最后降到149M
排查过程
第一次
-
读excel,也是5w的数据,内存70+
-
根据日志可以定位到
rows, err := fillExcelFile.GetRows(sheetName)这行代码导致内存突然增加,GetRows是第三方包的一个函数func GetExcelRows(infra *middleware.Infra, ossFileData []byte) ([][]string, error) { ... fillExcelFile, err := excelize.OpenReader(reader) ... rows, err := fillExcelFile.GetRows(sheetName) ... return rows, nil } -
用golang pprof
-
在单元测试中加入代码
import _ "net/http/pprof" go func() { http.ListenAndServe("0.0.0.0:8080", nil) }() -
在浏览器中输入
http://localhost:8080/debug/pprof/可以看到一些heap、goroutine信息 -
go tool pprof -inuse_space http://127.0.0.1:8080/debug/pprof/heap-inuse_space:查看进程正常使用的内存信息;与之对应的还有-alloc_space:查看总分配的内存信息 -
top10;prepareSheetXML方法直接耗了70M
-
-
5w的数据,耗了70M,还能接受,这次就没有纠结太多。
第二次
-
校验数据,5w不合法的数据加上样式,写入excel并返回。到了写这一步,内存直接飙到了288M。
-
根据日志,基本可以定位到了问题点,又是excel这个第三方包,在循环里SetSheetRow。
func (s *student) setInvalidFieldWarningStyle(templateData []byte, values [][]interface{}) *excelize.File { f, err := excelize.OpenReader(bytes.NewReader(templateData)) ... for i, value := range values { ... err := f.SetSheetRow(sheetName, startRow, &setValue) ... } return f } -
上GitHub看了issue。果然,好多反映内存问题。
-
还是用golang pprof分析了一下
-
看了一下该第三方的包的文档,换成了流的方式写
func (s *student) setInvalidFieldWarningStyle() error { streamWriter, err := file.NewStreamWriter(importservice.TemplateExcelSheetName) ... for i, param := range s.inValidParams { ... for _, column := range param.InvalidColumn { ... startRow := fmt.Sprintf("A%d", i+importservice.TemplateExcelStartRow) err := streamWriter.SetRow(startRow, tempRows) ... } return nil } -
果然,内存降下来了
总结
- 用golang pprof工具去分析内存,定位问题,才能进行下一步的优化