在 TiDB Hackathon 2021 赛事中,渡渡鸟复兴会赛队的作品“MVCC 时光机”充分的利用 MVCC 特性,加强 MVCC 数据的查询、整理、恢复的能力,提高问题处理的效率,摘得了赛事的 “三等奖” 。
这个项目是给运维同学在某些时候救命的功能。它通过 SQL 很好地解决了运维的操作问题。更赞的是,该项目引入了多 Safepoint 机制,也就应该给 TiDB 集群定期做一些全局 Snapshot,进行快速轻量级的逻辑备份。
作为一款分布式数据库,完善的高可用和容灾机制可以说是 TiDB 的核心特性。然而在生产中,理论上的灾难恢复和实际上要面对的却可能大相径庭。不可预知的硬件故障、自然灾害导致的断网断电固然可怕,而把生产环境当成测试环境的误操作、有漏洞被黑客攻击、业务出 Bug 写坏数据反而可能是更高频的事故。
队员之一 @disking 曾经就职于游戏公司,对这种“人为的失误”感受颇深:业务同事写的代码中出了一个 bug,在版本上线后的几个小时内被别有用心玩家利用获得了许多不正当的收益,这时候就要进行回档操作,让游戏数据回到某个具体的时间点,要实现的就是类似“时光机”的功能。手动回档、数据丢失造成的损失都会给团队带来非常大的麻烦。
现在物理上的故障处理 TiDB 已经给出了像 BR 工具、两地多中心方案等解决措施,而一句`rm -rf /*`,或者“实习生误操作”导致的错误数据写入带来的风险却更加不可控,这也是 TiDB 缺失的一大块拼图。
因为 TiDB 的事务是基于 MVCC 的,所以一段时间内的旧版本都在,理论上对于上述人祸,都是能够直接进行手动恢复的。但是现有的功能和计划中的功能相对较弱:
很可能需要排查数据损坏的情况,目前只能指定 ts 去读一个时间点的数据,要查看某条记录的变化历史太麻烦RECOVER TABLE 只能恢复 DROP/TRUNCATE 这种 DDL 操作,对 DML 没招GC Safepoint 之前的数据恢复不了,如果想保留长时间的数据,又太费空间了恢复数据要先把数据 dump 出来再重新写入,太慢了
因此充分的利用 MVCC 特性,加强 MVCC 数据的查询、整理、恢复的能力,就能提高问题处理的效率。MVCC 不只是可拿来暂时性地处理事务隔离,也可完全做为冷备,相比于外部的备份,其优点是可以更省空间,恢复数据也更方便更快。
@disking 其实最早在第二届 Hackathon 时就有了这个想法,灵感来源于 Oracle 的 Flashback 功能。当时他就把这个想法放到了研发的讨论群里面却没有正真获得回应,而当时的他还只有少数的精力来组建自己的战队。虽然不至于说耿耿于怀,但是这么有趣、有用的想法当然要找个机会实现一下,这次的 Hackathon 就是一个实现想法的好机会。
总的来说就是围绕 TiDB 的 MVCC 机制做了一些(乍)看起来比较 fancy 的操作,比如查询表记录的 MVCC 历史、篡改 MVCC 记录以及根据 MVCC 记录做到瞬时版本回退(Flashback 操作)等。
整个项目比较核心,或者说比较实用的点还是 GC SavePoint 和 Flashback 功能的组合拳,通过设置定期的快照备份,利用 TiDB MVCC 的机制做到 TiDB 存储内部的“冷备“,在一些关键时刻还是有救命效果的,毕竟只需要一条 SQL 就可以达成整表数据的 Flashback。
添加 gc_savepoint 系统表,能够最终靠 SQL 增删改查来进行管理。
添加 flashback table ts SQL 语句,用于指定 table 进行数据还原。意义是将表还原至不超过 ts 时间戳指定的版本。
将时间范围写入 table schema 并触发 DDL 操作,DDL 同步完成即可返回操作成功。
TiDB 请求 TiKV 时,需要将要忽略的 ts 区间放在请求中发给 TiKV。
结合上面的 MVCC 查询,我们大家可以看到现在表数据中的“变化渡渡鸟”记录在某个时间节点前还是“时间渡渡鸟”,通过 Flashback 操作,我们成功让数据变回了曾经的模样,把“时间渡渡鸟”召唤了回来。
在实际的灾难恢复场景中,如果我们一不小心错误地修改了某个表的几条数据,甚至是误删了整个表,都能够最终靠 Flashback SQL 来将其一键恢复到任意 MVCC 记录版本。
目前的实现仅基于 TableScan 进行了 Demo,还有一些 IndexScan 和点查询的适配工作不进行;有些 TiDB 的生态工具是越过 SQL 层进行数据查询的,这方面的兼容性也是接下来需要仔细考虑的问题。
@JmPotato 刚刚结束了自己的学生生涯,现在是一名 PingCAP 的研发工程师,@RinChanNOW 是他的本科室友,也加入到了本次的项目之中。他们的另一位室友也在字节跳动分布式系统研发的部门实习。
对于很多学生来说,成为一名应用开发者或者算法工程师或者进入 AI 行业都是更加主流的选择,为什么这么“巧”,他们不约而同地加入这个行业?
@JmPotato 表示,是他开了这个“头”。2019 年年底,他在听播客的时候偶然听到了东旭关于分布式数据库的分享,当时也不知道 PingCAP。后来进行有关的学习,在学习 Raft 的过程中接触到了 TiKV、TiDB 这些项目,才慢慢了解到原来它们都是 PingCAP 的产品。那时候 PingCAP 刚好在招聘实习生,自己也觉得心驰神往,做了很多相关的准备就开始面试、进入公司实习直到现在正式加入。
@RinChanNOW 也表示,是 @JmPotato 的这份实习为整个宿舍都打开了一个全新的世界,还记得当时大家一起实现一个简单的 Raft 协议,从当时就可以感觉到分布式系统的奇妙,不是被动的内卷,而是一种发自内心的热爱和向往。
虽然 @RinChanNOW 之前学习过分布式系统的相关知识,但是本身在学习和工作中都没有实际接触 TiDB 的经验。作为一名外部开发者,怎么加入到这场关于 TiDB 的 Hackathon 中?这也是很多同学对于 Hackathon 活动望而却步的重要原因。@RinChanNOW 就绝对没这样的担心。一方面 TiDB 的文档丰富,体系化的学习起来并不费力;另一方面 TiDB 的社区非常活跃,无论是 AskTUG 还是 TiDB internals 或者说就是 GitHub 上,都能遇到很多志同道合的伙伴,他们也都愿意帮助 new TiDBer 快速融入社区。
除了 TiDB 与 TiKV 的开发环境准备之外,需要做的一个准备工作就是了解 TiDB 和 TiKV 的代码结构与它们的数据流,也就是要去大致了解它们的源码,而这也是最耗时间的一个过程,所以我的代码量并不大,但是却花了很久才写完。
我根据需要改动的部分,结合 PingCAP 的官方博客,对源码进行了一波学习:
队伍中的 @disking 能算得上是 Hackathon 的老玩家了,除了这次的 TiDB Hackathon,@JmPotato 和 @RinChanNOW 也都程度或深或浅地参与到了其他类似的编程竞赛中,谈及不同赛事体验的差异,他们有着统一的看法:很多编程竞赛更多是面向学生的,留一些有着明确目标甚至是标准答案的作业,更像一场检验编程能力的考试,比拼的可能是谁的实现更优雅,效果更加好。而参与 TiDB Hackathon 就是一种完全不同的体验。TiDB Hackathon 更偏向实操,没明确的选题,是一场未知的冒险,比起代码实现更重要的是创造力和思考,只有真正在用、真正参与到产品迭代中的开发者才能感受到其中的乐趣。
但是受到疫情的影响,近两年的 TiDB Hackathon 虽然热闹,还是少了一点气氛。如果有机会,还是期待明年的 Hackathon 能够和各位选手来到同一空间现场交流,来一场线 小时密集开发。