有哪些阅读 TDengine 代码的好建议?

2022-03-21 00:00:00 函数 代码 模块 几个 线索
作者:castermode
链接:https://www.zhihu.com/question/502592495/answer/2257554061


说说我的看法吧:

近读了一下TDengine的代码,代码量较大,大部分是纯C代码。泛泛的读,确实有难度。关键是要找到一些线索,顺着线索读,就会好很多。

我个人采用了如下几个线索,供大家参考吧:

1.先读一些小基础组件,找找感觉。

我使用了很多年的C++,近些年来,更是用了不少C++11之后的特性。此时回头再看C代码,已经有些不适应了。看到手写的malloc,free,和大量的指针移动技巧,还有很多void*强制类型转换。有一种不系安全带,开车上高速的感觉(在现代C++中,大部分场景都直接使用智能指针了),过了好久才逐渐适应。对于使用其他语言的同学,这种感觉可能会更明显。这时可以先读一些小的独立的基础组件,找回C语言编程的感觉。我发现TDengine util 目录下,有很多好的素材可以用来找感觉。比如 tlist.h,tarray.h,tskiplist.h,实现的都很不错。再看tref.h,几乎就是一个手撸的智能指针。这些C代码对比起C++的STL,显得更短小精悍,直击问题本质,甚至可以把这些代码拿出来直接用到其他的项目里。另外再说一下,C代码结构比较清晰,基本上就是通过XX.h定义主要的数据结构和接口函数,在通过XX.c将函数体实现。所以如果想看跳跃表的代码,只需看tskiplist.h,tskiplist.c就行了。类似的小组件只要读上几个,甚至都不用读数据库内核的代码,就已经有很大收获了。

2.再读一些大的独立模块。

当读了几个小组件,找到了C语言编程的感觉后,就可以接着读一些更大一些的独立模块了。比如rpc 和 tsdb 两个目录,比较独立,可以分别阅读以下。

rpc模块(rpc目录)就是一个基于事件模型的网络库,相当于libevent,但更小巧精致。

我估计大部分同学应该使用TCP更多一些,所以可以先从TCP的代码读起。这里提供一个线索:rpcTcp.c : taosProcessTcpData。大家看这个函数中有一个epoll_wait。我相信了解网络编程的同学,找到了这个函数,基本就能明白一半了。

值得一提的是,rpc模块同样实现了UDP通信。读完TCP相关代码后,大家可以接着读UDP的相关实现。看TDengine的官网文档介绍,它的节点间通信会根据实际情况自动选择TCP/UDP,以适应物联网的场景,这点做的还是不错的。


tsdb模块(tsdb目录)是时序数据的存储引擎,大家可以把它理解为leveldb。但比leveldb稍复杂一些。主要区别在于在TDengine中,每一条数据是可以有多列的,而leveldb只有一列。另外,在一个tsdb中,是可以有多个表的。而leveldb内部不分表了,所有数据都在一起。虽然有这些不同,但整体结构还是LSM-Tree,也有 mem, imem。而且具体实现也是skiplist。相信熟悉leveldb代码的同学,读起来会有一些似曾相识的感觉。tsdb模块整体代码量1.5万行左右,和leveldb代码量差不多。大家可以体验一下C语言是如何实现类似的功能的。

这里也提供一个线索:

数据写入内存的入口 :tsdbInsertData - tsdbInsertDataToTable

数据落磁盘的入口 :tsdbCommitData - tsdbCommitTSData

大家可以从这两个入口函数一路看下去。


3.读整体分布式架构的代码

这部分代码有一定难度,需要一些分布式的背景知识,这里限于篇幅,就不展开讨论了。当仍然提供一些线索。

在TDengine中,分为dnode,vnode,mnode,client等(具体细节可以参考官网上的架构文档),分布式消息就是在这几个模块之间流动。大家可以看 taosmsg.h 里面的消息定义,有如下注释:

// message from client to dnode

// message from mnode to dnode

// message from client to mnode

// message from dnode to mnode

比如这个消息 TSDB_MSG_TYPE_MD_DROP_VNODE ,里面的 MD 就表示这是一条从mnode发往dnode的消息。而看名字,也能猜到功能是 "DROP_VNODE"。

大家可以在代码中 grep 感兴趣的消息定义,来查看具体的消息处理函数细节。



后,我经常会想为什么TDengine是用C语言开发的呢?我觉得可能有如下几个原因吧:

1.追求性能(C的性能毋庸置疑,但如果用不好,也很容易出问题)

2.适配各种硬件环境(TDengine初的设计应该不只是跑在标准服务器上,可能还有各种各样的物联网硬件。如果要适配这些硬件,确实只有C能做到,C++都不行,更何况其他语言了)

3.“老派”程序员的习惯(世界上确实有一批“老派”的程序员,伴随着C语言,操作系统的诞生成长起来的)

相关文章