FoundationDB 学习 - 事务流程

做者:唐刘sql

不久以前,FoundationDB (后面用 fdb 简化) 从新开源,对于你们来讲,这真的是一个很是好的消息。我也在第一时间下载了 fdb 的源码,开始研究,一方面是看咱们能在什么方面可以借鉴,另外一方面也是须要给一些朋友回答,TiKV 到底跟 fdb 有什么不同这样的问题。缓存

关于 fdb 的研究,本身预计会有几篇,此次是第一篇,来聊聊我最关注的一个问题 - fdb 是如何实现分布式事务的。app

关键组件

在开始介绍以前,要先说说 fdb 的关键组件。分布式

Coordinators

全部的 clients 和 servers 都是经过 cluster file 来链接到 fdb cluster,而这个 cluster file 就包含的是 coordinators 的 IP:PORT 列表。全部的 clients 和 servers 都会使用 coordinators 链接到 cluster controller。性能

Cluster controller

Cluster controller 是经过选举产生的(fdb 貌似使用的是 Paxos,这个后面会详细研究一下)。Cluster Controller 就是控制整个集群的,它是全部进程的入口,会监控进程是否挂掉,告诉某个进程相关的 role,以及在全部进程之间传递系统的信息。Clients 也经过 cluster controller 来实时的同步最新的 proxies。学习

Master

Master 主要是用来协调写子系统的,一个写子系统包括 master,proxies,resolvers 和 transaction logs。Proxy,resolver 和 transaction log 是一个总体单元,若是任意一个失败了,那么咱们就会从新找一个来将他们所有替换。Master 会给 proxies 分配 commit versions,对数据进行分布,以及全局的流速控制。优化

Proxies

Proxies 会提供 read versions,提交事务以及跟踪 storage servers 的 key ranges。若是要提供一个 read version,一个 proxy 会问其余全部的 proxies 当前最大的 committed version,而且同步的检查 transaction logs 并无被中止。Master 流速控制可能会减缓提供 read versions 的频率。server

对于一次事务提交,当只有下面操做所有完成,才能认为成功:sqlite

  • 从 master 获得一个 commit version进程

  • 使用 resolvers 来肯定当前事务并无跟以前已经提交的事务冲突

  • 让 transaction 持久化到 transaction logs

全部以 xff 开头的 key 是系统保留前缀,用来存放系统的元信息。任何对这段 key range 的修改都会 通-- 过 resolvers 同步到全部的 proxies。元信息包括数据的 key ranges 以及哪些 storage servers 有这些 range,其实也就是数据的路由表了。Proxies 也给 clients 提供相关的信息,让 clients 进行缓存,若是缓存缺失,就从 proxies 从新更新。

Transaction Logs

Transaction logs 会按照 version 的顺序接受 proxy 发过来的提交,并会使用 append only 的方式将修改的提交持久化到硬盘。在数据被写入到磁盘的时候,也会通知 storage servers 有相关的修改操做,让 storage servers 去获取而且 apply 到 storage servers 里面。

Resolvers

Resolvers 用来肯定不一样事务的冲突。当一个事务的 read version,读取了一个 key,在 commit 以前,另外一个事务写入了新的值,这时候就会有冲突。 Resovler 会在内存里面保存 5s 的全部写入提交,用来判断冲突,这也就是意味着,fdb 的事务执行时间不能超过 5s。

Storage Servers

Storage servers 就是存放数据的地方,fdb 会将数据按照 range 切分,存储到不一样的 storage servers 上面。Storage servers 会在内存里面保存最近 5s 的修改(Versioned data),若是一个 client 的 read version 超过了 5s,那就会过时出错了。Storage server 有 ssd 和 memory 两种,ssd 其实用的是 sqlite3。

流程

上面大概介绍了 fdb 的关键组件,这里就先来讲说事务。Clients 会先用一个 read version 读取全部的数据,而后在本地修改,最后再将全部的修改一块儿提交到 proxies,这其实也就是一个乐观事务模型。具体流程以下:

  • 开始事务

    • Clients 从 proxy 获取一个 read version

    • Proxy 会批量接受 clients 的请求,若是超过了限流控制,额外的请求会排队

    • Proxy 问其它的 proxies 当前最大的 commit version

    • Proxy 返回最大的 commit version 做为 read version

  • 读流程

    • Client 根据 read version 以及须要访问的数据的 key 或者 key range 找到对应的 storage servers

    • Storage server 接受到以后,若是发现 version 太老,结果返回错误。若是发现数据还不存在,就等或者超时

    • Storage server 会根据 read version 找对应的数据,并返回给 client

  • 提交事务

    • Client 将修改,read version 以及 read ranges 和 write ranges 提交给 proxy

    • Proxy 仍然是批量的接受请求

    • Proxy 将 range 切分而且发到不一样的 resolvers,若是 resolver 判断有冲突,结束事务

    • Proxy 经过 master 获得最近的 commit version

    • Proxy 将修改的数据按照实际的数据分布切分,加上 tag,推送到 transaction log servers

    • Transaction log servers 回复 proxy 说 log 已经落盘

    • Proxy 给 client 返回事务提交成功

能够看到,整个流程仍是很简单的,这里还须要注意几个后台流程。一个是 storage server 从 transaction logs 读取数据:

  • 根据提供的 version 和 tag 从 transaction logs 拿数据

  • 将数据读入到 storage server 的 Versioned data

  • 将数据写入 storage engine

另外一个就是 version 的更新,proxies 会按期的生成一个空的 commit request 来增长 commit version,这样 transaction logs 和 storage servers 的 version 都能增长,就能处理一个集群若是没有任何写入,后面新的读取也能按照 version 读到对应的版本,不会无限制的等待。若是个人 read version 比当前 storage server 的最大 version 要大,其实并不能保证读到正确的数据。为啥会作这个,主要是 fdb 用的时间戳来当的 version。

小结

上面仅仅是对 fdb 事务流程的简单介绍,几个 concern 的点:

  • Proxy 会跟其余的 proxies 交互问最大的 commit version,若是 proxy 多了会不会有性能问题
  • Resolver 若是 range 太多会不会也有性能问题

能够看到,fdb 在 resolver 那边其实就是将事务排队了,因此虽然外面看起来是乐观事务,但对于冲突严重的状况,性能也比较不错。以前我一直觉得 resovler 会是个单点,但后面知道 resolver 也是能够 scale 的。并且 fdb 本身也说作了不少的优化,保证了整个的性能。

后面我会详尽的捣鼓折腾下 FoundationDB,作下 benchmark,也正在将它集成到咱们的 YCSB 里面,毕竟对我来讲,至少 fdb 那套 deterministic 理念是能够借鉴学习的。若是你对咱们相关的 TiKV 工做感兴趣,欢迎联系我 tl@pingcap.com。

相关文章
相关标签/搜索