TDEngine 编译、运行与调试
August 11, 2021

TDEngine 是一个高性能的时序数据库,自 19 年底开源来,已经在 GitHub 上收获了近 16k star,在接下来一系列博客中,我将带大家分析其源码实现,了解其高性能的秘密。

在这篇文章里,我将介绍如何编译、运行与调试 TDEngine。

首先在 GitHub 上获取源码。

git clone git@github.com:taosdata/TDengine.git --depth 1

然后参考其 README 进行环境准备。在 ubuntu 16.04 上面确保以下依赖被满足。

sudo apt-get install -y gcc cmake build-essential git

然后在代码目录里创建一个 debug 文件夹,在这个文件夹中进行构建。

mkdir debug && cd debug
cmake .. && cmake --build .

最后到 100% 表示编译成功!

编译完成后,在 build/bin 下可以看到构建出来的几个二进制文件

╭─ichn@menheraArch ~/Projects/TDengine/debug ‹develop›
╰─$ cd build/bin
╭─ichn@menheraArch ~/Projects/TDengine/debug/build/bin ‹develop›
╰─$ ls
cqtest  demo  epoll  rclient  rsclient  rserver  sml  subscribe  taos  taosd  taosdemo  taosdump  taospack  tarbitrator  tdengineTest  tsim  waltest

在 debug 目录下通过

sudo make install

可以把编译出来的可执行文件安装到系统对应的目录上面,安装完成后会贴心地提示运行的方式:

Start to install TDEngine...
Created symlink /etc/systemd/system/multi-user.target.wants/taosd.service → /etc/systemd/system/taosd.service.

TDengine is installed successfully!

To configure TDengine : edit /etc/taos/taos.cfg
To start TDengine     : sudo systemctl start taosd
To access TDengine    : use taos in shell

TDengine is installed successfully!

按照其提示的方式使用 systemctl 启动服务端,然后用 taos 进入命令行

╭─ichn@menheraArch ~/Projects/TDengine/debug/build/bin ‹develop›
╰─$ sudo systemctl start taosd
╭─ichn@menheraArch ~/Projects/TDengine/debug/build/bin ‹develop›
╰─$ sudo systemctl status taosd
● taosd.service - TDengine server service
     Loaded: loaded (/etc/systemd/system/taosd.service; enabled; vendor preset: disabled)
     Active: active (running) since Wed 2021-08-11 09:22:59 CST; 8s ago
    Process: 42996 ExecStartPre=/usr/local/taos/bin/startPre.sh (code=exited, status=0/SUCCESS)
   Main PID: 43001 (taosd)
      Tasks: 112 (limit: 154510)
     Memory: 15.9M
        CPU: 55ms
     CGroup: /system.slice/taosd.service
             └─43001 /usr/bin/taosd

Aug 11 09:22:59 menheraArch systemd[1]: Starting TDengine server service...
Aug 11 09:22:59 menheraArch systemd[1]: Started TDengine server service.
Aug 11 09:22:59 menheraArch TDengine:[43001]: Starting TDengine service...
Aug 11 09:22:59 menheraArch TDengine:[43001]: Started TDengine service successfully.
╭─ichn@menheraArch ~/Projects/TDengine/debug/build/bin ‹develop›
╰─$ taos

Welcome to the TDengine shell from Linux, Client Version:2.1.6.0
Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.

taos>

接下来就可以尝试一下基本功能了。

参考其 SQL 文档,可以创建库、超级表和表,然后尝试一些简单的 SQL。

CREATE DATABASE power KEEP 365 DAYS 10 BLOCKS 4 UPDATE 1;
USE power;
CREATE STABLE meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);
CREATE TABLE d1001 USING meters TAGS ("Beijing.Chaoyang", 2);
CREATE TABLE d1002 USING meters TAGS ("Shanghai.Yangpu", 2);
INSERT INTO d1001 USING METERS TAGS ("Beijng.Chaoyang", 2) VALUES (now, 10.2, 219, 0.32);
INSERT INTO d1002 VALUES (now, 10.2, 219, 0.32), (now+1s, 10.2, 219, 0.32);
select * from d1001;
select count(*) from d1002;
select sum(current) from d1002;

跑起来之后,为了后续更好地分析代码,我们可以尝试用 gdb 在代码中打断点。

通过 sudo systemctl status taosd 可以看到 taosd 的 PID,然后通过

sudo gdb -p $PID

就可以把 gdb 附加到 taosd 的进程上面。

注意,如果进入 gdb 后提示 ptrace: Operation not permitted. 那么需要调整下 kernel 配置,切换到 root 权限,输入

echo 0 > /proc/sys/kernel/yama/ptrace_scope

允许 ptrace 追踪。

然后就可以进到熟悉的 gdb 界面了

