PostgreSQL码农集散地

Oracle的RAC神话被打破了?

文章开始前推荐2个学习环境: 

1、欢迎使用镜像快速体验PostgreSQL/DuckDB强大功能:《最好的PostgreSQL学习镜像》

2、欢迎使用云起实验室: 《免费体验PolarDB开源数据库》

3、PolarDB开源数据库内核、应用等学习图谱:  https://www.aliyun.com/database/openpolardb/activity 

为什么只读实例不能共享一份数据? Oracle的RAC神话被打破了!虽然有点晚,但开源

https://www.bilibili.com/video/BV18m4y1o7yL/

社区版本:
如果数据是完全静止的, 相对比较容易做到多个计算节点共享一份数据, 类似于FDW. (当然即使某一时刻没有写入, 如果之前有过写入的话, PG还有后台vacuum, freeze操作, 可能会改变数据块的内容.)
假设数据是动态的, 主实例会对数据进行增删改操作, 那么:

  • 1、主实例的shared buffer、OS page cache中都可能有脏数据.

  • 2、主实例可能正在修改 data block.

由于有以上2种操作的存在, 如果只读实例(RO)和主实例(RW)直接共享存储有什么问题?

  • 1 只读节点从存储中读取的data block,可能是不一致的, 例如有可能是partial write的页面, checksum都不正确.

  • 2 假设当前只读实例上的某个事务的快照是LSN1, 但是在整个事务中读取到的数据块可能存在比较老的版本(指这个block在LSN1之前经过了修改, 并且还没有从shared buffer或page cache中刷到磁盘中),不符合数据一致性要求。

  • 3 假设当前只读实例上的某个事务的快照是LSN1, 但是在整个事务中读取到的数据块比自身内存中想要的数据要超前(指这个block在LSN1之后经过了修改, 并且已经从shared buffer或page cache中刷到磁盘中, 而且旧的tuple版本也已经被清理),不符合数据一致性要求。

  • 4 假设只读实例通过回放WAL来同步RW节点的脏页, 由于内存有限, 脏页可能从RO节点的shared buffer淘汰出去, 那么再次访问时必须从共享存储读取data block, 类似2和3, 这个block可能是旧的, 也可能是超前的.

因此, 传统的共享存储架构, 主库在用的时候, 备库是不能启动的。仅仅用户HA.

PolarDB:
https://github.com/ApsaraDB/PolarDB-for-PostgreSQL/blob/main/doc/PolarDB-CN/Architecture.md

Image

解决旧页面问题:
维护每个Page到日志的“倒排”索引, LogIndex本质是一个可持久化的hash数据结构。
Image
Image

  • 1 只读节点通过WAL receiver接收从主节点过来的WAL meta信息。

  • 2 WAL meta记录该条日志修改了哪些Page。

  • 3 将该条WAL meta插入到LogIndex中,key是PageID,value是LSN。

  • 4 一条WAL日志可能更新了多个Page(索引分裂),在LogIndex对有多条记录。

  • 5 同时在BufferPool中给该该Page打上outdate标记,以便使得下次读取的时候从LogIndex重回放对应的日志。

  • 6 当内存达到一定阈值时,LogIndex异步将内存中的hash刷到盘上。

解决未来页面问题:
控制刷脏不超过logindex的last LSN.
Image

  • 1 所有只读节点回放到的最老的位点为T4位点。

  • 2 主节点在刷脏时,对所有脏页按照LSN排序,仅刷在T4之前的脏页(包括T4),之后的脏页不刷。

  • 3 其中,T4的LSN位点称为“一致性位点”。

本期问题1:
为什么PG社区版本只读实例不能共享一份数据?

  • a. 只读实例可能读到旧页面

  • b. 只读实例可能读到未来页面

  • c. 只读实例可能读到bad block(partial write导致)

  • d. 只读实例无法通过WAL日志同步主实例的脏数据

答案:

  • abc

解释:

  • 参考本文内容

本期问题2:
为什么PolarDB可以共享一份数据?

  • a. 通过logindex存储WAL的倒排数据: pageid和lsn的映射关系, 解决只读实例block从shared buffer被淘汰后从共享存储中可能读到旧页面的问题.

  • b. 控制主实例shared buffer刷脏, 不能将超过logindex last lsn的脏页刷盘. 解决读取到未来页面的问题.

  • c. 所有脏data page都需要从wal回放, 不会出现bac block(partial write导致)

  • d. 只读实例支持logindex, 不需要回放wal的data record, 只需要解析wal中pageid和lsn的映射关系, 并存储到logindex中.

答案:

  • abcd

解释:

  • 参考本文内容

欢迎关注我的github (https://github.com/digoal/blog) , 学习数据库不迷路.  

近期正在写公开课材料, 未来将通过视频号推出, 欢迎关注视频号:

Image

文章中的参考文档请点击阅读原文获得.