Go pprof 和 bcc,谁更能排查程序性能问题?

导读

本文介绍我们在以前排查海东青v1.4.0版本吞吐性能问题的过程。我们在对v1.4.0做查询QPS性能测试时发现:提高并发数无法提高吞吐,且CPU、IO利用率较低,网络也没到瓶颈。我们尝试使用Go pprof排查,没有取得效果,最终通过bcc才成功找到问题的原因(由于block cache配置的容量太小)。

通过本文,大家可以学习bcc的使用,感受bcc的强大。希望本文可以帮助大家排查性能问题、优化性能。此外,文末列举了一些发散性的问题请教大家,请多多指教。

全文2500余字,预计阅读时间5分钟。

本文章节简要大纲:

01. 介绍性能问题

02. 使用Go pprof排查问题

03. 使用bcc排查问题

04. 结语,以及一些发散性问题

01. 性能问题

在以前我们发布了海东青v1.4.0版本之后,测试同学准备对它进行查询QPS性能测试,希望知道海东青能够提供多大的QPS。

测试环境和方法如下:

1. 测试环境:由一台服务器、一台客户机组成,均为 48核、SSD、256G内存,它们同属一个千兆网络局域网。

2. 测试方法:在服务器上部署单节点海东青数据库服务,在客户机上使用我们开源的性能测试工具fcbench [1] 进行查询性能测试,使用select max(aqi) from city_air_quality where time >= '{now}'-30d and city = '{city}'作为查询语句模板。

性能测试结果为:

并发数 QPS CPU使用率 IO利用率
48

1426

55%

20%
128

1541

65%

22%
256

1564

65%

22%

 (本文为了方便探讨不同核数下的测试情况,CPU利用率使用单核计算方式。此外,测试过程中的网络没有达到瓶颈且当时监控已无法访问,因此不将其列出)

从结果可以看出两个现象:

  1. 在并发数提高后,QPS基本没有提升。

  2. CPU和IO利用率较低。

测试同学要我给个解释。

Go pprof 和 bcc,谁更能排查程序性能问题?_第1张图片

 

(听到这话我就头疼)

我可以说 “这正是因为我们程序写得好,能够节约使用CPU和IO,所以QPS没有提升” 么?

那他估计要回复我六个字:你真是个人才。

02. 使用Go pprof排查

不过,以前教我Go语言的大佬确实是一个人才。那时我见过他使用go pprof mutex [2]分析程序性能问题。

于是我依葫芦画瓢,在性能测试的过程中使用下面的命令得到火焰图:

go tool pprof -http :18080 http://海东青服务器IP:6060/debug/pprof/mutex

Go pprof 和 bcc,谁更能排查程序性能问题?_第2张图片

 从上面的火焰图可以看出,整个“耗时”全都花在了互斥锁上面。互斥锁所在模块伪代码如下:

func (t *Table) Get(k1, k2, k3 string) Value {
  t.Lock()
  defer t.Unlck()

  if m1, ok := t.map[k1]; ok {
    if m2, ok := m1[k2]; ok {
      if node, ok := m2[k3]; ok {
        c.list.MoveToFront(node)
        return node.Value
      }
    }
  }
  return nil;
}

func (t *Table) Put(k1, k2, k3 string, v Value) {
  t.Lock()
  defer t.Unlck()

  listNode := c.list.PushFront(ListNode{Value:v})

  if m1, ok := t.map[k1]; ok {
    if m2, ok := m1[k2]; ok {
      if v, ok := m2[k3]; ok {
        return
      }
      m2[k3] = listNode
    }
  }
  return;
}

可以看到这两个函数里面的临界区非常大,猜测性能问题应该与此有关。

于是尝试优化:将多层map改为一层map,以减少map查找次数。

但改动之后的QPS仍然没有提升。

此时,我们直接采取极端的做法:直接去掉锁(生气了)。这次得到的火焰图完全没有之前火焰图中的锁了(只剩下一个与查询流程完全无关的锁),但QPS还是没有提升,CPU利用率也只有64%。

看起来Go pprof不得行啊·····

现在(编写本文时)看来,并结合go-profiler-notes [3]的描述,当初是否应该使用 go pprof block来分析?还请用过它的朋友指正。

后来我又在程序中引入fgprof [4],仍然无济于事。

路在何方?

03. 使用bcc排查问题

当我们为这个性能问题焦头烂额的时候,路过我们工位的大佬听到了我们的探讨,说了一句:

你们落后了一个版本,现在bcc才是最厉害的。

我心里想,大佬是在跟我们说话么?他说的bcc应该是一个游戏英雄吧?

大佬在我的浏览器上打开:https://github.com/iovisor/bcc,让我好好学习下。(尴尬了,看来以后得去小黑屋悄悄讨论问题)

后面,大佬又发了他偶像brendangregg的两篇博文 [5] [6] 让我学习。

