如何使用Apache BookKeeper构建分布式数据库-01

初识HerdDB

HerdDB是一个分布式数据库,数据在服务器群集之间分布,而无需共享存储。

HerdDB的主要语言是SQL,建议客户端同时使用JDBC驱动程序API和低级API。

HerdDB可嵌入到任何Java虚拟机中,每个节点无需网络即可访问本地数据。

HerdDB复制功能建立在Apache ZooKeeper和Apache BookKeeper项目上。

HerdDB与NoSQL数据库非常相似,实际上在低级上,它基本上是具有SQL抽象层的键值数据库,使每个用户都可以利用现有的专有技术并将现有的应用程序移植到HerdDB。

HerdDB设计用于快速“写入”和主键读取/更新数据访问模式。

HerdDB支持事务和“提交读取”隔离级别

HerdDB使用Apache Calcite作为SQL解析器和SQL Planner

基本概念

数据与任何SQL数据库一样,都以表进行组织,并且为了利用HerdDB复制功能,表在表空间内进行分组。

表空间是表的逻辑集合,是建立复制的基石。

有些数据库功能仅在相同表空间的表之间可用:

事务可能只跨越相同表空间的表 子查询只能跨越相同表空间的表 复制是在表空间级别配置的,因此对于每个表空间,只有一个服务器被设计为“领导者”(管理者),然后您可以配置一组“副本”。 系统自动在副本之间复制数据并透明地处理服务器故障。

系统总览

让我们从一个高层次的观点出发,我们要构建什么以及我们需要什么特性。

  • 我们需要一个数据库,一个持久保存数据的存储,并且可以从远程客户端访问它
  • 我们将数据存储在机器集群中
  • 我们的机器不共享磁盘或使用共享的安装,仅网络连接(LAN或WAN)
  • 机器可能会发生故障,磁盘随时可能丢失,但是我们希望该服务对客户端可用,直到它们的一部分启动并运行
  • 我们希望能够在不中断服务的情况下添加和删除计算机
  • 我们希望完全控制一致性

这个列表听起来很普通,有几种设计具有这种功能的系统的方法。

为了使其更加具体,让我们创建一个具体方案:

  • 我们有一个带有一个表的SQL数据库
  • 数据通过N台计算机复制
  • 没有共享磁盘,只有服务器之间以及服务器和客户端之间的网络连接
  • 我们采用复制状态机的架构模式

预写日志

为了支持ACID(原子性,一致性,隔离性,耐久性)语义,数据库使用预写日志记录。

假设数据库将表的副本存储在本地易失性存储器(RAM)中。 当客户端请求写操作(如UPDATE操作)时,数据库会将操作“记录”到持久性存储中,并将记录的新值写入日志(WAL)。

当存储确认写入(fsync)时,更改将应用于表的内存副本,然后存储将结果确认给客户端。 一旦我们更新了内存中的副本,其他客户端便能够读取新值。预写日志流和表内容:

avatar

在上面的示例中,表开始为空,然后我们执行第一个写操作INSERT(record1),该操作在LSN(日志序列号)1处发生。我们的表现在包含record1。 然后我们在LSN2上记录另一个修改,即INSERT(record2),现在该表包含record1和record2,然后在LSN3上记录DELETE(record1),该表仅包含record2。

当服务器重新启动时,它执行恢复操作,读取所有日志并重建表的内容,因此最终导致表中仅包含record2。

如果在日志上有一个值,我们确定它不会丢失,并且任何在重新启动事件之前读取该值的客户端都可以再次读取相同的值。

如果我们在写入日志之前应用了内存中的更改,则不会发生这种情况。

请注意,只有更改表内容的操作才写入预写日志:我们不记录读取。

检查点

你始终可以从日志中重建表的内容,但不能存储无限的日志,因为在某个时间点该日志被截断了。 为了释放空间,此操作通常称为检查点。

当数据库执行检查点时,它将在持久存储中刷新给定LSN上表的内容。一个检查点发生在LSN3:

avatar

现在我们已经持久地将表保留在LSN3上,我们可以节省资源并将部分日志从LSN1删除到LSN3。 因此,当服务器执行恢复时,它只需要重播LSN4,这反过来又允许更快的启动顺序。

在检查点期间,表的内容存储在哪里? 您可以有几种策略,例如,可以将内容存储在某些本地磁盘上(记住要使用fsync)。 但是,如果相对于对WAL的写入次数而言,表的内容确实很小(因此您在同一组记录上有很多更改),则可以考虑将表的内容转储到WAL本身。

复制状态机

复制状态机是一个实体,在这种情况下为表,即在给定时间(日志序列号)处于给定状态(表的内容)的状态,并且在一组状态下状态更改的顺序相同 相互连接的机器,因此最终每台机器都保持相同的状态。

每当您在计算机上更改一条记录时,都必须在其他所有副本上应用相同的更改。

我们需要对计算机状态进行更改的总顺序,而我们的预写日志非常适合此目的。每个节点都有一个表的副本,并且WAL是共享的:

avatar

在我们的体系结构中,只有一个节点能够更改系统状态,即更改表的内容:我们称其为领导者。

每个节点在内存中都有整个表的副本。发生写操作时,会将其写到WAL,然后使其对客户端可见以进行读取。

其他非领导者节点(跟随者)跟踪日志,它连续地从日志中读取更改,其更改顺序与领导者发布的顺序完全相同。

追随者会将所有更改应用于自己的本地副本,这样,他们将看到表的相同历史记录。

同样重要的是,只有在WAL确认相同的更改之后,追随者才能应用每个更改,否则,追随者将在领导者的将来。

Apache BookKeeper是我们需要的预写日志:它是持久且分布式的。它不需要共享磁盘或远程存储,不需要保证所有项目的顺序,也不需要防护。在下一篇文章中,我将向您展示Apache Bookkeeper如何保证满足我们的需求。

资料

https://streamnative.io/blog/tech/2020-02-04-how-to-build-database/