ClickHouse介绍
ClickHouse是一个开源、高性能的列式 OLAP 数据库管理系统,用于使用 SQL 进行实时分析。
为什么需要ClickHouse UDAF?
ClickHouse中已存在了许多聚合函数,绝大多数情况下已经覆盖我们的需求,但是有时候我们仍然需要自定义函数逻辑,去实现复杂的数据分析需求,因此就有了基于ClickHouse中实现自定义函数(UDF)或自定义聚合函数(UDAF)需求,这篇文章我们讲下如何基于ClickHouse实现UDAF。
实现UDAF步骤
- 源码下载编译运行
- 实现UDAF
源码下载编译运行(mac系统)
推荐电脑内存配置 >= 16G,CPU核数越多越好,主频越高越好
安装 Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# ...and follow the printed instructions on any additional steps required to complete the installation.
安装 Xcode 和命令行工具
从 App Store安装最新的Xcode。 至少打开一次以接受最终用户许可协议并自动安装所需的组件。 然后,确保在系统中安装并选择了最新的命令行工具:
sudo rm -rf /Library/Developer/CommandLineTools
sudo xcode-select --install
安装所需的编译器、工具和库
brew update
brew install cmake ninja libtool gettext llvm gcc binutils
下载ClickHouse源代码
git clone --recursive git@github.com:ClickHouse/ClickHouse.git
此方式会下载ClickHouse最新的代码,但是不推荐此方式,因为新代码最新的代码BUG比较多,推荐选择稍微旧点的稳定版本源代码下载。另外ClickHouse的依赖较多,如果只下载主工程,在下载依赖的过程中很可能会因网络问题导致失败,所以推荐在github上直接下载包含所有依赖的源码包,如下图所示
构建ClickHouse
cd ClickHouse
// 创建构建目录
mkdir build
cd build
// 设置构建参数,$(brew --prefix llvm)的意思是要找出llvm在哪个目录,这里替换成llvm的绝对目录也可以
cmake -DCMAKE_C_COMPILER=$(brew --prefix llvm)/bin/clang -DCMAKE_CXX_COMPILER=$(brew --prefix llvm)/bin/clang++ -DCMAKE_AR=$(brew --prefix llvm)/bin/llvm-ar -DCMAKE_RANLIB=$(brew --prefix llvm)/bin/llvm-ranlib -DOBJCOPY_PATH=$(brew --prefix llvm)/bin/llvm-objcopy -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
// 进行构建,如果是第一次执行,则可能需要等待一两个小时的时间(看硬件性能)
cmake --build . --config RelWithDebInfo
构建完成后,会在build目录下产生一个programs目录,在programs目录下有一个clickhouse可执行文件,如果要启动clickhosue server服务,可执行./clickhouse server
其它平台可参考官网其它文章:clickhouse.com/docs/en/dev… 以下是本人在其它平台上操作ClickHouse源码编译运行经验:
- Windows上源码编译运行没用操作成功(环境问题,官网也没有说如何基于Windows系统操作)
- Centos7虚拟机上源码编译运行没有操作成功(环境问题)
- Centos8虚拟机上源码编译运行操作成功(有一些小问题,但是经过一些简单的环境处理就能解决问题)
Clion中编译运行ClickHouse(mac系统)
以上是在命令行中进行的操作,如果需要代码开发的话,那就需要使用IDE了,下面讲下如何在Clion(收费产品,免费使用30天)中源码编译运行ClickHouse。
下载ClickHouse源码(参考上面在命令行中下载源代码)
打开ClickHouse项目
用Clion打开ClickHouse项目,把下面的参数设置为Clion的默认cmake参数
cmake -DENABLE_JEMALLOC=0
设置的路径如下图所示,路径为Preference -> CMake -> CMake options
注意Clion默认的cmake build type是Debug模式,在这种模式下,ClickHouse性能会比较差。
修改完后,需要删除cmake-build-debug目录,右键项目,点击Reload CMake Project按钮,重新执行cmake,如下图所示
运行ClickHouse server
上一步执行完后,我们找到programs下的main.cpp文件,找到main方法,运行此方法,启动ClickHouse server,此时Clion会执行cmake --build,即会编译ClickHouse源代码,如果是第一次执行main方法,需要等待大约1-2个小时(取决于机器硬件配置),正常启动后的日志如下
实现UDAF
关键接口IAggregateFunction
实现UDAF需要实现AggregateFunctions目录下的IAggregateFunction接口,其中一个是IAggregateFunction.cpp文件,子类要在这个文件中注册UDAF函数,另一个是IAggregateFunction.h文件,子类要在这个文件中写函数逻辑,这里面我们先主要关注add,merge,serialize,deserialize,insertResultInto这5个方法,如下图所示
add方法
对于输入的每一条数据,都会调用一次add方法。其中columns代表多个列,可通过下标访问某一列,例如column[0],下标从0开始;row_num代表行号,表示当前是第几条数据,下标从0开始。我们可以通过这个方法,对每一条数据进行聚合处理。
merge方法
ClickHouse为了实现高性能的计算,使用了多线程,每个线程都会有一个聚合的结果,所以我们需要把每个线程的聚合结果合并起来,merge方法就是做这个事的。
serialize和deserialize方法
这2个方法我暂时还没用到,我猜测在ClickHouse的分布式模式下,数据会被分片到多个服务器上,每个服务器都会有一个聚合结果,但是所有的聚合结果最后都会汇总到一块,所以就需要这2个方法,做聚合结果的序列化和反序列化。
insertResultInto方法
这个方法会对最终的结果进行输出。
实现这2个文件之后,需要在AggregateFunctions目录中的registerAggregateFunctions.cpp文件中对函数进行注册,具体怎么注册,可以到这个文件中看看已经注册的UDAF,照葫芦画瓢写下就行。
sum函数实现
下面举个例子,看看ClickHouse已经存在sum函数是如何实现的。
sum函数的UDAF函数对应的是AggregateFunctionSum.cpp和AggregateFunctionSum.h这两个文件,在AggregateFunctionSum.cpp文件中,通过registerAggregateFunctionSum方法注册了sum函数,如下图所示
add方法
在AggregateFunctionSum.h文件中的add方法中,columns[0]就代表我们要进行的sum的那一列,然后column.getData()[row_num])就代表这一列的当前行的值,如下图所示
之后把这个值(我们定义为V)传给AggregateFunctionSumData的add方法,AggregateFunctionSumData的add方法如下图所示
这里有个T sum{},这里的sum就是总合,把sum和V传给AggregateFunctionSumAddOverflowImpl的add方法,AggregateFunctionSumAddOverflowImpl的add方法如下图所示
可以看到,这里就是把V加到了sum上,非常简单。
merge方法
下图是AggregateFunctionSum的merge方法,直接调用了AggregateFunctionSumData的merge方法
在AggregateFunctionSumData的merge方法中,也是直接将另一个线程sum结果加到当前线程的sum上
serialize和deserialize方法
下图是AggregateFunctionSum的serialize和deserialize方法,直接调用了AggregateFunctionSumData的read和write方法
下图是AggregateFunctionSumData的read和write方法,这里其实就是对sum进行序列化,然后走网络传输
insertResultInto方法
下图是insertResultInto方法,这里的this->data(place).get()实际上就是从A中AggregateFunctionSumData中取sum值,然后通过push_back方法将sum写到响应列表中
总结
本节我们介绍了如何在命令行和Clion中源码编译构建ClickHouse,以及通过实现IAggregateFunction接口的add、merge、serialize和deserialize(分布式的集群需要这两个函数)和insertResultInto方法实现一个UDAF,后续我会继续研究和分享ClickHouse的其它源码,欢迎大家一块研究,参与讨论~