Go pprof 和 bcc,谁更能排查程序性能问题?_第3张图片

于是我们怀着信任大佬(其实是死马当活马医?)的心态尝试bcc。

首先我们需要安装BCC和FlameGraph:

1. 建议机器CPU平台为x86,操作系统为Ubuntu 20+ (因为其他CPU架构或者OS通常会在运行bcc时报错)。

2. 按照 https://github.com/iovisor/bcc/blob/master/INSTALL.md#ubuntu---source 编译构建bcc,但务必在编译之前先执行如下两个步骤:

    2.1. clone bcc代码之后请checkout 到 v0.24.0 tag !(因为最新的master分支可能存在编译失败的问题)。

    2.2. 执行 git submodule init和git submodule update进行 clone libbpf 依赖。

3. Clone  https://github.com/brendangregg/FlameGraph 安装FlameGraph 。

 如果之后运行bcc命令报错,可以在 https://github.com/iovisor/bcc/blob/master/FAQ.txt 或 https://github.com/iovisor/bcc/issues 里查找问题解决方法

由于之前的测试服务器无法满足bcc要求,因此我们在一台满足bcc要求的32核机器上部署了bcc,并直接使用它作为海东青服务器,再次进行测试(只测试48并发),测试结果为:

  1. CPU利用率为70%

  2. QPS为 3200

在测试期间,我们使用如下命令收集off-CPU堆栈:

sudo /usr/sbin/offcputime-bpfcc -p `pgrep -nx fctsdb` -df > out.stack

按Ctrl+C 之后终止收集,会得到out.stack文件,然后使用如下命令生成火焰图:

FlameGraph/flamegraph.pl --color=io --countname=us out.stack > slow.svg

查看火焰图可以看到如下堆栈:

Go pprof 和 bcc,谁更能排查程序性能问题?_第4张图片

 放大红色方框内部分:

Go pprof 和 bcc,谁更能排查程序性能问题?_第5张图片

 可以看到readIntegerBlock 函数在读取block之后insert到block cache和lookup block cache中的临界区占比较大,其占比也较为符合测试期间CPU只能利用到70%的表现。

临界区加锁次数是由于block cache miss较多导致。

因此,我们在fcshell里使用如下命令加大block cache容量(修改前为64M):

set variable "data-block-cache-memory-size" = '1000M'
// 此变量为系统级变量,需要重启海东青生效

我们重启海东青数据库之后重新测试,结果为:

  1. CPU利用率为93%

  2. QPS为 4800

再次生成火焰图:

Go pprof 和 bcc,谁更能排查程序性能问题?_第6张图片

 我们可以看到之前占比较大的部分已经占比很小了。而且QPS和CPU都得到了提升。(请叫我调参工程师)

至此,我们通过bcc终于解决了性能测试中某些查询在加大并发之后QPS无法提升且CPU压不满的问题。

04. 结语

bcc工具确实如大佬所说,十分管用,准确定位到了我们的性能问题,但我有几个问题想请教大家:

  1. Go pprof和bcc分别适用于哪些场景呢?
  2. 是否所有Go程序的性能问题都能通过bcc来排查呢?
  3. 是否有其他的Go pprof使用姿势能帮助我们排查本文中的性能问题呢?
  4. 如果是Go程序启动过程慢,该如何排查问题呢?Go pprof可以么?
  5. 有没有出Go pprof和bcc之外的其他方式来排查性能问题呢?
  6. 对于上一篇解决吞吐问题 文章中遇到的问题,是否可以在分析出问题与模块响应时间有关系之后直接用bcc或者go pprof来排查出问题模块呢?

欢迎大家在评论区留言或加群探讨。

当然,由于作者技术水平有限,文中介绍的排查过程难免出现错误,还请读者批评指正。

最后,感谢大家的阅读,谢谢大家。

参考文档

  1. https://github.com/falcontsdb/fctsdb-bench,它是海东青基于开源大幅修改的时序数据库性能测试工具,后续会对其进行专门介绍

  2. https://pkg.go.dev/net/http/pprof 

  3. https://github.com/DataDog/go-profiler-notes/blob/main/block.md#relationship-with-mutex-profiling

  4. https://github.com/felixge/fgprof

  5. https://www.brendangregg.com/offcpuanalysis.html

  6. https://www.brendangregg.com/FlameGraphs/offcpuflamegraphs.html

关于海东青

海东青时序数据库是一款高性能的支持跨平台、国产化、主从高可用、SQL的时序数据库,兼容InfluxDB、MySQL协议。

海东青官网:

https://fctsdb.rockontrol.com/

海东青下载:

https://github.com/falcontsdb/release

Docker使用:

docker pull falcontsdb/fctsdb:free_2.2.0

Go pprof 和 bcc,谁更能排查程序性能问题?_第7张图片

你可能感兴趣的:(海东青数据库,golang,运维)