经典论文研读:《F1:A Distributed SQL Database That Scales》

一 简介

F1是Google提出的分布式关系型数据库,支持便捷的水平伸缩。这篇论文是NewSQL分布式数据库架构的基石。论文首先定义了F1分布式数据库设计的关键方向:

  • 可伸缩性:数据库要提供对业务透明的水平扩展能力,并支持数据迁移、数据rebalance
  • 可用性:提供高可用的数据服务
  • 一致性:这里特指强一致性
  • 易用性:需要支持索引、ad-hoc类查询接口
    F1的设计理念是兼顾传统SQL数据库的功能完备于NoSQL数据库的灵活伸缩能力。

F1采用Spanner作为基础数据库(Google提出的分布式NoSQL数据库,后文介绍),并继承了Spanner数据库的特性。在此基础上,F1扩展出了分布式关系型数据库关键能力:

  • 分布式查询
  • 事务级索引
  • schema变更
  • 变更历史记录

为了缓解分布式数据库引入到高时延,F1做了一些性能优化的工作,使得F1能在性能上和传统数据库持平。

  • 通过数据schema定义让数据内聚,内举的数据相邻存放,提高了访存局部性,减少了RPC访问次数
  • 由于F1的使用者大量使用批处理、并行处理、异步读取等操作,F1设计了新的ORM接口,可以限定一次查询内部RPC访问次数的上界

二 整体架构

经典论文研读:《F1:A Distributed SQL Database That Scales》_第1张图片

Load Balancer
查询时,默认访问最近DataCenter,并在必要时执行负载均衡。
F1 Server
F1 Server无状态(几乎)的数据查询服务,不存放任何数据,而是通过访问Spanner查询数据。
Slave Pool
Slave Pool由F1进程组成,整个集群共享Slave Pool中的进程。
F1 Master
当计算并发度较高影响时延时,F1 Master会从Slave Pool中分配进程交给F1 Server管理。Slave进程会分担部分查询工作。F1 Master会监听Slave进程的状态。

三 数据模型

表层级结构

广告场景中“customer->canpaign->adgroup”的逐层一对多的表层级关系,抽象为root表和子表的模型。其中customer表为root表,campaign是customer的子表,adgroup是campaign的子表。
经典论文研读:《F1:A Distributed SQL Database That Scales》_第2张图片

子表与其父表存放在一起(就好像子表是父表中的一列),这样存放有几点好处:

  • 当想查询一个customer下的的所有adgroup时,只需要简单的range查找即可
  • 当需要进行双表Join操作时,只需要利用有序数组合并的算法即可
  • 更新时,由于同一个customer的数据存放在一起,可以避免单个更新操作引起Spanner集群事物同步

protobuf列类型

F1中对protobuf列类型做了很多扩展,可以将protobuf列看作是可嵌套的schema结构。F1在SQL语法上也做了相关的支持。
经典论文研读:《F1:A Distributed SQL Database That Scales》_第3张图片
索引
支持本地索引和全局索引。
本地索引:在建立索引时,必须确定root表的主键值(如CustomerId),这样的设计是为了限定索引的覆盖范围,避免跨机器构建与查询。
全剧索引:无需指定root表主键值,但是性能会比较差,尤其是面对大批量事务操作时

四 Schema变更

F1支持schema变更有很大挑战,有如下几个原因:

  • F1是一个庞大的分布式数据库,其数据中心扩地域分布
  • schema结构加载在本地内存中,很难保证跨机器原子更新
  • 在schema变更时,需要保证查询和事务持续进行
  • 系统的可用性和时延不能收到schema变更的影响

为了避免造成不可接受的影响,schema变更采用分批更新模式,即不同F1 Server上使用的schema可能不一样。
同时,为了解决schema不兼容变更导致数据库崩溃的问题,F1也做了一些设计:

  • 每个Server至少加载两个schema版本,但是只使用其中一个对外提供服务;当时机合适的时候,会释放旧版本schema
  • 两阶段提交:先更新schema,然后准备好数据,最后再提供服务

五 事务

F1支持三种事务:

  • 快照
    这是一种只读事务,使用方可以指定时间戳获取特定的数据版本。
  • 悲观事务
  • 乐观事务

F1在每行数据中隐式插入了一个行锁,行写入时会记录当前更新的时间戳;当执行批量事务时,会加一个短周期的悲观锁,用来依次校验时间戳是否重冲突,一旦冲突,则事务提交失败

F1默认使用乐观锁,但是乐观锁有两个问题:

  • 乐观锁依赖于行锁,而只有已经存在的行,才能加锁;当数据中出现新增行时,可能出现数据覆盖
  • 当并发度比较高时,吞吐量低

支持使用方自定义列簇锁

行锁本质上是对一行中的所有列加锁。F1也支持使用方通过自定义“列子集锁”,这样可以增加并发吞吐。

六 修改历史记录

F1会记录所有的变更操作,这写记录有很多变种用法:

  • 用于通知变更
    类似于DB的bin log。
  • 用于更新Client缓存
    客户端会缓存数据库的部分状态。客户端可以通过监听变更记录来更新缓存。

七 客户端设计

F1客户端支持ORM接口、SQL接口和NoSQL接口。

八 查询处理

经典论文研读:《F1:A Distributed SQL Database That Scales》_第4张图片

F1查询时会根据Query执行中心查询或者分布式查询,上述例子是一个分布式查询的例子。F1会将SQL解析成多个步骤,执行类似于MapReduce的拆解和聚合。在查询时,所有的数据访问均为远程访问。

在访问时,时延主要来源于两个地方:网络和磁盘吞吐。其中网络的延迟可以通过批访问和流水线访问来缓解;磁盘延迟通常是因为硬件吞吐量限制,而F1底层使用的CFS分布式存储,吞吐量量得到了保障。

你可能感兴趣的:(论文研读,数据存储)