前年有个项目做 OceanBase 迁移,半夜出工单,一条核心 SQL 突然从毫秒级掉到秒级。现场的人查了一整晚,统计信息收了,plan cache 清了,参数翻了个遍,没用。第二天我远程上去,干的第一件事不是查视图,是把源码里这条 SQL 走的访问路径估算函数翻出来,对着统计信息的实际值手算了一遍代价。四十分钟后定位完成,问题出在一个 NVL 包裹的谓词上,优化器对这种谓词用了默认选择率,估行差了几十倍。
现场的人问我怎么想到的。我说没捷径,代码里写着呢。
这件事之后我就动了做这个系列的念头。OceanBase 对我们这代 DBA 最大的善意,就是开源。Oracle 时代遇到解释不通的行为,我们只能翻 MOS、跑 10053 trace、对着黑盒猜。猜对了叫经验,猜错了叫事故。现在不一样了,每一个"它为什么这么干",都有标准答案躺在代码里,等着你去翻。
这个系列一共 36 篇,每篇从课程或生产里挑一个难啃的点,到源码里把答案挖出来。第一篇先把地基打了,源码拉下来,编出来,跑起来。
先想清楚,读源码到底图什么
很多 DBA 对读源码有个误解,觉得那是研发的事,自己把视图和参数用熟就够了。我讲课时常跟学员算三笔账。
第一笔,排查账。生产上的怪问题,十有八九视图里看不出根因。报错信息就一行,背后是哪段代码抛的、什么条件下抛的,只有源码知道。把报错文本拿到代码里一搜,定位到抛错那一行,上下文读五十行,方向基本就有了。这个动作我后面会反复用,它是读源码性价比最高的入口。
第二笔,面试账。OBCE 考试考"是什么",面试官爱问"为什么"。为什么三地五中心是五个副本,为什么 plan cache 命中了 SQL 反而慢,为什么统计信息更新了计划反而岔了。这些问题的答案教材里只有结论,推导过程在代码里。你能把推导讲出来,段位就不一样了。
第三笔,信任账。新上一家数据库,文档写得再漂亮,你心里其实是没底的。翻过代码之后,你知道它的多数派怎么判、内存怎么算、日志什么时候能删,这种底气是看任何二手资料都换不来的。
账算完了,下面动手。
先花半小时看目录,能省后面几十个小时
源码 clone 下来,别急着编。先把 src 目录的结构看懂,这个投入产出比极高。我刚翻 OB 代码的头一个月,一半时间浪费在"这段逻辑到底在哪个目录"上,后来把目录地图钉在脑子里,效率翻了几倍。
src 下面十一个顶层目录,我按 DBA 视角分成三堆。
第一堆是干活的主力,四个。
src/sql管 SQL 的一生,解析、改写、优化、执行、plan cache、会话,全在这。src/storage管数据怎么存,memtable、转储、合并、SSTable、事务、锁,都在这。src/logservice管 redo 日志和 Paxos 复制,高可用的命根子,选举代码也在它下面的 palf/election 里。src/rootserver是集群大管家,建租户、负载均衡、发起合并、调度备份,全归它。
第二堆是打辅助的,三个。
src/observer是进程入口加 MySQL 协议层,SQL 从网络字节流变成内部请求就在这发生。src/share是公共仓库,内存分配、IO 调度、ASH 采样、内部表定义、统计信息的收集执行,全塞这。src/pl管存储过程。
第三堆按需翻,src/diagnose、src/plugin、src/objit、src/libtable,用到再说。
我自己的习惯是给每个目录钉一个钉子。GV$ 视图里某个字段解释不通,先想它归哪个目录管,再进去搜。比如锁相关的视图,直接去src/storage下面找 lock 相关的子目录,比全文乱搜快得多。这个习惯比记住任何具体代码都值钱。
把这堆目录跟 DBA 日常关心的问题对上,画一张地图:
这张图后面每一篇都会用到。先有地图,再看细节,顺序反了容易在几十万行代码里迷路。
有个问题先替你回答了,后面面试也用得上。RootService 是不是独立进程?翻src/rootserver/ob_root_service.h:141,ObRootService就是个普通的 C++ 类,跑在 observer 进程里,集群里同一时刻只有一个 observer 上的它是干活的主角。它是 observer 进程里的一个角色,想成 Oracle 里某个后台进程承担了全局协调职责就行,只不过 OB 里这个角色可以在节点间漂移。
编译,劝退率最高的一步
说实话,OB 编译的门槛不低。我第一次编的时候踩了一晚上的坑,几个关键点先交代,能救你一晚上。
Mac 别折腾。OB 官方构建只支持 Linux,我在 Mac 上试过,工具链的问题一个接一个,果断放弃。正确姿势是找一台 Linux 机器,用官方 dev 镜像,gcc、cmake、依赖库全预装好。
内存要够。编译机 16G 起步,链接阶段特别吃内存。我见过 8G 机器编到最后链接阶段被 OOM killer 干掉的,前面两小时全白等。机器实在小,把并发数降下来,慢点总比挂了好。
流程本身不长:
# 官方开发镜像,工具链全配好 docker run -it --name obdev \ -v $HOME/oceanbase-master:/data/oceanbase \ oceanbase/centos7-dev:latest bash cd /data/oceanbase # debug 模式,带调试符号,后面 gdb 要用 bash build.sh debug --init --make -j8-j8按核数调。第一次编译一到两个小时,看机器。中途报错九成是依赖包或者磁盘空间,照着报错装包就行,没有玄学。编完检查build_debug/src/observer/observer这个文件在不在,在,就算过关。
build.sh 本身值得翻开看一眼,开头几行注释就把构建选项交代清楚了(build.sh:43-48):
echo -e "\tBuildType => debug(default), release, errsim, dissearray, rpm" ... echo -e "\t# Build by debug mode and make with -j24." echo -e "\t./build.sh debug --make -j24"逐行看这段。第一行列出了全部构建类型,debug 是默认项,release 是生产用的,errsim 是错误注入测试用的,这个后面做故障实验能派上用场。后面两行是官方给的示例命令,debug 加--make -j24,跟我上面给的命令一个套路,只是并发数不同。一个脚本开头就把示例写好,这个习惯挺圈粉的。
有一个细节必须强调,一定要编 debug 版。release 版开了编译优化,gdb 跟进去变量全被优化掉,函数内联,调用栈都是断的,没法看。debug 版跑得慢,可我们拿它来学习,又不是拿它跑生产,慢一点完全没关系。
起一套能随便折腾的集群
光有二进制不够,实验要真集群。我的环境是腾讯云上三台机器,obd 部署的三节点集群 obce4cluster,另外一套 OCP 管着。日常验证连 sys 租户:
obclient -h<OBProxy地址> -P2883 -uroot@sys#obce4cluster -p -Doceanbase -A -C自己的测试集群最大的价值是心理负担为零。杀节点、造网络分区、灌爆 memtable、半夜发起合并,随便折腾,坏了重建。这个系列后面 35 篇的实验,全是在这种折腾自由的环境里跑出来的。拿生产集群做实验那不叫学习,叫赌博。
如果你只有一台机器,单机 obd 也能起步,编译、gdb、SQL 链路这些实验都能做。但三副本、选举、负载均衡这些玩法就做不了了,副本都没地方摆。这个系列的实验密度,建议直接上三节点。
这套投入值在哪
最后再算一笔总账。搭环境加编译,顺利一晚上,不顺利两三天,听起来贵。
换来的是一件终极武器。以后每一个 OB 的玄学问题,计划为什么变了、锁为什么超时、clog 为什么删不掉、合并为什么卡住,你都能绕过猜,直接看答案。前面十几年我在 Oracle 上练出来的那套猜黑盒的本事,在 OB 这里可以退役了,这是好事。
课程解决的是"是什么",考试够用。面试官和生产事故问的是"为什么"。这个系列要干的,就是把这两层之间的墙拆掉。
面试怎么答,生产怎么查
面试里被问"你了解 OceanBase 的实现吗",能说出 src 下四个主力目录的分工,再补一句"我用 debug 版编过、拿 gdb 跟过 SQL 的完整链路",段位立刻就不一样了。面试官心里清楚,会说这句话的人,跟只背过课件的人,解决问题时的武器库完全不同。
生产上,这篇的方法论直接可用。遇到看不懂的报错,把报错文本拿到源码里搜,定位到抛错的那一行,读上下文五十行,八成的问题在这一步就有方向了。剩下两成,多半是你搜错了关键词,换个错误码再搜一次。
留给你的三个思考题
面试官问"OceanBase 的 SQL 入口在哪个模块",你答完 src/sql 之后,他追问"RootService 是独立进程吗"。答案是什么?(提示,翻翻 src/rootserver/ob_root_service.h,再看看这个类跑在哪)
你只有一台 4C8G 的虚拟机,想验证三副本行为。你会怎么规划?哪些实验在这台机器上注定做不了,为什么?
回忆你最近遇到的一个 OB 玄学问题。如果要去源码里找答案,你猜它在 src 下哪个目录?写下你的猜测,找机会验证。直觉准不准,试过才知道。