微软在去年sigmod上发表的一篇论文,介绍了一种新的DBaaS架构,叫做Socrate。使用存储-计算分离的思想,提供更高可用性、扩展性和低成本。
出于安全、成本等方面的考虑, 越来越多的创业公司和大企业将数据托管到云上。与此同时,客户也希望云数据库服务具有高可用性(5个9)、支持大规模数据(例如100TB oltp)和更高的性能。此外,服务是弹性的,能够随着负载的变化自动增长或者收缩使客户能够在"pay as you go"模式下节省成本。
然而在云上依旧使用传统的单片数据库架构并满足这些要求是不可能的:
Aurora是第一个提出使用存储-计算分离的架构的商业系统,将计算层(例如事务处理)和存储层(例如checkpoint recovery)独立地部署。
这篇论文提出了一种新型的服务于oltp数据库系统的架构,叫作Socrates。Socrates采用存储-计算分离的思想,并且将log从存储中分离出来,将其视为一级模块。日志与存储分离也就意味着持久性(由日志实现)和高可用性(由存储层实现)的分离。传统实现通常是维护数据的多个副本将持久性和高可用耦合在一起。而由于两者依赖的必要条件并不完全重合,分离这两个概念是有意义的:1.持久化不需要数据的副本 2.高可用不需要固定的副本数。分离这两个概念使得Socrates使用最适合的机制来处理任务。具体来讲,相比于其它市面上的数据库,Socrates要求更少的本地昂贵的、快速的存储副本,整体上减少数据拷贝,更少的数据带宽和更少的计算资源来保持副本最新。
table1展示了Socrates在各方面的优异表现。如何做到这些提升是这篇论文的主题。

在Socrates之前,SQL DB是基于传统HADR架构的,各方面的表现见Table1。HADR是日志复制状态机的经典实现。含有一个主节点处理所有更新事务并传输所有更新的日志到备节点上。日志传输是在分布式数据库系统中保持副本一致性的标准做法。另外,主节点会定期备份数据到Azure的存储服务上(叫作XStore):日志每5分钟备份一次,每天做一次整个数据库当天的增量备份,每周做一次完全的备份。备节点可以处理只读事务。如果主宕机了,会选一个备成为新的主。使用HADR架构SQL DB需要四个节点(一主三备)来保证高可用和持久性:由于日志5分钟才会备份一次,如果四个节点都宕机了也会导致数据丢失。

HADR架构的优点:
HADR架构的缺点:
上述缺点也是为什么目前SQL DB有4T数据库大小限制的原因。
socrates复用了很多sql server上已有的组件,这一章节介绍了其中的几个重要组件。
SQL Server在本地的存储中维护数据的多版本,以提供read snapshot(实现SI)。
Accelerated Database Recovery(ADR)是SqlServer2019增加的新特性。在ADR之前,SQL Server使用RIES-style的模式恢复:分析日志,回放最后一次checkpoint之前的所有未提交事务的redo日志,回放所有已提交(failed)事务的undo日志。在这种模式中,遇到长事务undo阶段会变得无限长。
使用多版本存储可以优化这种情况:在一个共享的一致性的多版本存储中,系统可以在宕机后立马访问已提交的版本,即使很多情况下允许有undo阶段,数据库在解析和redo阶段后立马恢复服务,这是一个很短的常量级的时间(需要定期的checkpoint)。
2012年,SqlServer发布了一个feature叫作buffer pool extension(BPE),将buffer pool延伸到本地的ssd存储中,"hot"数据在内存中,"warm"数据在ssd中。在Socrates中,扩展了这种思想,使buffer pool具备可恢复性,例如宕机后的可恢复性。将这个组件称为RBPEX,它作为对数据页的缓存机制服务于存储和计算层。这种方式可以使得宕机后节点快速恢复之前的性能:如果宕机是比较短暂的(像升级引起的重启),读的代价将会更小,相比从远程server中重新读取缓存的page,直接回放少量的更新日志记录代价也会更小,提高了可用性。
Socrates将数据库引擎的组件分布在多层之中。为了支持更丰富的计算分布,扩展了传统的SQL Server网络层(称为Unified Communication),使用了一种新的协议,称为Remote Block I/O,简称RBIO。RBIO是一种无状态协议,强类型,支持自动版本控制,对短暂性故障具有可快速恢复性,并且对最佳副本选择具有QoS支持。
当数据库文件存储在Azure中时,SQL Server 2016引入了快速备份的能力。这个feature依赖于XStore实现的blob snapshot的特性,XStore是一个日志结构的存储系统,备份几乎是实时的,因为它只需要维护一个指针(时间戳)指向当前日志的头部。Socrates扩展了这个feature,将备份/恢复的工作完全使用XStore snapshot。因此,Socrates可以不用消耗计算层的CPU和IO就可以在常量时间内完成备份或者恢复。在XStore的快照机制下,一个数百TB的大数据库也可以在分钟内完成备份。
在I/O栈的最低层,SQLServer使用一个叫作FCB的抽象层作为"File Control Block"。FCB层抽象了底层设备的细节,提供给上层I/O的能力,支持多个文件系统、多样的存储平台和I/O模式。
了解sql server的这些组件和设计原则后再看Socrates的架构就比较清晰了,遵循上述设计原则和目标:

