技术分享 | ClickHouse GDB 调试笔记

作者:xuty

本文来源:原创投稿

*爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。


一. 背景

记录下第一次使用 GDB 调试 ClickHouse 源码的过程,这里仅仅是通过简单的调试过程了解 ClickHouse 内部的机制,有助于解决疑惑,代码小白,有错误见谅。

二. 调试问题

调试 ClickHouse 主要是为了解决个人遇到的一个实际问题,下面先描述下这个问题:

  1. 通过 clickhouse 自带的mysql表函数导入全量数据时(这里建了一张测试表memory_test50w行数据56G),因为超过最大的内存限制(CK服务器36G内存),导致了如下报错。
localhost :) insert into `test`.`memory_test`  select * mysql('192.168.1.1:3306','test','memory_test','root','xxxx');                                                  

Received exception from server (version 20.8.12):
Code: 241. DB::Exception: Received from 127.0.0.1:9000. DB::Exception: Memory limit (total) exceeded: would use 35.96 GiB (attempt to allocate chunk of 17181966336 bytes), maximum: 28.14 GiB: While executing SourceFromInputStream. 
  1. 大致过程就是 ClickHouse 会将 mysql 的数据读入内存然后批量写入,这里涉及到一个 buffer 的问题,ClickHouse 会读入多少行数据或者多少bytes的数据后再批量写入,根据show processlist观察来看,貌似达到100多w行数据就会写入一次,随后释放内存,再循环读写。那么我们的问题其实读取MySQL原表中100多万行的数据超过了我们 ClickHouse 内存配置大小。

  1. 关于这个问题,如果你CK服务器内存配置比较大其实是不会遇到的,我这里CK服务内存仅为32G,所以可能碰到这个内存问题,最简单的其实扩容下内存就行了,但是为了避免有些项目上不好进行内存扩容,所以需要想下其他方法解决。
  • 最开始想到的是用swap分区,但是实际测试下来,ClickHouse 只会用物理内存,不会用到虚拟内存。
  • 第二个方式是通过where条件+分页查询mysql,减小内存占用的峰值,实测有效,但是比较麻烦。
  1. 接着我想着是不是有什么参数可以控制这个批量写入的阀值,这样不就不会遇到内存不够的问题了嘛,100w会超过内存限制,50w应该就不会超过了把。但是我google了下并没有找到对应的参数,群里问了下也没人知道,没办法,只能去源码里找找看,这个值到底是不是写死的。
三. 打印栈帧

首先我们要通过 pstack 打印下堆栈信息,不然无法知道函数入口在哪,在这之前需要我们额外安装下对应 ClickHouse 版本的clickhouse-common-static-dbg的 rpm 包(调试库),不然堆栈信息会比较简陋,而且后面GDB调试也会有问题。

比如我的 ClickHouse 版本为 20.8.12.2 ,那么对应的rpm包就为clickhouse-common-static-dbg-20.8.12.2-2.x86_64.rpm

再 ClickHouse 中执行 insert 语句后,通过pstack + clickhouse进程pid > /opt/ck_pstack.log 导入到一个日志文件中。

四. GDB调试

GDB 不多介绍,不过个人更喜欢使用CGDB,使用 Yum 安装即可,我使用的 OS 版本是 CentOS7.9 。

使用 GDB 调试前,还需要将对应 ClickHouse 的源码下载后解压到/build/目录下(默认的编译目录)。

然后调试步骤大概是:

  1. 首先新建个窗口,clickhouse-client 连接进入 ClickHouse ,等待执行 SQL 。
  2. 打开 CGDB ,attach 到 Clickhouse 的 pid 上,在对应函数行打上断点,这里选择的是DB::SourceFromInputStream::generate(从栈帧中选择),CGDB 中需要配置忽略信号量,不然 CGDB 会一直断开。
(gdb) att 1446
(gdb) handle SIGUSR2 noprint nostop
Signal        Stop      Print   Pass to program Description
SIGUSR2       No        No      Yes             User defined signal 2
(gdb) handle SIGUSR1 noprint nostop
Signal        Stop      Print   Pass to program Description
SIGUSR1       No        No      Yes             User defined signal 1
(gdb) b DB::SourceFromInputStream::generate
Breakpoint 1 at 0x16610f40: file ../src/Processors/Sources/SourceFromInputStream.cpp, line 135.
  1. 在第一步骤打开的窗口中执行 insert 语句。
  2. CGDB 中按 c 继续,就会跳到 generate 函数上

  1. 接着就是慢慢n,打印参数,一步一步看代码流程。
五. max_block_size

这里直接上调试发现的结果,当读取的行数等于 max_block_size 的时候,就会跳出循环读取,批量写入 ClickHouse ,释放内存,这个 max_block_size 的 GDB 打印的值为1048545,看着非常像是一个可以配置的参数。

搜索源码中 max_block_size 的赋值,下面这段看着比较像,由min_insert_block_size_rows配置参数决定。

接着从 system.settings 表中搜索了下,发现 min_insert_block_size_rows 这个参数的描述和默认值确实都非常像,基本确定这个参数就会影响批量写入的行数。

六. 测试

在会话中修改参数为1w,然后执行 insert ,可以跑通,而且不会报错。

localhost :) set min_insert_block_size_rows = 10000;

0 rows in set. Elapsed: 0.001 sec. 

localhost :) insert into `test`.`memory_test`  select * from mysql('192.168.213.222:3306','test','memory_test','root','xxxx');                                                  

INSERT INTO test.memory_test SELECT 
    *
FROM mysql('192.168.213.222:3306', 'test', 'memory_test', 'root', 'xxxx')
Ok.

0 rows in set. Elapsed: 2065.189 sec. Processed 500.00 thousand rows, 51.23 GB (242.11 rows/s., 24.81 MB/s.) 

show processlist 也可以看到确实是1w行就写入,那么就不会再产生内存不足的问题,至此这个问题基本解决。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS是一个开源的实时操作系统,它提供了一套用嵌入式系统的任务调度和管理机制。GDB(GNU Debugger)是一个功能强大的调试工具,可以用于调试C/C++程序。在使用FreeRTOS进行开发时,可以结合GDB进行调试。 要在FreeRTOS中使用GDB进行调试,需要进行以下几个步骤: 1. 配置编译器:首先,需要确保你的编译器支持GDB调试功能。常用的编译器如GCC和Keil都支持GDB调试。 2. 编译选项:在编译FreeRTOS应用程序时,需要添加一些编译选项以支持GDB调试。例如,在GCC中,可以使用"-g"选项来生成调试信息。 3. 连接器脚本:在链接应用程序时,需要使用连接器脚本来指定调试信息的位置。连接器脚本可以告诉GDB在哪里找到符号表和调试信息。 4. 启动GDB调试:在编译和链接完成后,可以使用GDB启动调试会话。可以通过命令行输入"gdb"命令来启动GDB,并使用"target remote"命令连接到目标设备。 5. 设置断点:在GDB中,可以使用"break"命令设置断点。可以设置函数断点、行号断点或地址断点等。 6. 执行调试:一旦设置好断点,可以使用GDB调试命令来执行程序。可以使用"run"命令来运行程序,使用"step"命令逐行执行,使用"next"命令执行下一行,使用"continue"命令继续执行等。 7. 查看变量:在调试过程中,可以使用GDB的"print"命令来查看变量的值。可以使用"info locals"命令查看局部变量,使用"info global"命令查看全局变量等。 8. 结束调试:当调试完成后,可以使用GDB的"quit"命令退出调试会话。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值