上线新功能导致服务器cpu暴涨。经分析为map assign过于频繁,导致频繁的内存malloc与memorymove,耗尽cpu。
使用go tool定位过程记录
1. 获取进程内函数执行统计
go tool pprof http://10.139.252.109:9360/debug/pprof/profile
top10的函数如下:
2. runtime.mapassign占了40.25%的cpu时间,初步判断是map使用不当导致。
使用web命令获取可视化执行路径
3. 下载到windows本地,用chrome打开,如下
注意中间最粗的条线。耗时75.77s(统计总共收集了102秒的运行记录),占了70%多的cpu执行时间。一直往下看可以看到主要耗在这两个函数上。而且这两个函数会导致大量的垃圾回收(因为有大量的map指针)
这两个函数有大量的runtime.mapassign。可见问题肯定在map的使用不当上。
4. 分析代码
原来的代码如下:
发现两个问题
a:) for循环里每次map分配都会申请新的内存,而且会带来频繁的扩容(导致大量内存move)。
b:) map[string]map[int]int 这个数据结构的定义有问题。经过分析上下文,发现map[int]int这个数据结构的用处只是存两个值而已。所以没必要用map[int]int
5. 解决方法
a:) 因为map是指针类型,所以垃圾回收会扫描(垃圾回收只会扫描指针类型数据)。根据上面的分析,map[int]int可以换成struct{a int, b int}。 struct是值类型,所以就避免了大量的垃圾回收对cpu的消耗,同时减少了堆内存的分配。
修改后的数据结构为:
struct Unit{t int, d int}
map[string]Unit
b:) 针对第一个问题:
因为代码中map的大小是可以提前算出来的,所以提前分配大小。
第一行代码改为
clkFromatFqMap := make(map[string]Unit, len(fqMap))
问题解决,cpu恢复正常。