程序功能

该程序的主要功能是将文件中的数据导入clickhouse数据库。

[问题描述]

服务器内存定期耗尽

[问题分析]

因为是用go语言开发的,所以使用了业界流行的工具pprof。

参考网址:https://cizixs.com/2017/09/11/profiling-golang-program/

使用工具和想法:

1)先修改源代码

  1. 安装工具观察

3)基于刀夹现象的分析

4)修复内存缺陷代码,然后根据分析结果修复内存泄漏

  1. 发布代码进行跟踪分析

\u003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003du003d u003d

1)修改代码:

您需要编写几行代码才能使用此工具收集数据。

1 //参考pprof

2 导入“net/http”

3 导入_ "net/http/pprof"

4

5 //主函数中的新端口监视器

6 //由于我的代码是一个守护进程,这里有一个新的方法来开启一个监控协议防止阻塞

7 函数 main(){

8 去 func(){

9 http.ListenAndServe("0.0.0.0:80", nil)

10 }()

11 //其他代码

12 ...

13 }

以上源码修改后,重新部署到服务器观察内存情况;

内存仍然可以继续消耗内存而不释放它。

2)在服务器上安装golang pprof程序来收集数据。

安装方法:yum install golang pprof

  1. 使用命令转储堆。这个工具的好处是dump之后可以直接生成pdf或者png

1 [root@centos ~]# go tool pprof /root/clickhouse_runner/clickhouse_mssql_e

tl http://0.0.0.0:80/debug/pprof/heap

2 通过 HTTP 从 http://0.0.0.0:80/debug/pprof/heap 获取配置文件

3 保存在 /root/pprof/pprof.clickhouse_mssql_etl.alloc_objects.all 中的配置文件

oc_space.inuse_objects.inuse_space.012.pb.gz

4 文件:clickhouse_mssql_etl

5 类型:inuse_space

7 进入交互模式(命令输入“help”,选项输入“o”)

8 (pprof) pdf

9 在profile003.pdf中生成报告

10 (pprof) 退出

11 [root@centos ~]

从上面的heap可以看出,代码中的主内存使用是由clickhouse驱动的,而调用Clickhouse的部分并没有释放内存(golang的内存gc逻辑后来仔细分析了,因为gc有滞后速度,而进口商创建速度更快,因此速度更慢)。

4)找到内存泄漏的源头,开始修改代码

修改前的源代码:

1 连接,错误 :u003d sql.Open("clickhouse", connstring)

2 如果错误!u003d nil {

3 返回错误

4 }

5 load_start :u003d time.Now()

6 笔交易,错误:u003d connect.Begin()

7 如果错误!u003d nil {

8 log.Println(full_filename, "begin err", err)

9 返回错误

10 }

11 stmt, err :u003d tx.Prepare("insert ... values....")

12 如果错误!u003d nil {

13 log.Println(full_filename, "preare err", err)

14 返回错误

15 }

16 _, 呃 :u003d stmt.Exec(...)

17 如果是 !u003d nil {

18 log.Println("err", is)

19 }

20 er2 :u003d tx.Commit()

21 如果 er2 !u003d nil {

22 log.Println(db_view, "err", er2)

23 }

24 stmt.Close()

25 连接。关闭()

//总结一下通过分析自己的代码和clickhouse驱动代码有两种改善内存泄漏的方法

泄漏:

一个。修改clickhouse中的驱动代码,执行代码后立即重置内存,而不是等待gc处理:

1 func (stmt *stmt) 关闭() 错误 {

2 stmt.ch.logf("[stmt] 关闭")

3 //再次添加回收内存数据

4 如果 stmt.ch.block !u003d nil {

5 stmt.ch.block.Reset()

6 }

7 返回零

8 }

湾。直接释放stmt对象,使用gc的自动回收(考虑这种方式会更合理)

1 stmt.Close()

2 连接。关闭()

3 //添加直接stmt,将object连接到nil

4 //清除内存

5 stmt u003d 无

6 笔 u003d 无

7 连接 u003d 无

修改后完整代码:

1 连接,错误 :u003d sql.Open("clickhouse", connstring)

2 如果错误!u003d nil {

3 返回错误

4 }

5 load_start :u003d time.Now()

6 笔交易,错误:u003d connect.Begin()

7 如果错误!u003d nil {

8 log.Println(full_filename, "begin err", err)

9 返回错误

10 }

11 stmt, err :u003d tx.Prepare("insert ... values....")

12 如果错误!u003d nil {

13 log.Println(full_filename, "preare err", err)

14 返回错误

15 }

16 _, 呃 :u003d stmt.Exec(...)

17 如果是 !u003d nil {

18 log.Println("err", is)

19 }

20 er2 :u003d tx.Commit()

21 如果 er2 !u003d nil {

22 log.Println(db_view, "err", er2)

23 }

24 stmt.Close()

25 连接。关闭()

26

27 //***** 为 gc 清除内存 ******

28 stmt u003d 无

29 笔 u003d 无

30 连接 u003d 无

31 //////////////////////////////////////// //////////////////////////////

5)发布修改后的代码,观察一下,发现系统内存可以正常回收和释放

[结论]

经过这次Go golang调试,真正的原因是gc内存没有及时释放,存在lag(通过其他服务器观察,压力低时内存可以正常释放)。

所以最好的做法是通过设置object obj u003d nil来告知GC,当涉及到golang使用大对象或者频繁创建内存的时候,我真的不再使用内存块了,这样GC就可以快速回收,减少迭代gc。

另外,这种方式可以应用于java、c#等语言,它们都有类似的问题。