MYSQL POLARDB 学习系列之 拆解 POLARDB 5 snapshot 与 物理页面刷新 (翻译)

53005de8687f7d331f45daa2576ee683.png

Polardb  serverless 提供基于mvcc 多版本控制 snapshot isolation ,与innodb 实现MVCC 的原理一样,将版本信息都存储在undo log 中,在Polardb  serverless 中一个事务依赖于一个snapshot的时间戳来控制那些版本的记录可以被看到。这里他在RW 节点维护一个单调递增的时间戳名为CTS, 将这个时间戳的数据分配给所有的节点。

一个读写的事务需要获得CTS时间戳两次,一次在事务开始的阶段,cts_read 第二次在事务commit的阶段。 当事务提交时,它将cts_commit连同它修改的记录一起写入。所有记录和撤销记录都保留一个列来存储修改它的事务的cts_commit读取事务.

然而对于超大型的事务,CTS commit 不能对要修改的行立即的更新,因为这将触发大量的随即写操作。因此,cts_commit 列需要异步的被更新,这会引起一个问题,当前事务不能通过cts_commit 字段来鉴别当前行的行的版本,这个问题的解决通过查看RW 节点上的CTS 日志的数据结构解决。

CTS 日志是一个循环的数组,记录了最近被读写事务使用的cts_commit 时间戳,假设最近的1000000 个时间戳。如果事务还没有被committed, 则这个位置的值为NULL。

获取时间戳并决定记录是否可见在事务的处理中是一个高频出现的过程,我们通过单边RDMA 来优化时间戳的获取和CTS 数组的访问。CTS时间戳计数器是使用RDMA CAS[42]原子地获取和递增的。此外,CTS日志被放置在一个连续的注册到RDMA网卡的内存区域,以便RO节点可以通过单面的RDMA读取并更有效率的使用。相较于使用RPC的方式这样的方式他在不占用RW CPU的资源的情况下提高了性能,避免了RW 节点成为系统运行的瓶颈。

页面物理刷新

传统的数据库系统定期的将数据页面刷新到永久存储的系统上,然而这样的问题显然在有大量的介于RW 中的内存节点和POLARFS 之间的网络交互在我们的polardb serverless结构中,而这样的操作将影响一些更高级的数据写入的性能,如日志写,和读取页面的操作。

为了解决这个问题,Aurora 提供了一个概念 log is database. 他将redo  log 记录作为页面的增量的存在,将最新版本的页面应用到redo log通过这样的方式来解决日志写入与存储之间的矛盾问题。日志首先持久化到XLOG服务,然后异步发送到一组页面服务器。每个页面服务器负责一个数据库分区,独立地重放日志,生成页面并服务GetPage@LSN请求。

PolarDB采用了类似的方法,更接近于Socrates 系统,我们扩展了PolarFS,使日志和页面分别存储在两种类型的块中(即,日志块和页面块)。重做日志首先持久化到日志块,然后异步发送到页面块,在页面块中应用日志来更新页面。

重做日志需要持久化到日志的chunck中,然后异步发送到page chunck中,在页面的块中,通过日志来更新页面。与Socrates的不同之处在于,为了重用PolarFS组件并最小化更改,日志只发送给页面块的leader节点,leader将通过ParallelRaft实现页面并将更新传播到其他副本。

这样的工作方式增加了应用日志操作的复制成本,但这并不是十分重要的因为应用日志是一个异步的操作,并不会影响我们的核心工作。同时基于数据库通过数据的复制的一致性,保证了POLARDB 不会像Aurora 需要通过gossip协议在存储节点之间保证数据的一致性。

e6d2c95ec63f64c179f75c1fd6f5c1ce.png

如上图中的描述,PolarFS中的存储节点可以同时托管多个log chunk和page chunk。在事务提交之前,RW将重做日志文件的更改刷新到日志块中。当写入的数据复制到三个副本后,事务才可以提交。之后,RW将重做日志的更改分解为日志记录。根据每条日志记录所涉及的页面,一条日志只会发送到相应的页面块,该页面块的分区受该日志记录的影响。

每个页面chunk维护一个数据库的分区,在页块的leader节点接收到日志记录之后RW节点立即将这些记录保存到副本以确保数据的持久性,此后将这些数据插入到内存中使用页面id作为键的哈希,然后RW中的脏页可以被清除。但如果这个过程没有完成,则RW节点会在本地缓存持续保留脏页,同时脏页也不会在远程内存中被清除。

在后台,老的版本的页面从缓存或磁盘中读出并与内存中的散列的日志记录进行合并。此后,新的版本的页面将被重新写回,此时底层的存储会支持多个版本的页面,也支持快速的时间点恢复的方式,执行GC是为了在后台收集重做日志和就的版本,当服务的在请求页面的时候,leader节点合并缓存和磁盘中的版本,并将页面上的记录记录在内存散列中,生成最新的页面并将其返回给数据库节点。

———————————————————————————————

下面将有个人的理解将上面的内容用自己的理解来写一遍

1  redo 作为数据固化的唯一方式,写入log chunk

2  在写入后,redo 的日志会通过  libpfs接口进行解析ingq将里面的数据按照顺序写入队列

3  这些数据将再次写入到数据页面的 chuck,此时日志和数据都写完了

4  此时脏的数据可以被清除了

而在上面的做这些事情的情况下,实际上同时还做了老版本的数据和新的版本的数据标记在hash列表中,此时系统的页面就有多个版本了,此时就具备了事务的回滚的能力。

5  当读取页面的时候,主节点将多个版本的页面进行合并并将记录在hash表中更新。

779c5f0af2a0794caa1b9eef78fdb46e.png

68dd9b0288b8c7e49fc2a2bc841f7f7e.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值