一次线上报表导出的功能异常无法使用,看监控日志,服务器内存有段时间占用高
除内存异常,其他组件都没发现问题,怀疑OOM导致服务挂了,查询服务运行时间,发现确实是重启了.
1 2 3 4 5
| ps -aux | grep "xxxx"
ps -o lstart,etime -p xxx
|
go pprof 分析内存占用
分析异常api的功能主要是excel的读写(用的excelize这个库),便用prof测试下内存占用,这里在测试用例读取一个几万行的excel文件(.xlsx),使用go的工具pprof记录内存占用
从.xlsx文件与.csv文件读取对比看,excelize与xlsx读取xlsx文件,比使用encoding/csv包读取csv文件更占内存,相差在2至3倍,前者平均需要4,5GB内存,后者仅占1.7GB左右.
综上,没有特殊需求,简单的表格文件保存可以优先选择csv
文件大小
以下测试使用同样内容的文件,8w行,csv:30.1MB,xlsx:18.7MB
go pprof使用
使用命令go进行benchmark测试并保存mempprof
1
| go test -benchmem -run=^$ -bench ^BenchmarkExcelizeLoadXlsx$ github.com/xxxxx -memprofile mem_xlsx.pprof
|
使用命令go查看pprof文件,会启动一个server支持在web页面查看
1
| go tool pprof -http=:9999 mem_xlsx.pprof
|
xlsx读取.xlsx文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| func LoadXlsx(filepath string) []*xlsx.Row { f, err := xlsx.OpenFile(filepath) if err != nil { return nil } fmt.Println("sheets:", len(f.Sheets)) if len(f.Sheets) == 0 { return nil } s := f.Sheets[0] return s.Rows }
func BenchmarkLoadXlsx(b *testing.B) { filePath := "./80000.xlsx" for i := 0; i < b.N; i++ { all := LoadXlsx(filePath)
if all == nil { return } } }
|
excelize读取.xlsx文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import ( "testing"
"github.com/xuri/excelize/v2" )
func ExcelizeLoadXlsx(filepath string) [][]string {
f, err := excelize.OpenFile(filepath) if err != nil { return nil } defer f.Close()
rs, err := f.GetRows("Sheet1") if err != nil { return nil } return rs }
func BenchmarkExcelizeLoadXlsx(b *testing.B) { for i := 0; i < b.N; i++ { filePath := "./80000.xlsx" all := ExcelizeLoadXlsx(filePath)
if all == nil { return } } }
|
encoding/csv读取csv文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import ( "encoding/csv" "log" "os" "testing" "time"
"github.com/google/uuid" )
func ReadCsv(filepath string) [][]string { opencast, err := os.Open(filepath) if err != nil { log.Println("csv文件打开失败!") return nil } defer opencast.Close()
ReadCsv := csv.NewReader(opencast)
readAll, err := ReadCsv.ReadAll() if err != nil { return nil } return readAll }
func BenchmarkLoad(t *testing.B) { filePath := "./80000.csv" for i := 0; i < b.N; i++ { all := ReadCsv(filePath)
if all == nil { return } } }
|