原文链接:https://neon.tech/blog/architecture-decisions-in-neon
1. Neon是什么?
Neon是一个对标AWS Aurora for Postgres的开源的基于存算分离的云原生数据库[1, 2]。其创始团队都是资深的Postgres内核专家,有着数十年的内核开发经验,比如其CEO Nikita Shamgunov[3]也是MemSQL的创始人,另一位联合创始人Heikki Linnakangas[3]则是Postgres的内核专家,贡献了很多特性。
AWS Aurora[5]开创性地提出了存算分离的架构用于OLTP数据库,国内外也有不少企业效仿推迟类似架构的产品,可以说其开创了OLTP数据库的新篇章,在和Google推出的Spanner[6]竞争中显然已经取得了商业上更大的成功。近几年国内各大厂商也纷纷推出了其存算分离的OLTP数据库,比如华为的Gauss DB[7]、阿里的PolarDB[8]、腾讯的TDSQL-C(CynosDB)[9]以及字节的VeDB[10]等,并且这些产品都已经成为了公有云RDS主推的数据库产品,其保证100%兼容MySQL/Postgres的前提下,提供更好的扩展性和性能,能够保证计算和存储独立扩容。除此之外,通过存算分离架构,计算层成为无状态化为serverless提供了可能,数据库可以弹性扩容、按量计费,这无疑给业务提供便捷的同时也能够极大地降低数据库的使用成本。
Neon正是在这个大背景下,旨在做一款AWS Aurora for Postgres的开源替代。实际上其走的更远一些,一开始在架构设计上对WAL进行了特殊处理保存,从而能够实现可以读取Page的任意版本,并且引入COW(Copy on Write)技术,允许用户在某个时刻建立数据的分支(Branch),与原有数据独立,在真正写入的时候复制出一份数据。该方案下也免去了数据单独备份(PITR)的需求,充分利用公有云低成本的对象存储服务进一步将数据库成本降低。
下面内容基于原文总结整理,从作者的视角去了解Neon架构的决策考量点
2. 存算分离的好处
首先,作者总结了一下存算分离的好处:
- 共享一份数据可以运行多个计算实例
- 可以快速启停计算实例
- 提供即时恢复数据库的能力
- 可以简化一些操作,比如备份和归档,可以在存储层处理不会影响业务
- CPU和IO资源可以独立伸缩
3. 决策1:为什么不直接使用SAN或者现成的分布式文件系统?
如果只是为了存算分离,使用SAN或者现成的分布式文件系统也是可以的,并且也确实有商业数据库使用这种方式,但是这样就不能控制文件系统层,如果想要上云也会变得很困难,而且整个系统会变得非常复杂笨重,所以Neon决定自建存储层。
4. 决策2:存储层和计算层的接口?
Neon已经决定自建存储层,那么计算层和存储层到底以什么样的接口进行交互呢?这个其实没有太多选择,要么就是文件系统接口,计算层去适配,要么就是像已有的存算分离数据库一样遵循“log is database”的思想暴露log和page接口。Neon选择了第二种,和市面上其他数据库(Aurora、QL Server等)一样,计算层和存储层通过WAL和Page交互,计算层通过网络将WAL记录写入存储层,Page的读取也通过网络从存储层读取,WAL物化到Page则在存储层完成。
5. 决策3:WAL和Page是否需要两个服务?
存储层暴露WAL和Page读接口,那么WAL和Page服务是在一个服务里还是独立成两个服务呢?Aurora是提供了一个服务,Azure的Socrates将其独立成了两个服务,neon的作者人物独立成两个服务有如下优势:
- WAL服务和Page服务能够独立并行开发
- 为了保证持久性,WAL服务一定会使用一致性算法,独立后可以更加容易去验证一致性算法的正确性
- WAL服务和Page服务的IO模型和负载模型有很大区别,WAL服务都是append-only的,Page服务则有大量读、写、更行操作,所以独立部署可以利用硬件分别进行优化
最终,Neon的架构如下,n个Safekeeper节点通过一致性算法构成WAL服务,接受计算节点的WAL记录保证其持久性和高可用,WAL服务将提交的WAL传入PageServer构造成Page,Page最终会存储在对象存储降低存储层板,PageServer实际上是Page的缓存
6. 决策4:计算节点和PageServer的对应关系?
Page最终是存储在Pageserver,那一个database的page是存储在一个Pageserver上还是分散存储在不同的Pageserver上呢?一个pagesever只包含一个database的page还是包含多个database的page?首先neon是一个多租户的数据库,所以对于第二个问题,一个pageserver一定是要包含多个database的page,对于的问题,如果一个database的page分散在多个pageserver可以提供更高的可用性,但是实现稍微复杂一些,所以当前的实现先放在一个pageserver后续再提供分片的策略提供更好的可用性以及支持更大的数据库。
7. 决策5:将历史数据和最新的数据同等对待
传统的OLTP数据库都有一套自己的PITR机制,确保能够将数据库恢复到任意时刻,以应对一些极端场景,比如用户误删除数据。neon的作者这里采用了一种不同的思路去进行数据的备份恢复:保留所有历史数据,这样理论上用户就可以回滚到任意历史时刻。基于LSM-Tree的思想实现了Pageserver,老的page会备份到S3,不会进行删除,这样可以降低存储所有数据的成本,用户需要访问历史Page的时候就可以从S3读取到本地返回给用户。同时基于该存储引擎实现了Branch的功能,可以对任意时刻数据建立一个分支,利用COW(copy on write)的策略实现数据的隔离。Pageserver的实现细节,比如LSM-Tree只保留了最新的数据,老数据会逐渐被compact掉,那么Pageserver是怎么做到保留所有版本的呢?这个后面单独展开讲,本文只关注作者在架构上做的决策。