从整体上来看,Socrates的架构由四层组成:

图3展示了XLOG服务的内部实现。从图中的左上角开始,主计算节点直接写日志块到Landing Zone(LZ)中,LZ是一个快速持久化的存储服务,可以提供强大的数据完整性、可靠性和一致性保证,具有类似SAN的能力。
SQL DB Hyperscale当前的版本使用Azure Premium Storage service (XIO)来实现LZ。XIO使用三个数据副本来保证持久性。每个存储服务在性能、代价、可用性和持久性之间做了一定权衡。
主节点使用同步的方式将日志块直接写入LZ中以获得尽可能低的commit延迟。LZ的目的是要快(可能昂贵)但小。LZ被组织为一个循环缓冲区,日志格式采用对所有Microsoft SQL服务和产品中使用的传统SQL Server日志格式的向后兼容扩展的形式。这种方式遵循了不重复造轮子的原则并且获得了Socrates和所有其它SQL Server产品和服务的能力。日志扩展的一个关键能力是它允许在写存在时并发的读并且可以获得一致性的信息,不需要任何的同步(beyond wraparound protection)。最小化层间的同步将使得系统更具伸缩性和弹性。
主节点也会写日志块到特殊的XLOG进程,这个进程将日志传送到page server和备节点。这些写是异步的而且是不可靠的,使用一种lossy协议。Socrates为了持久性同步可靠地写日志到LZ,为了可用性异步地写入XLOG。
主节点并行地写日志到LZ和XLOG。在没有同步的情况下,日志可能在LZ持久化之前到达备节点,这可能会导致数据的不一致性或者数据丢失。为了避免这种情况,XLOG只传播已经在LZ中持久化的harden日志。主首先将所有日志写入到XLOG的一块挂起区域,之后主再通知XLOG所有持久化的日志块。一旦日志块持久化了,XLOG将它从挂起区域移到LogBreaker用于分发。
为了分发和归档日志块,XLOG进程实现了一个存储层次结构。一旦一个日志块移动到LogBroker中,一个称为destaging的内部XLOG进程就会将日志移动到固定大小的本地SSD缓存以实现快速访问,并移动到XStore以实现长期保留。XStore使用廉价存储,价格便宜、耐用,但速度慢。将此日志块的长期存档称为LT。如果未特别指定,SQL DB将日志记录保留30天,用于指定时间点恢复和模糊备份的灾难恢复。在LZ中保留30天的日志记录的成本非常高,是一项低延迟、昂贵的服务。虽然这个分层体系结构很复杂,但不需要其他日志备份过程,在LZ和LT(XStore)之间,所有日志信息都是持久存储的。而且,这个层次结构满足了Socrates对延迟(LZ中的快速提交)和成本的要求(XStore中的大容量存储)。
备节点和page server从XLOG服务中拉取日志。这种方式下体系结构的可伸缩性更强,因为LogBroker不需要维护日志使用者,可能会有数百个page server。在最上层,LogBroker在内存中维护了一个日志块的hashmap(在图3中所示的Sequence Map),在理想情况下,所有的日志请求都在Sequence Map中得到回应。如果Map中没有找到,则查找XLOG进程中的本地SSD缓存。本地的SSD缓存是日志尾部的另一个环形缓冲区。如果请求者需要的块也不在SSD缓存中,则需要从LZ中拿取,如果还是没有找到则从最后一层存储LT中拿取,最后一层确保日志可以被找得到。
XLOG进程也实现了一些其它的分布式DBaaS系统的通用功能:日志生命周期租约、LT Blob清理、备份/还原、日志使用统计、block filter等。
socrates主节点的部署与在SQL Server中的独立进程几乎完全一样。数据库实例本身不感知其它副本的存储。它不知道它的存储是否是远程还是本地。相反,HADR架构下的主非常清楚参与的复制状态机,并且实现仲裁来完成日志的持久化和事务提交。而且,HADR的主以一种紧密耦合的方式获取备的信息,相比来看,Socrates的主在实现上更加简单。
不过两种架构下的主计算节点所实现的核心功能是相同的:处理读写事务和产生日志。与部署的SQL Server有几个显著的差异:
最后的差异是主节点具备检索没有被缓存在本地的数据页的能力。称这个机制为GetPage@LSN。GetPage@LSN机制是一个RPC,由主进程使用RBIO协议从FCB I/O虚拟化层初始化。
getPage(pageId, LSN)
pageId唯一地标识主节点需要读取的页,LSN标识一个page的日志序列号,值与页面的最大LSN相同。page server返回已应用此LSN或者更新的page。
举个例子如下:
在这个场景中,为了保证可以读到最新版本的page,需要指定的lsn。
为了确保更新,Page Server按照如下方式处理一次getpage(x, x-lsn)请求:
这个简单的协议确保了page server不会将page X的旧版本返回。
主节点根据pageid上建立了一个hashmap,用于确定getpage中使用的lsn,hashmap记录了每一个被淘汰page的最大LSN,保证了读的安全。
遵循复用的设计原则,Socrates备节点共享与HADR中相同的apply日志功能。一个差异点是Socrates不需要保存和持久化日志块,这是XLOG所负责的。另外,Socrates是一个松耦合的架构,备节点不需要知道谁产生的日志。query processor、security manager和transaction manager这些都没有变化。
最显著的差异也是不需要保存数据库完整的备份。这点对于支持大型数据库和使计算节点无状态(使用cache)的设计目标至关重要。
这种设计下,备节点可能会处理到不在它自己buffer中的page相关联的日志记录(不在memory和SSD中),所以需要有不同的处理策略。一个可能的策略是从底层fetch page并且应用log,这种方式下,备节点与主节点的缓存大致保持一致,备宕机后性能可以更加稳定。SQL DB Hyperscale当前的实现策略是简单的忽略掉不在cache中的数据页。这个策略会导致一个问题,备节点处理只读事务时的GetPage请求与判断page是否存在于cache中存在并发性问题。为了解决这个冲突,备节点必须在实际调用之前先注册GetPage请求,直到数据页被load进来,备节点的应用日志线程排队处理GetPage请求中Page相关的日志记录。
另一个问题出现在备节点执行B树遍历来处理只读事务时。备节点使用与主节点相同的GetPage协议,但是在同一时刻备节点的LSN可能会比主节点中写入的LSN小,这会带来不一致问题。考虑下面B-Tree遍历的部分场景:
根据GetPage的协议,PageServer可能会返回一个更新的page,这导致了不一致问题。不过这种不一致比较容易甄别。如果在遍历过程中检测出了不一致,它会暂停一会儿等待日志apply。之后再次启动B-Tree的遍历,以到达一致的结果。
这里的关键点是,SQL Server持久数据结构的设计使得即使在存在从不同时间点(从LSN的角度)物理地获取页面时,也可以实现逻辑一致的遍历。从给定事务时间戳的页中提取记录的正确版本(SI隔离级别)。
Page Server主要负责三件事,第一管理数据库的分区 (通过apply日志),第二响应计算节点的GetPage请求,第三完成分布式的checkpoint和备份操作。
遵从reuse的原则,PageServer apply日志与备节点处理日志的过程是类似的。相比备节点需要关心所有有关数据库change的日志记录和需要消费所有的日志块的特性,PageServer仅仅需要关心特定Page Server中的分区相关的日志块。为此,主节点为每个日志块增加充分的注释信息标明日志块中的日志记录需要应用到哪些分区中。XLOG利用这些过滤信息仅仅分发相关的日志块到对应的Page Server中。
回应GetPage@LSN请求也很简单。Page Server也使用RBPEX、SSD扩展可恢复缓存。这个机制与计算节点相同,但是策略不同。计算节点缓存最热的页面以达到最好的性能,它们的缓存是稀疏的。相比而言,Page Server缓存的热页面较少,而是缓存所有那些不足以放入计算节点缓存中的页面。分区的所有的page页全部存储在Page Server的RBPEX中。此外,Socrates将page server的RBPEX组织在一个保持跨距的布局中,使得来自计算节点的单个I/O请求(覆盖多个页面范围)在Page Server上转换为单个I/O请求。因为Page Server的缓存是密集的,相比计算节点的RBPEX不会带来读放大。这个特性对于性能是非常重要的,尤其是对于那些查询多达128个page的场景。Page Server缓存的另外一个重要特性是当XStore无法提供服务时,Page Server继续以这样一种模式运行:page写入RBPEX中并记录不在XStore中的页面,当XStore重新联机时,重新恢复检查点(并捕获XStore)。相同的机制允许Socrates聚集多次I/O一次性写到XStore中,以便在底层存储服务中获得尽可能高的吞吐。最后,当一个Page Server启动时,RBPEX以异步的方式建立,而同时Page Server已经可用并且能够服务请求和应用日志。最后,Page Server通过与XStore交互进行checkpoint和备份操作。
如前所述,数据库真正的数据是保存在XStore中的。XStore的细节在Windows Azure Storage: a highly available cloud storage service with strong consistency这篇论文中有描述。简单的来讲,XStore是cheap(基于硬盘)、durable(由于跨可用性区域的高度复制,几乎没有数据丢失),通过一个log-structured设计提供有效的备份和恢复。XStore扮演传统数据库中硬盘和磁盘相同的角色。计算节点的主存和SSD缓存(RBPEX)扮演传统数据库memory的角色。
对于检查点,Page Server定期将修改过的页面发送到XStore。备份是使用XStore的snapshot实现,通过简单地记录时间戳在短时间内创建备份。Socrates支持特定时间点还原,而且相比HADR架构利用XLog中的snapshot可以更加快速,不受限于数据量的大小。
Socrates将数据库分为了几个mini services,每个服务独立自治,并且在通信上尽可能地异步。与其它云上系统像aurora最大的不同是将日志分离,将availability与 durability分开,XLog和XStore负责durability,计算节点和page server仅用于可用性:它们fail不会丢数据,但在恢复之前服务不可用。这种分离方式最大的优点是提供更灵活和细粒度的控制,可以在可用性、性能和成本方面做更多的权衡。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31537575/viewspace-3122361/ ,如需转载,请注明出处,否则将追究法律责任。