引言
在数据库 OceanBase 3.0 峰会上,OceanBase 宣布正式开源,并成立 OceanBase 开源社区 , 300 万行核心代码向社区开放。开源的 OceanBase 社区版代码由于经过多年的迭代与变化,新人上手殊为不易。为了帮助大家理清头绪愉快上手,本人将利用碎片时间围绕“源码解读”写个系列介绍。将通过一系列文章进行阐述,帮您理清数据库的内在本质。
本系列将从以下六大模块进行介绍:
一、数据库的整体架构:梳理 OceanBase 数据库代码的整体架构和模块构成,以及各模块的各自功能。
二、SQL 的一生:介绍 OceanBase 数据库中任意一条 SQL 的执行流程,包括接收、处理、返回结果给客户端的过程。
三、分区的一生:讲解 OceanBase 数据库存储层的相关知识。
四、事务的一生:解析 OceanBase 数据库事务的外部接口。
五、租户的一生:阐述 OceanBase 数据库多租户的特性。
六、虚拟表:拆解 OceanBase 数据库虚拟表的本质。
(注:各位看官,本系列是代码导读,不是设计解读,一定要结合代码来看,并且最好配上动手实践,否则就是把辅导手册当教材看了。)
通过本系列的源码解读文章,您首先可以了解 OceanBase 数据库的基本原理,轻松 get 数据库的实现步骤。推而广之,您也可以把 OceanBase 的实现原理应用到其他数据库,这对您学习其他的数据库也将带来帮助。其次,在熟悉了 OceanBase 的代码之后,如果有需要,您可以直接在后续工作中使用我们的代码,或者为 OceanBase 社区贡献您的代码。
本文为《带你读源码系列》第一篇,主要为大家介绍 OceanBase 数据库代码的整体架构和模块构成,以及各模块的功能。
顶层目录
上图为顶层目录。主体代码在 src 目录下,单元测试代码在 unittest 目录下。unittest 目录下单测的目录结构与 src 目录下的结构和命名方式相同。例如,src/sql/abc.cpp 对应的单测文件是 unittest/sql/test_abc.cpp,单测使用 gtest 和 gmock 框架。unittest 目录下也包含一些重要组件的集成测试。
test 目录下则是系统测试,这里的测试对象是完整启动的 observer。其中 test/mysql_test 目录下包含的各种测试用例是利用修改后的 mysql_test 框架运行的测试用例。它主要用 SQL 来测试系统功能正确性。
cmake 目录和 build.sh 脚本编译相关,我们将在以后的文章中详细介绍。
deps 目录
deps 目录比较特别,它包含 src 所依赖的东西。deps/3rd 目录包含一组工具,用来下载和编译第三方库,专门为社区版研制。deps/easy 是阿里的多隆大神早年间开发的一个基于 libev 的 rpc 框架,我们在此基础上做了一些修改。现在随着 OceanBase 的开源,OceanBase 的 rpc 框架是基于 easy 的。deps/oblib 是最核心的基础库。为什么放到这里呢?因为它经过多次与 OceanBase 代码仓库的分分合合。
oblib 目录
一般情况下,oblib 库不依赖于 OceanBase src,只被依赖。rpc 是 OceanBase 业务代码所使用的内部 rpc 框架,它依赖于 libeasy;rpc模块也提供了一组方便的宏来快捷定义 rpc。lib 目录是依赖的最底层,它没有外部依赖,包含了错误码定义、容器类、内存分配器等大量基础类,以及最基础的头文件 ob_define.h(想喝咖啡时你可以改一下这个文件然后执行 make进行编译)。一般情况下,oblib 目录下的代码,特别是 oblib/src/lib 下的代码是与 OceanBase 业务代码无关的。也就是说,如果你在做一个其他的 C++ 项目,也可以直接使用这个库。注意,OceanBase 的编码规范要求不使用 STL 容器,所以这里有大量的“轮子”。common 目录下的代码依赖于 lib,但是比顶层 src 又更业务无关一些。如果你做一个存储系统(即使不是数据库),可能用的到这里的公共类。这里面最重要的类是 ob_object.h 中的 ObObj 表示一个包含类型信息的值。比如新增列类型就要改这个类。
接下来重点介绍 deps/oblib/src/common 目录下的几个子目录。object 目录下是最重要的数据类型 ObObj 的定义,OceanBase 支持的列数据类型,这从枚举类型 ObObjType 中可以看出来。可以看出 36 以后是 Oracle 租户类型下的数据类型。ObObj 是存储和数据处理的“原子”。rowkey 目录下定义的 ObRowkey 是每一行记录的主键。OceanBase 在底层存储只有索引组织表,每一行必须有主键;用户可见的无主键表是通过一个隐藏的自增列做 rowkey 的,算是一个模拟。存储引擎的 memtable 和 sstable 中都是用 rowkey 索引的。row 目录下定义了一行记录的表示 ObNewRow (你找不到ObRow:),他是数据处理的“分子”,基于它定义的 ObRowIterator 是很多操作类的接口。
log 目录定义了一组很好用的日志宏。OceanBase 代码里面到处都有的 LOG_WARN 等宏就是在 ob_log.h 提供的。它的接口综合了 printf 和 cout 的优点,没有 cout 那么简约,又是强类型的,且限定了统一的 key-value 风格。为了在 C++ 老版实现这组接口,我们用了很多模板和宏的绝妙小技巧。如果你先熟悉这组接口,再尝试贡献代码,你会爱上他们。
src 目录
终于轮到了 src 目录。
election 是分布式选举模块,它是比较独立的,因为在运行时如果选举不出 1 号 leader,系统所有组件就都不工作。它独立于 Paxos 协议。该选举协议要求各节点时钟同步。clog 最初的意思是 commitlog,现在成了专有词汇,特指 OceanBase 的事务 redo 日志。Paxos 的实现也在这个目录下。archive 是日志归档组件,备份恢复依赖该组件。
rootserver 目录是 OceanBase 集群总控服务。这个命名不够准确,准确的名字应该是 rootservice,它不是独立进程,而是某些 observer 内部启动的一组服务,感兴趣的读者可以看看 OceanBase 0.4 的开源代码。集群管理和自动容灾、系统自举、分区副本管理和负载均衡,以及 DDL 的执行都在这个组件中。
share 目录是被强行从“母体”oblib/src/common 中剥离出来的公共类,所以它们的 namespace 是 common 而不是 share。
sql 就是 SQL。storage 就是存储引擎。事务管理位于 storage/transaction 下。
observer 是所有组件的“总装车间”,入口是 ob_server.h 和 ob_service.h。MySQL 协议层的命令处理入口位于 observer/mysql。
数据库的源码内容博大精深。本文对 OceanBase 源码的目录架构做了一个整体概述,后续将会对具体内容进行层层拆解,希望对大家有所帮助。