今天讲一下 Go 在我们 TiDB 的应用。我先自我介绍一下,我2012年自己创业做基础架构方向的创业,但是没有做起来,然后去了360基础架构组搞 MySQL 的开源中间件。后来觉得中间件这个方案是一个会受到限制的方案,于是我们就开始探索可能需要像NewSQL 的东西。再后来发现 TiDB 也在做同样的事情,所以就加入了 TiDB。我今天主要讲 TiDB 从测试到优化。
图1
大家知道做数据库非常难,做单机数据库就已经很艰难了如果做一个分布式的,而且是带SQL、带事务的数据库,更是难上加难。对于我来说在做 TiDB 的过程中觉得最难的一点,就是怎么确保我们写的代码是对的。我们在内部将测试分很多块,一个是最简单的单元测试,还有集成测试,都是持续在跑。比如说,我们在 Github 提交一个代码就会跑我们的单元测试和集成测试,每天和每个星期跑的测试量不一样,除了这些我们还有一个叫薛定谔的测试平台待会我会给大家介绍一个 Failpoint 的测试框架,最后会讲讲我们怎么做 TiDB 内存上的优化,演讲的过程就像做软件的过程,我们先把事情做正确,再把事情做快。
我们是一个 toB 的公司。toB 公司跟互联网公司不一样,我们的试错成本很高,我们不能像那些互联网公司一样上线之后,让客户给测或者有灰度策略。我们不能做这些事情,所以我们内部做了很多测试相关的事情。
图2
这个是我们主要的产品概览。TiDB 可以认为是 SQL的引擎,可以发给存储,存储分两块,一个是 TiFlash,一个是TiKV,这就是我们的行存和列存。PD 是做数据调度的部分,大家可以看到,Go 用在除了存储外的所有组件,包括 PD、TiDB 、Binlog、还有周边的 k8s、薛定谔等。
图3
给大家讲一下 TiDB 的 SQL Layer 架构。首先从左边看过来是一个 SQL,它先解析协议,拿出来 SQL 以后进行 Parse,解析成一个一个的 AST 树,然后进行改写,改写变成我们内部表示的一个Logical Plan 的树形结构。然后我们会做两个事情,先对这个树做一个逻辑的优化,逻辑优化就是对它做关系代数上的等价转化,这里还不涉及走哪个执行路径。之后到了物理优化,物理优化就会找出比较优的路径,比如是否走索引,是否直接扫表等等,这里会通过统计信息里面的概要信息来计算出一个代价,来选择路径,之后下面就是一个分布式的执行器,当然还有一些别的模块。这个 SQL Layer 是非常复杂的东西,要实现整个 SQL 的逻辑有很多组合。那么我们怎么测试这个复杂的东西呢?
图4
我们在分布式系统里面遇到的一些测试问题,这些问题可能出现在所有的模块,不止是我们自己写的模块,它有可能遇到磁盘坏了、内核的Bug、网络断了、CPU 有问题、包括时钟有问题等我们都遇到过;软件层面的问题就更多了,包括我们自己写代码的 Bug、、协议栈出现 Bug 等。我们怎么办呢?
图5
我们会引入一个随机的错误注入的框架,它叫做 Schrodinger(薛定谔平台),它是可以通过配置文件来选现在要组建的集群,比如说选多少个 CPU、多少个内存、运行几个TiDB 节点、几个PD节点,它会帮我把配置的集群拉起来,我们跑这些测试的过程中会注入一些随机的错误,我们通过这个平台发现了非常多之前自己测试时没有意识到的问题。
图6
这个就是我们的界面,我们通过 Create new box 就可以启动一个测试,一旦出问题就会汇报回馈给我们,。有非常非常多的测试。,Schrodinger是一个可以随机注入错误的平台,这个错误是不能预知的,。Schrodinger 很多时候会帮我们完成随机测试。那么对于一些确定性的测试方案该如何注入呢?
图7
我们在 Go 里面注入一些埋点。Failpoint 是指比如说一个正常函数正常返回是下面这个 Default 字符串,那我们注入一个 failpoint。这里有一个注释,最后会有一个 Return SomeFuncStringDefault 的语句,如果我们把它激活,他在函数进来就会返回这个值。有这个东西,我们就可以在关键路径注入任意错误,包括可以随机触发,有概率性的触发这个事情都可以做到。为什么要有这样一个 Failpoint 的东西呢?就是因为有一些我们已经确认过会触发某些问题的场景,比如说在客户那里或者在薛定谔我们在自己工作中遇到一些问题,我们已经查明问题,就需要把它加入确定性测试中,保证我们以后改动不会再出现这个问题(回归测试),这个时候我们需要借助于 Failpoint。原因一方面就是因为随机性的问题很难重现,所以我们用这种方法重现,另一方面是我们自己想出来的问题不能通过薛定谔平台去随机注入,所以。Failpoint 是薛定谔测试的一个补充。