╭─ichn@menheraArch ~/Projects/TDengine/debug ‹develop›
╰─$ sudo gdb -p 43001
GNU gdb (GDB) 10.2
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
Attaching to process 43001
...
[New LWP 43147]
[New LWP 43148]
[New LWP 43978]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
--Type <RET> for more, q to quit, c to continue without paging--
0x00007f3d832808ca in __futex_abstimed_wait_common64 () from /usr/lib/libpthread.so.0
(gdb)

输入 info proc 可以验证下 attach 到的是 taosd

(gdb) info proc
process 43001
cmdline = '/usr/bin/taosd'
cwd = '/'
exe = '/usr/local/taos/bin/taosd'

鉴于我们目前对代码还不是很熟悉,我们可以随便点。

处理查询的函数大概会叫 query,在 src/query/src/queryMain.c 文件中可以找到一个叫做 qCreateQueryInfo 的函数。

我们在这里打一个断点

(gdb) b src/query/src/queryMain.c:qCreateQueryInfo
Breakpoint 1 at 0x7f3d83738120: file /home/ichn/Projects/TDengine/src/query/src/queryMain.c, line 71.

然后用 c 继续执行。

回到 taos 客户端,输入一个简单的查询语句,譬如 select * from d1002;

可以发现这个语句并不会返回,而在 gdb 中,命令提示符显示程序已经停在了我们刚刚打断点的函数上面。

(gdb) c
Continuing.
[Switching to Thread 0x7f3d137fe640 (LWP 43121)]

Thread 85 "dnodeQueryQ" hit Breakpoint 1, qCreateQueryInfo (tsdb=0x7f3d1807db00, vgId=3, pQueryMsg=0x7f3cc8002c48, pQInfo=0x7f3d137fdd78, qId=35916213100505100) at /home/ichn/Projects/TDengine/src/query/src/queryMain.c:71
71      int32_t qCreateQueryInfo(void* tsdb, int32_t vgId, SQueryTableMsg* pQueryMsg, qinfo_t* pQInfo, uint64_t qId) {

通过 gdb,我们可以查看调用栈、各个变量的值,也可以追踪执行过程,这会在之后的代码阅读中帮我们更好地理解很多行为,也会在 debug 的时候帮我们快速定位问题。

举例来说,如果输入 bt 命令,可以看到代码执行到这个函数的调用栈

(gdb) bt
#0  qCreateQueryInfo (tsdb=0x7f3d1807db00, vgId=3, pQueryMsg=0x7f3cc8002c48, pQInfo=0x7f3d137fdd78, qId=35916213100505100) at /home/ichn/Projects/TDengine/src/query/src/queryMain.c:71
#1  0x0000560e9e12fb13 in vnodeProcessQueryMsg (pVnode=0x7f3d180436e0, pRead=0x7f3cc8002c00) at /home/ichn/Projects/TDengine/src/vnode/src/vnodeRead.c:236
#2  0x0000560e9e12f24c in vnodeProcessRead (vparam=0x7f3d180436e0, pRead=0x7f3cc8002c00) at /home/ichn/Projects/TDengine/src/vnode/src/vnodeRead.c:54
#3  0x0000560e9e0e01fb in dnodeProcessReadQueue (wparam=0x560e9e3dedf0) at /home/ichn/Projects/TDengine/src/dnode/src/dnodeVRead.c:137
#4  0x00007f3d83274259 in start_thread () from /usr/lib/libpthread.so.0
#5  0x00007f3d8319d5e3 in clone () from /usr/lib/libc.so.6

可以看出,线程从 dnodeProccessReadQueue 开始处理读取的查询,往下进入 vnode。

使用 n 可以单步运行程序,每次执行一行代码。

使用 p variable 可以查看变量。

(gdb) n
72        assert(pQueryMsg != NULL && tsdb != NULL);
(gdb) n
74        int32_t code = TSDB_CODE_SUCCESS;
(gdb)
76        SQueryParam param = {0};
(gdb)
77        code = convertQueryMsg(pQueryMsg, &param);
(gdb)
78        if (code != TSDB_CODE_SUCCESS) {
(gdb) p param
$1 = {sql = 0x7f3ccc001e30 "select * from d1002;", tagCond = 0x0, colCond = 0x0, tbnameCond = 0x0, prevResult = 0x0, pTableIdList = 0x7f3ccc00ea80, pExpr = 0x7f3ccc00eb50, pSecExpr = 0x0, pExprs = 0x0, pSecExprs = 0x0, pFilters = 0x0, pGroupColIndex = 0x0, pTagColumnInfo = 0x0, pGroupbyExpr = 0x0,
  tableScanOperator = 1, pOperator = 0x7f3ccc000ce0, pUdfInfo = 0x0}

可以看到,param 变量中就有我们输入到 taos 中的查询语句。

就此,我们知道了如何编译、运行并且使用 gdb 来调试 tdengine,后续的博客中我会带大家更进一步地深入 tdengine 的源码,了解其高性能的设计亮点。