大雨,闲来无事,学习一下Spanner的最新进展。
搞数据库研发的应该没有不知道Spanner[1]的,其在2012年就做成了一个无限扩展、自动均衡、高度可用、支持分布式事务、全球化的数据库,刚看到论文时,无限崇拜,惊为天人。当时我也写了一篇文章学习[2],不过经过这几年我们实际开发表格存储[3]并在云上对外服务的情况看,当时那篇文章学习的深度还是很浅的。
最近也看到有一些数据库跟Spanner做对比,比如TiDB和OceanBase,祝愿这些数据库能尽快学习并超越,为中国的基础软件构建添砖加瓦。
广告时间:阿里云表格存储正在招聘SQL引擎方面的专家,以助力计算引擎的易用和高性能,欢迎自荐or推荐,一旦成功,奖励优厚。
在Spanner 2017的论文中,主要描述了作者在为一个强大的分布式数据库内核添加SQL访问过程中的一些思考,对于有志于做类似事情的系统来说,有不错的借鉴意义。
下面基本还是按照论文的节奏写,以利于记录对论文的理解,不过笔者对SQL引擎细节了解甚少,错误之处敬请指点。
介绍
论文首先说了做SQL的原因:没有powerful的查询语言,应用开发不容易。想必这个都能理解,看看MegaStore/Percolator复杂的应用代码,不搞数据库的工程师恐怕不太容易写对,特别是碰到了错误需要重试的时候。在SQL开发成功后,很多应用都迁移了上来,目前大概每秒千万的QPS(考虑到其全球只有少数的实例,这个数字不是很高)。
论文也说明了他们使用的技术并非非常独特,比如让计算靠近数据,提高并发度,分区裁剪等技术都是常用的技术,其主要贡献在于提供了分布式查询执行的经验,自动解决查询重启问题,SQL选择的考虑,以及什么样的存储格式更有利于AP/TP混合的系统。
Spanner 2012背景
简单介绍一下之前Spanner论文里面的概念,后文会用到。
shard:一个表水平分为很多的分区,每个分区叫shard,也有一些其他系统叫partition,segment,slice等
parent-child relation:意思是两个表shard列一样,比如用户表和用户记录表,他们的第一列PK都是用户ID,这样可以将相同用户ID的行物理存储在一起,利于查询
paxos group:每个shard都是一个paxos group提高可用性,反之未必,一个paxos group可以包含多个shard(利于IO优化)
一致性控制:悲观锁和MVCC同时都有,所以后面提到查询优化会说如何降低锁的粒度
coprocessor framework:不是通常理解的coprocessor,主要是根据logical range key找物理shard的过程,同时涉及到paxos replica的选择,专门抽出来的原因猜测是replica选择是个复杂的问题
分布式查询执行
SQL执行分为解析、编译、优化、执行。这里Spanner重点强调了几个分布式查询执行的元操作器,典型的是Distributed Union和Distributed Apply。
Distributed Union:如何能将任务并行化,以及并行化之后如何合并结果。以最简单的表scan为例,简单想想就是直接跟各个shard发请求然后合并就可以了(当然,并发多少也要思考)。Distributed Union的贡献是形式化的增加了一个算子,这个算子能将串行任务变为并行(约束保证变换之后保持等价)。既然是一个算子,就具备了查询树上节点上移下移的能力(跟其他算子一样)。以下面SQL和图1为例,最土的办法是先每个表运行Distributed Union,然后Join、分组、排序、过滤等。但是考察之后,发现两个表的shard key一样,可以shard内做,所以Distributed Union算子可以上推,简化了执行逻辑。跳出来看,我觉得这个概念有点像将DBMS的查询引擎套到Spanner上产生的,由单机到分布式的过程中,某些运算符需要被分布式。
SELECT ANY_VALUE(c.name) name, SUM(s.amount) total
FROM Customer c JOIN Sales s ON c.ckey=s.ckey
WHERE s.type = ’global’ AND c.ckey IN UNNEST(@customer_key_arr)
GROUP BY c.ckey
ORDER BY total DESC
LIMIT 5
图1
Distributed Apply :主要解决的问题是join里面的随机读问题。比如
SELECT * FROM user_table t WHERE t.name = ‘kkk’, name建立了索引
在单机数据库中不存在这个问题,根据name索引查询user id,然后立刻反查主表就可以。但是在分布式数据库中,主表和索引表是两个表,物理隔离,此时的查询更像是一次主表和索引表的join,Distributed Apply就是想尽可能高效的解决此类问题。核心思想是不能每次都反查,RPC太昂贵,要聚集成batch,然后批量的发给shard server,没准还可以发现不少duplicated key。
查询分区裁剪
分布式数据库里面每个表都有很多的shard,查询时候一般不会涉及这么多shard,那么就要裁剪掉那些不相关的。作者认为shard粒度太大了,需要更细的range,并且介绍Spanner本来就保存了比shard粒度更细的range,以便做这种裁决。
这里分区裁剪除了减少查询数据量外,还能更好的控制锁粒度,从而提高并发(range锁的威力还是很大的)。文中作者提到要在分区裁剪的cost和seek的cost之间做一个权衡,我不太理解,因为我认为显然seek的代价要大于分区裁剪,难道Spanner的分区数已经到了极其庞大的地步?
后面介绍了Filter tree,就是一般的查询树,不多说。
整体看,这一节没什么特别的,根据查询条件尽可能的减少访问范围是所有引擎都会考虑的,也没有特别普适性的技巧。从range锁优化这个角度看,Spanner越来越像RDBMS了。
查询重启
分布式系统里面会碰到各种错误,比如网络分割,机器重启,进程崩溃,文中还提到了distributed wait和data movement。Distributed wait可能的原因是因为机器忙导致paxos group里面follower跟不上leader。Data movement是某些数据(好像叫Dictionary)可能正在被动态迁移到其他可用区,比如从美国迁移到欧洲,这个Spanner 2012里面有论述。
上面的错误都可能导致某些查询执行中断,此时有两个选择,一个是让用户重试,比如著名的指数退避原则,但是作者认为这样难用,而且容易导致query长尾,同时也影响了架构的灵活性(对动态负载均衡的约束)。
另一个是系统内部重试掉,这个符合作者的意图,其认为该方法有很多好处,包括:简化编程、降低请求毛刺、让长查询能顺利执行、利于升级、简化Spanner内部处理。在论文下一节里面,作者描述了做到重启的技术,其实就是查询的checkpoint,restart后从哪里开始,这个checkpoint的设计要考虑兼容、高效编解码等方面。
这里的观点我非常同意,系统的接口越简单,内部自动化的机会就越多,同时用户也越爽。
SQL的选择
这里到了有趣的地方了,在Spanner SQL之前,Google内部已经有了几套SQL引擎,那么新的SQL引擎要不要跟他们保持兼容呢?还是跟开源产品比如PostgreSQL等保持兼容?最终,作者认为应该跟公司内部产品保持兼容,这真是一个理性的选择,因为最大的客户在公司内部,保持语法的类似能够最小化用户的学习成本。不过反过来说,如果云化提供给全球开发者使用,这个选择可能就不好了。
作者还干了一件很伟大的事情,就是跟其他SQL引擎共享parser、函数库以及测试用例,以最大程度的保持兼容,这个相当不容易,能做成可能跟Google的文化和代码管理方式有关。关于测试用例,SQL引擎的开发不是容易的事情,好在SQL的测试用例足够多,如果开发一款新的SQL引擎,如何重用已有的测试用例是最重要的事情,没有之一。
AP/TP共存的存储格式
随着硬件的进步和分布式技术的成熟,越来越多的数据库系统开始融合AP/TP功能,避免用户一份数据到处拷贝。
Spanner衍生自BigTable,开始是为了TP设计的,每行存在一起(行存),利于整行读取;但是对于一个AP系统来说,因为经常扫描数据,按照每列物理存储的话(列存),对cache和CPU都会更友好。作者借鉴了PAX[5]格式,就是行存结构的block内部采用列存,其认为这样能比较好的对应TP/AP系统的挑战。功能已经开发完,正在上线中,因为Spanner有后台自动move data的功能,所以可以基于此功能做到用户无感知的换存储类型。我们团队之前讨论压缩比的时候讨论过这个思路,其实人家10年前就发论文了。
最后的忠告
对于那些想做spanner的人,作者想说:For other development organizations that will need to go a similar path we would recommend starting off with the relational model early on, once a scalable, available storage core is in place; well-known relational abstractions speed up the development and reduce the costs of foreseeable future migration。
最后的最后
通篇读过来,不会像读Spanner 2012那么激动。分布式查询虽然比单机查询复杂,不过基本原理已经被研究的差不多了,也没有新的普适的理论被提出来,工程优化基本都在已有的条条框框内,所以这些工作更像是工程经验的分享,实现细节上的考量。虽然如此,我认为这才是最重要的,在一个稳定成熟的产品发展历程中,平平淡淡才是绝大多数。
[1]. Spanner 2012: http://research.google.com/archive/spanner-osdi2012.pdf
[2]. Spanner 2012读后感:spanner 的前世今生 – RaymondSQ – 博客园
[3]. 阿里云表格存储:表格存储_海量数据存储-阿里云
[4]. Spanner 2017: Spanner
[5]. PAX:http://research.cs.wisc.edu/multifacet/papers/vldbj02_pax.pdf