前一阵子参加了第一届OceanBase数据库大赛, 期间接触了MiniOB和OceanBase社区版. 本文对我开始学习OceanBase社区版时的经历做一个简单的介绍, 皆在帮助准备学习OceanBase源码的新人快速上手.
编译、安装和部署
万事开头难. 所幸OceanBase社区版的构建工具非常”简洁”, 一个build.sh
搞定了大部分操作. 首先是准备环境, OceanBase本身的构建脚本支持大部分主流Linux发行版. 但如果你希望在同一个系统上build和run, 还是建议你使用rpm体系的发行版, 因为部署工具obd只支持rpm体系.
安装依赖
编译OceanBase前只需要一些常见的工具链即可. 构建脚本会自己下载固定版本gcc和二进制依赖库等, 所以通常不用担心发行版之间的差异.
Redhat Based
yum install git wget rpm* cpio make glibc-devel glibc-headers binutils m4
Debian Based
apt-get install git wget rpm rpm2cpio cpio make build-essential binutils m4
编译OceanBase
./build.sh debug --init --make
只有第一次编译需要--init
, --init
会在deps/3rd/
下载编译器和依赖库.
部署OceanBase
OceanBase启动参数会根据硬件配置变化, 还是交给OBD部署比较方便. OBD目前只支持CentOS环境, 其他环境可能需要hack一下. 首先准备一个可用的OceanBase集群, 可以参考快速开始.
添加OceanBase源
sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://mirrors.aliyun.com/oceanbase/OceanBase.repo
安装
ob-deploy
和ob-client
sudo yum install -y ob-deploy
部署一套OceanBase运行环境, 这里使用最简单的单节点配置为例 (需要替换
$OB_DEPLOY_NAME
):cat > deploy.yml <<EOF oceanbase-ce: version: 3.1.0 tag: ob-test servers: - 127.0.0.1 global: home_path: /var/lib/oceanbase devname: eth0 mysql_port: 2881 rpc_port: 2882 zone: zone1 cluster_id: 1 datafile_size: 10G EOF obd cluster autodeploy "$OB_DEPLOY_NAME" -c deploy.yml
查看OceanBase运行状态
obd cluster display "$OB_DEPLOY_NAME"
创建测试租户
obd cluster tenant create "$OB_DEPLOY_NAME" --tenant-name test obclient -uroot@test -h127.0.0.1 -p2881
然后使用编译得到的observer
替换集群使用的observer
:
找到集群使用的
observer
位置, 可以使用ps找.ps -ef | grep observer
停止OceanBase集群, 替换
observer
. 如果涉及多个节点, 需要一一替换:obd cluster stop "$OB_DEPLOY_NAME" cp ./build_debug/src/observer/observer "$OB_CLUSTER_SERVER_BINARY_PATH"
最后重启OceanBase集群即可
obd cluster restart "$OB_DEPLOY_NAME"
在比赛中我也写了一些用于远程开发、编译、测试的工具 (ccat3z/oceanbase-competition-toolset), 或许可以提供一些帮助.
代码风格
在开始阅读OceanBase源码前, 我建议先看一下OceanBase 代码风格指南. 尤其是被现代C++, GoLang, Java, C#等”惯坏”的同学们, 第一次看到大量的if (OB_FAIL(...)) {} else {}
可能会比较困惑.
OceanBase为避免常见陷阱牺牲了编程复杂度, 强制单入口单出口, 禁止中途使用return
, goto
, throw
等跳转指令. 这样能够让开发人员不容易忘记释放资源. 我们以最简单的顺序语句举例, 如果不限制单出口,可能会这么写 (GoLang玩家有被暗示到):
int ret = OB_SUCCESS;
ret = do_something1();
if (OB_SUCCESS != ret) {
free(...);
log_error(...);
return ret;
}
ret = do_something2();
if (OB_SUCCESS != ret) {
free(...);
log_error(...);
return ret;
}
// ... 更多代码
限制单出口后, 能够防止在中途跳转时忘记释放资源:
int ret = OB_SUCCESS;
ret = do_something1();
if (OB_SUCCESS != ret) {
log_error(...);
}
if (OB_SUCCESS == ret) {
ret = do_something2();
if (OB_SUCCESS != ret) {
log_error(...);
}
}
// ... 更多代码
free(...);
return ret;
上述代码仍然非常冗长,当有效代码只有两行。 因此OceanBase增加了一些常用宏用来精简代码:
int ret = OB_SUCCESS;
if (OB_FAIL(ret = do_something1())) {
log_error(...);
} else if (OB_FAIL(ret = do_somthing2())) {
log_error(...);
} else if (OB_FAIL(...)) {
// ...
// ...
// ...
} else { }
ret = do_something1();
if (OB_SUCCESS != ret) {
}
free(...);
return ret;
我们可以在OceanBase中看到大量类似的if else
的结构. 除了顺序语句, OceanBase 代码风格指南中提到了很多语句的规范, 基本能覆盖大部分场景. 可以从中发现, OceanBase的代码风格确实复杂了一些, 刚开始理解代码会有少许阻力, 因此先快速浏览一遍风格指南能为读代码提供不小帮助. 一定有同学会和RAII, defer, GC等等做比较, 这点还是在你读OceanBase代码的过程中再评价吧.
项目结构
首先来到项目的顶层目录, OceanBase社区版项目结构非常容易理解, 下面简单介绍.
cmake/
和build.sh
: 构建相关工具, 我们在第一节中已经用到了.deps/
: 依赖库rpm/
: RPM包打包脚本tools/
: 一些开发和运维工具test/
和unittest/
: 集成测试和单元测试, 使用方法见如何运行测试src/
: OceanBase源码share/
: 公共类election/
: 集群的选举模块rootserver/
: 集群的总控服务archive/
: 日志归档组件, 用于备份恢复clog/
: OceanBase版redo logsql/
: SQL模块. SQL解析, 查询优化, 执行器等等storage/
: 存储引擎observer/
: OceanBase主进程, 系统入口
下一步?
至此已经做好了学习OceanBase源码的全部准备. 下一步可以参考OceanBase官方教程, 挑选感兴趣的模块作为切入点. 或者跟随OceanBase 源码解读系列文章学习. 如果有什么想改进的, 可以查阅OceanBase 开发者手册, 成为一个Contributor!