1.5 MiniOB 开发调试环境搭建
本文将介绍两种方式的构建方法,可根据实际情况选择手动构建或直接使用 Dockerfile 构建。
手动打造开发环境
MiniOB 编译
前提条件:系统上已经安装了 Make 等编译工具。
使用 MiniOB 需要具备以下两个条件。
CMake:3.10 版本以上。
GCC/Clang:GCC 建议 8.3 版本以上,编译器需要支持 C++20 等新标准。
安装 CMake
需要安装 3.10 版本或 3.10 以上版本的 CMake,在 CMake官网 下载对应系统的 CMake。
wget https://github.com/Kitware/CMake/releases/download/v3.24.0/cmake-3.24.0-linux-x86_64.sh bash cmake-3.24.0-linux-x86_64.sh
如果是 MAC 系统,执行以下命令安装。
brew install cmake
安装 GCC
如果是个人学习使用,Clang 通常没有问题,如果想参加比赛或使用 OceanBase 官网训练营,建议安装 GCC。因为 Clang 某些情况下的表现会与 GCC 不一致。
另外,如果 GCC 的版本比较旧,需要安装较新版本的 GCC。有些比较旧的操作系统(如 CentOS 7),自带的编译器是 4.8.5 版本,该版本对 C++20 等新标准支持不友好,建议升级 GCC。
在 Linux 系统上,通常使用 yum install gcc gcc-g++ 就可以安装 GCC,但是有些时候安装的编译器版本比较旧,需要手动安装。
如何查看 GCC 版本
gcc --version
下载 GCC 源码
可以在 GCC 官网浏览,找到镜像入口,选择速度比较快的镜像,如 Russia, Novosibirsk GCC 镜像链接。
建议选择 8.3 或以上版本。MiniOB 是为了学习使用,可以选择较新版本的 GCC,不要求稳定。
编译 GCC 源码
注意 编译高版本的 GCC 时,需要本地也有一个稍高一点版本的 GCC。比如编译 GCC 11,本地的 GCC 需要能够支持 stdc++11 才能编译,可以先编译 GCC 5.4,然后再编译高版本。
# 解压 tar xzvf gcc-11.3.0.tar.gz cd gcc-11.3.0 # 下载依赖 ./contrib/download_prerequisites # 配置。可以通过 prefix 参数设置编译完成的 GCC 的安装目录,如果不指定,会安装在 /usr/local下 # 可以配置为当前用户的某个目录 ./configure --prefix=/your/new/gcc/path --enable-threads=posix --disable-checking \ --enable--long-long --with-system-zlib --enable-languages=c,c++ # 开始编译 make -j # 安装 # 编译产生物会安装到 configure --prefix 指定的目录中,或系统默认目录下 make install # 修改环境变量 # 可以将下面的配置写到 .bashrc 或 .bash_profile 中,这样每次登录都会自动生效 export PATH=/your/new/gcc/path/bin:$PATH export LD_LIBRARY_PATH=/your/new/gcc/path/lib64:$LD_LIBRARY_PATH export CC=/your/new/gcc/path/bin/gcc export CXX=/your/new/gcc/path/bin/g++ # NOTE: 上面的环境变量 CC 和 CXX 是告诉 CMake 能够找到我们的编译器。CMake 优先查找的 # 编译器是 CC 而不是 GCC,而一般系统中会默认带 CC,所以使用环境变量告诉 CMake。 # 也可以使用 CMake 参数 # `-DCMAKE_C_COMPILER=/your/new/gcc/path/bin/gcc -DCMAKE_CXX_COMPILER=/your/new/gcc/path/bin/g++` # 来指定编译器如果
./contrib/download_prerequisites下载时特别慢或下载失败,可以手动从官网上下载依赖,解压相应的包到 GCC 的目录下。ftp://ftp.gnu.org/gnu/gmp https://mpfr.loria.fr/mpfr-current/#download https://www.multiprecision.org/mpc/download.html
构建 MiniOB
下载源码
git clone https://github.com/oceanbase/miniob.git
环境初始化
如果是第一次在这个环境上编译 MiniOB,需要执行如下命令安装 MiniOB 的依赖库。
cd miniob bash build.sh init
命令执行后,脚本会自动拉取依赖库,并编译安装到系统目录。
说明 如果使用 GitPod 开发,可以跳过这步,GitPod 会自动执行。
编译
执行如下命令将编译 debug 版本的 MiniOB。
bash build.sh
执行如下命令将编译 release 版本的 MiniOB。
bash build.sh release
详细使用方法可执行
bash build.sh -h查看帮助信息。
MiniOB 运行
编译完成后,可在 build 目录(build_debug 或 build_release)下找到 bin/observer 目录,这是 MiniOB 的服务端进程,bin/obclient 是自带的客户端进程,当前服务端程序启动已经支持了多种模式,可以以 TCP、unix socket 方式启动,这时需要启动客户端以发起命令。observer 还支持直接执行命令的模式,这时不需要启动客户端,直接在命令行输入命令即可。
说明
observer 提供的参数可通过 ./bin/observer -h 命令查看帮助。
以直接执行命令启动服务端进程
您可执行如下命令以直接执行命令的方式启动服务端程序,这种情况下可以直接输入命令,不需要启动客户端,所有请求都会以单线程的方式运行,配置项中的线程数不再有实际意义。
./bin/observer -f ../etc/observer.ini -P cli
以监听 TCP 端口方式启动服务端进程
您可执行如下命令以监听端口的方式启动服务端程序,此处以 6789 端口为例。
./bin/observer -f ../etc/observer.ini -p 6789
这种情况下需执行如下命令启动客户端程序,启动后客户端会连接到服务端的 6789 端口。
./bin/obclient -p 6789
以监听 unix socket 方式启动服务端程序
您可执行如下命令以监听 unix socket 的方式启动服务端程序。
./bin/observer -f ../etc/observer.ini -s miniob.sock
这种情况下需执行如下命令启动客户端程序,启动后客户端会连接到服务端的 miniob.sock 文件。
./bin/obclient -s miniob.sock
并发模式
默认情况下,编译后的程序不支持并发操作,如若需要支持并发,需要在编译时增加 -DCONCURRENCY=ON 选项,示例如下。
cmake -DCONCURRENCY=ON ..
或执行如下命令。
bash build.sh -DCONCURRENCY=ON
之后使用上述命令启动服务端进程,即可支持并发操作。
常见问题
问题现象:在启动服务端进程时出现报错找不到链接库 A。
可能原因:安装依赖时,默认安装在 /usr/local/ 目录下,而环境变量中没有将目录包含到动态链接库查找路径。
解决方法:
您可将如下命令添加到 HOME 目录的 .bashrc 中,之后执行 source ~/.bashrc 命令加载环境变量后重新启动程序。
export LD_LIBRARY_PATH=/usr/local/lib64:$LD_LIBRARY_PATH
LD_LIBRARY_PATH 是 Linux 环境中运行时查找动态链接库的路径,路径之间以冒号(:)分隔。
将数据写入 bashrc 或其它文件中,下次启动程序时会自动加载,而不需要再次执行 source 命令加载。
注意
如果你的终端脚本使用的不是 bash,而是 zsh,那么就需要修改 .zshrc。
MiniOB 调试
调试 C/C++ 程序,常用的有两种方式,一是通过打印日志进行调试,二是借助 GDB 调试器进行调试。通过调试程序,可以熟悉程序执行的每一步细节,不仅可以定位问题,也可以用来熟悉代码,增加编程能力。
MiniOB 的关键数据结构
parse_def.h: struct Selects; //查询相关 struct CreateTable; //建表相关 struct DropTable; //删表相关 enum SqlCommandFlag; //SQL 语句对应的 command 枚举 union Queries; //各类 DML 和 DDL 操作的联合 table.h class Table; db.h class Db;
MiniOB 的关键接口
RC parse(const char *st, Query *sqln); //sql parse 入口 ExecuteStage::handle_request ExecuteStage::do_select DefaultStorageStage::handle_event DefaultHandler::create_index DefaultHandler::insert_record DefaultHandler::delete_record DefaultHandler::update_record Db::create_table Db::find_table Table::create Table::scan_record Table::insert_record Table::update_record Table::delete_record Table::scan_record Table::create_index
打印日志调试
MiniOB 提供的日志接口
deps/common/log/log.h: #define LOG_PANIC(fmt, ...) #define LOG_ERROR(fmt, ...) #define LOG_WARN(fmt, ...) #define LOG_INFO(fmt, ...) #define LOG_DEBUG(fmt, ...) #define LOG_TRACE(fmt, ...)
日志相关配置项 observer.ini
LOG_FILE_NAME = observer.log # LOG_LEVEL_PANIC = 0, # LOG_LEVEL_ERR = 1, # LOG_LEVEL_WARN = 2, # LOG_LEVEL_INFO = 3, # LOG_LEVEL_DEBUG = 4, # LOG_LEVEL_TRACE = 5, # LOG_LEVEL_LAST LOG_FILE_LEVEL=5 LOG_CONSOLE_LEVEL=1
GDB 调试
Attach 进程
[admin@localhost run]$ gdb -p `pidof observer` GNU gdb (GDB) Red Hat Enterprise Linux 8.2-15.el8 Copyright (C) 2018 Free Software Foundation, Inc. (gdb)
设置断点
(gdb) break do_select Breakpoint 1 at 0x44b636: file /home/admin/source/stunning-engine/src/observer/sql/executor/execute_stage.cpp, line 526. (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x000000000044b636 in ExecuteStage::do_select(char const*, Query*, SessionEvent*) at /home/admin/source/stunning-engine/src/observer/sql/executor/execute_stage.cpp:526(gdb) break Table::scan_record Breakpoint 2 at 0x50b82b: Table::scan_record. (2 locations) (gdb) inf b Num Type Disp Enb Address What 1 breakpoint keep y 0x000000000044b636 in ExecuteStage::do_select(char const*, Query*, SessionEvent*) at /home/admin/source/stunning-engine/src/observer/sql/executor/execute_stage.cpp:526 2 breakpoint keep y <MULTIPLE> 2.1 y 0x000000000050b82b in Table::scan_record(Trx*, ConditionFilter*, int, void*, void (*)(char const*, void*)) at /home/admin/source/stunning-engine/src/observer/storage/common/table.cpp:421 2.2 y 0x000000000050ba00 in Table::scan_record(Trx*, ConditionFilter*, int, void*, RC (*)(Record*, void*)) at /home/admin/source/stunning-engine/src/observer/storage/common/table.cpp:426 (gdb)继续执行
(gdb) c Continuing.
触发断点
执行:miniob > select * from t1;
[Switching to Thread 0x7f51345f9700 (LWP 54706)] Thread 8 "observer" hit Breakpoint 1, ExecuteStage::do_select (this=0x611000000540, db=0x6040000005e0 "sys", sql=0x620000023080, session_event=0x608000003d20) at /home/admin/source/stunning-engine/src/observer/sql/executor/execute_stage.cpp:526 526 RC rc = RC::SUCCESS; (gdb)单步调试(跳过函数调用)
575 std::vector<TupleSet> tuple_sets; (gdb) next 576 for (SelectExeNode *&node: select_nodes) { (gdb) n 577 TupleSet tuple_set; (gdb) 578 rc = node->execute(tuple_set); (gdb)单步调试(进入函数调用的内部)
(gdb) s SelectExeNode::execute (this=0x60700002ce80, tuple_set=...) at /home/admin/source/stunning-engine/src/observer/sql/executor/execution_node.cpp:43 43 CompositeConditionFilter condition_filter; (gdb)打印变量
(gdb) p tuple_set $3 = (TupleSet &) @0x7f51345f1760: {tuples_ = std::vector of length 0, capacity 0, schema_ = { fields_ = std::vector of length 0, capacity 0}} (gdb)watch 变量
(gdb) n 443 RC rc = RC::SUCCESS; (gdb) n 444 RecordFileScanner scanner; (gdb) n 445 rc = scanner.open_scan(*data_buffer_pool_, file_id_, filter); (gdb) watch -l rc Hardware watchpoint 3: -location rc (gdb) c Continuing. Thread 8 "observer" hit Hardware watchpoint 3: -location rc Old value = SUCCESS New value = RECORD_EOF 0x000000000050c2de in Table::scan_record (this=0x60f000007840, trx=0x606000009920, filter=0x7f51345f12a0, limit=2147483647, context=0x7f51345f11c0, record_reader=0x50b74a <scan_record_reader_adapter(Record*, void*)>) at /home/admin/source/stunning-engine/src/observer/storage/common/table.cpp:454 454 for ( ; RC::SUCCESS == rc && record_count < limit; rc = scanner.get_next_record(&record)) { (gdb)结束函数调用
(gdb) finish Run till exit from #0 0x000000000050c2de in Table::scan_record (this=0x60f000007840, trx=0x606000009920, filter=0x7f51345f12a0, limit=2147483647, context=0x7f51345f11c0, record_reader=0x50b74a <scan_record_reader_adapter(Record*, void*)>) at /home/admin/source/stunning-engine/src/observer/storage/common/table.cpp:454结束调试
(gdb) quit A debugging session is active. Inferior 1 [process 54699] will be detached. Quit anyway? (y or n) y Detaching from program: /home/admin/local/bin/observer, process 54699 [Inferior 1 (process 54699) detached]
使用 Dockerfile 构建
Docker 环境说明
MiniOB 镜像文件基于 CentOS:7 制作,镜像包含:
jsoncpp
google test
libevent
flex
bison(3.7)
gcc/g++ (version=11)
MiniOB 源码(/root/source/miniob)
在 Docker 中 /root/source/miniob 目录下载了 GitHub 的源码,可以根据个人需要,下载自己仓库的源代码,也可以直接使用 git pull 拉取最新代码。 /root/source/miniob/build.sh 提供了一个编译脚本,以 DEBUG 模式编译 MiniOB。
操作步骤
首先要确保本地已经安装了 Docker。
使用 Dockerfile 构建。
Dockerfile: https://github.com/oceanbase/miniob/blob/main/Dockerfile
# build docker build -t miniob . docker run -d --name='miniob' miniob:latest # 进入容器,开发调试 docker exec -it miniob bash
使用 docker hub 镜像运行。
docker run oceanbase/miniob




