> For the complete documentation index, see [llms.txt](https://mit-public-courses-cn-translatio.gitbook.io/mit6-824/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mit-public-courses-cn-translatio.gitbook.io/mit6-824/lecture-10-cloud-replicated-db-aurora/10.9-zhi-du-shu-ju-ku-readonly-database.md).

# 10.9 只读数据库（Read-only Database）

如果你查看论文的图3，你可以发现，Aurora不仅有主数据库实例，同时多个数据库的副本。对于Aurora的许多客户来说，相比读写查询，他们会有多得多的只读请求。你可以设想一个Web服务器，如果你只是查看Web页面，那么后台的Web服务器需要读取大量的数据才能生成页面所需的内容，或许需要从数据库读取数百个条目。但是在浏览Web网页时，写请求就要少的多，或许一些统计数据要更新，或许需要更新历史记录，所以读写请求的比例可能是100：1。所以对于Aurora来说，通常会有非常大量的只读数据库查询。

对于写请求，可以只发送给一个数据库，因为对于后端的存储服务器来说，只能支持一个写入者。背后的原因是，Log需要按照数字编号，如果只在一个数据库处理写请求，非常容易对Log进行编号，但是如果有多个数据库以非协同的方式处理写请求，那么为Log编号将会非常非常难。

但是对于读请求，可以发送给多个数据库。Aurora的确有多个只读数据库，这些数据库可以从后端存储服务器读取数据。所以，图3中描述了，除了主数据库用来处理写请求，同时也有一组只读数据库。论文中宣称可以支持最多15个只读数据库。如果有大量的读请求，读请求可以分担到这些只读数据库上。

![](/files/-MFnEdpRirRRHWHhnc7m)

当客户端向只读数据库发送读请求，只读数据库需要弄清楚它需要哪些data page来处理这个读请求，之后直接从存储服务器读取这些data page，并不需要主数据库的介入。所以只读数据库向存储服务器直接发送读取page的请求，之后它会缓存读取到的page，这样对于将来的一些读请求，可以直接根据缓存中的数据返回。

![](/files/-MFnPeeORZSXv5oMhqi3)

当然，只读数据库也需要更新自身的缓存，所以，Aurora的主数据库也会将它的Log的拷贝发送给每一个只读数据库。这就是你从论文中图3看到的蓝色矩形中间的那些横线。主数据库会向这些只读数据库发送所有的Log条目，只读数据库用这些Log来更新它们缓存的page数据，进而获得数据库中最新的事务处理结果。

![](/files/-MFnPnjaLWSsyNba5Bip)

这的确意味着只读数据库会落后主数据库一点，但是对于大部分的只读请求来说，这没问题。因为如果你查看一个网页，如果数据落后了20毫秒，通常来说不会是一个大问题。

这里其实有一些问题，其中一个问题是，我们不想要这个只读数据库看到未commit的事务。所以，在主数据库发给只读数据库的Log流中，主数据库需要指出，哪些事务commit了，而只读数据库需要小心的不要应用未commit的事务到自己的缓存中，它们需要等到事务commit了再应用对应的Log。

另一个问题是，数据库背后的B-Tree结构非常复杂，可能会定期触发rebalance。而rebalance是一个非常复杂的操作，对应了大量修改树中的节点的操作，这些操作需要有原子性。因为当B-Tree在rebalance的过程中，中间状态的数据是不正确的，只有在rebalance结束了才可以从B-Tree读取数据。但是只读数据库直接从存储服务器读取数据库的page，它可能会看到在rebalance过程中的B-Tree。这时看到的数据是非法的，会导致只读数据库崩溃或者行为异常。

论文中讨论了微事务（Mini-Transaction）和VDL/VCL。这部分实际讨论的就是，数据库服务器可以通知存储服务器说，这部分复杂的Log序列只能以原子性向只读数据库展示，也就是要么全展示，要么不展示。这就是微事务（Mini-Transaction）和VDL。所以当一个只读数据库需要向存储服务器查看一个data page时，存储服务器会小心的，要么展示微事务之前的状态，要么展示微事务之后的状态，但是绝不会展示中间状态。

以上就是所有技术相关的内容，我们来总结一下论文中有意思的地方，以及我们可以从论文中学到的一些东西。

* 一件可以学到的事情其实比较通用，并不局限于这篇论文。大家都应该知道事务型数据库是如何工作的，并且知道事务型数据库与后端存储之间交互带来的影响。这里涉及了性能，故障修复，以及运行一个数据库的复杂度，这些问题在系统设计中会反复出现。
* 另一个件可以学到的事情是，Quorum思想。通过读写Quorum的重合，可以确保总是能看见最新的数据，但是又具备容错性。这种思想在Raft中也有体现，Raft可以认为是一种强Quorum的实现（读写操作都要过半服务器认可）。
* 这个论文中另一个有趣的想法是，数据库和存储系统基本是一起开发出来的，数据库和存储系统以一种有趣的方式集成在了一起。通常我们设计系统时，需要有好的隔离解耦来区分上层服务和底层的基础架构。所以通常来说，存储系统是非常通用的，并不会为某个特定的应用程序定制。因为一个通用的设计可以被大量服务使用。但是在Aurora面临的问题中，性能问题是非常严重的，它不得不通过模糊服务和底层基础架构的边界来获得35倍的性能提升，这是个巨大的成功。
* 最后一件有意思的事情是，论文中的一些有关云基础架构中什么更重要的隐含信息。例如：
  * 需要担心整个AZ会出现故障；
  * 需要担心短暂的慢副本，这是经常会出现的问题；
  * 网络是主要的瓶颈，毕竟Aurora通过网络发送的是极短的数据，但是相应的，存储服务器需要做更多的工作（应用Log），因为有6个副本，所以有6个CPU在复制执行这些redo Log条目，明显，从Amazon看来，网络容量相比CPU要重要的多。
