Oracle RAC可以说是具有较高保障级别的生产环境中最常用的部署架构,我们能根据场景让应用选择load balance还是failover的模式(可以参考
《通过JDBC让应用能体验到Oracle高可用的"红利"》)。
如果是load balance连接模式,这就牵扯到一个问题——缓存的读取方式。
首先,我们以单实例的Oracle数据库为例,用户执行一条SQL,Oracle Server通过解析、优化器等的处理,确定SQL的执行计划,读取数据的时候,会从磁盘存储的数据文件中(前提是所需数据当前不在缓存中)通过物理IO将所需数据读到Oracle的缓存中(这个缓存就称为Buffer Cache),然后再执行相应的操作。数据加载到Buffer Cache,一方面缓存是可以共享的,不同用户执行的SQL可能用到Buffer Cache中的相同数据,另一方面内存操作的效率远高于物理IO。
如果应用通过load balance的模式使用Oracle RAC,执行一条SQL,由于会涉及到多节点,有可能出现所需数据不在同一个节点的Buffer Cache缓存中,不同节点间Buffer Cache是怎么共享数据的,就成为了关键的问题。
解决问题的钥匙,就是Cache Fusion,即内存融合技术。
在此推荐一篇介绍Oracle RAC Cache Fusion的论文《Cache Fusion: Extending Shared-Disk Clusters with Shared Caches》,言简意赅地介绍了这个Cache Fusion是怎么解决Oracle RAC不同节点之间共享数据的问题。
英文原版的论文可以自行搜索。
《Cache Fusion: Extending Shared-Disk Clusters with Shared Caches》译文如下。
Abstract
Cache Fusion组件(内容融合技术)是Oracle RAC的基础组件,它实现了多个节点间的共享内容。在传统的基于shared-disk应用中,多节点间通过分布式锁和读写共享的盘来交互信息。Cache Fusion扩展了基于shared-disk架构的信息交互方式,允许不同节点间通过interconnect网络共享数据库内部的buffer。数据直接从一个节点的buffer传递到其他节点,避免了读写共享盘。Cache Fusion提高了基于shared-disk数据库的性能,同时又保留了shared-disk架构(存储计算分离)的优势。
1. Introduction
Oracle RAC名字的由来:Oracle RAC是基于shared-disk架构的分布式数据库,每个db节点可以直接访问disk,动态的扩展节点,任何应用可以不用做任何修改可以直接从传统的master/slave模式迁移到RAC模式。此外RAC通过扩展多个节点可以提高数据库服务的整体性能和可用性。只要有一个节点活着,数据库就是可用的。
在传统的shared-disk数据库中,共享盘是做为data交互的唯一手段。例如:node1读取的页面在node2上是脏页,node1必须等待node2刷脏后,node1才能读取到这个页面的最新内容。
最新硬件的发展(超大规模的SAN存储/Infiniband)给分布式数据库架构提供了新的可能性。Cache Fusion的核心思想,就是充分发挥新网络硬件的大带宽低延迟,data的共享通过网络而不是共享存储,共享盘的IO代价比网络要高。不同的Oracle实例直接通过网络读取其他节点上页面的最新内容,避免了昂贵的IO。因此,Orace RAC的架构演变为:shared-disk shared-buffer。
2. Overview of Real Application Clusters
RAC中每个实例都有自己的redo文件和buffer内存。Global Cache Service(GCS)跟踪维护所有节点的local cache资源,组织成一个大的global cache。GCS同步对global cache的访问:同一时间只允许一个节点修改一个cache resource。
GCS是一个去中心化的分布式结构:每个节点维护global cache的一个子集。这样设计有2个优势:1. 单点性能瓶颈:global cache资源的维护均摊到了每个节点上;2. 单点故障:硬件或者软件故障不会影响其他节点,被这个节点维护的资源暂时不可访问(需要recover流程),其他资源仍然能够继续使用(例如上锁成功后可以写入);
全局资源的分配要考虑到这些资源的访问模式,例如:被某个节点高频次访问的资源,可以分配给这个节点来管理。
GCS知道所有页面的分布视图,因此可以把一个读或者写请求转发到一个最合适的节点来处理。例如:一个节点要修改一个block,GCS可以把这个请求直接转发给该页面当前的holder,这个holder再把修改之后的页面最新内容传给发起者,同时GCS标记该block的holder为这次请求的发起者。
3. Cache Fusion
Cache Fusion协议:通过网络来共享节点间的buffer cache。有两种共享模式:1. Read-Sharing:查询操作时读取其他节点上的buffer;2. Write-Sharing:更新操作访问其他节点的buffer;
3.1 Cache Fusion Read-Sharing
read-sharing机制是通过Oracle的Consistent Read机制来实现的。Oracle CR是基于多版本的的并发控制协议,使得事务不上任何锁能够读取到一致的数据集。Oracle中的每个事务都对应一个快照时间,也就是SCN,CR机制保证事务能够读取SCN时间点的一致的数据集。CR的原理:当事务A修改block时,在回滚段存储undo日志。同时事务B读取这个block时,使用这个block的current和undo构造出这个block在事务B的SCN对应版本时的一个clone。clone副本仅仅在内存中不会持久化到盘上。事务B不需要等待事务A提交或者abort就能读取到它所需要的block的一个版本。
在RAC中,节点A读取的block在节点B的buffer cache中时,B读取undo创建一个一致性的CR clone版本,并发送给节点A。这个block的holder仍然是节点B,所以对B没有任何影响。仅当读取的block不在任何的RAC集群节点中才发起磁盘的IO。Read-Sharing协议保证一旦一个block被某个节点读取过,后续的读取再不需要从磁盘上读。
3.2 Cache Fusion Write-Sharing
Write-sharing需要GCS的介入。当节点A计划更新一个block时,GCS服务会执行cache-coherency的过程。GCS发现这个block在节点B的buffer cache中:1. 通知B需要释放这个block的ownership权利;2. 节点B在自己的buffer cache中保存这个block的一个副本,以备后续的读使用,同时释放ownership,并发送这个block给节点A;注意:节点B给节点A发送的block拷贝可以是dirty的,节点B上存储了redo日志,并没有来得及刷脏。允许共享脏页能够大大减少磁盘的读写操作。
仅当block不在任何节点的buffer cache中才发起磁盘读操作。
RAC的write-sharing协议还有一个重要的优势:一个block被写的过程中,拥有 current copy 的节点仍然能够继续读取这个block。因为对于当前这个事务来说,这个block的数据版本足够新了。例如上面例子中:节点B可以继续读取block,因为它有block的一个镜像,即使它已经把ownership交出去了,并且把block发送给了新的ownership。这个行为和传统的shared-disk是相反的:对一个block的写操作会invalid掉所有节点buffer cache的该block的拷贝(防止没有读取最新的版本),同时上锁阻止其他节点的读取,直到写操作完成。
RAC cache fusion的write-sharing和read-sharing机制,能够保证IO数量和单节点实例在相同workload相同总内存时IO相当(很好证明:一旦内存被某个节点读取过了,就不再发起IO操作。有一点不同是:一个block可能在多个节点上同时存储,只是为了加速本地读取的性能);
ownership作用:1. 谁写谁就是ownership;2. 只有ownership才会对该block刷脏(刷脏的唯一性,不会出现多个节点对一个block的刷脏);3. 在write-sharing时,先上排他锁,从其他节点上接管该block的ownership;
3.3 Efficient inter-node messaging
Cache Fusion协议减少了IO,依赖节点间高性能的数据传输。三方面优化性能:
(1)节点间低延迟的通信:cache fusion本质上一个大的状态机,使用定长固定格式的消息格式,这样可以高效的生成和解释。同时使用高速硬件来加速网络;
(2)管理buffer的节点数是常量,一个buffer关联3个node,不会随着RAC集群的扩大而变多:
reqiest请求方;
dire目录管理;
holder,当前持有者;
(3)最小化节点间事件同步频率,动态目录迁移机制:高频词访问的页面所属的目录信息移交到local来管理,这样可以减少一次网络访问,甚至没有网络访问开销。
4. RAC support for DSS(OLAP) workloads
RAC的Cache Fusion sharing协议可以为OLTP很好的提供buffer共享。RAC的多节点并行执行也能加速OLAP大范围扫描数据的场景。RAC的优化器是cost based的,同时能感知集群的状态:1. 磁盘block和节点的亲和性(哪些节点上缓存了哪些block);2. 表在存储上的最优并行度;3. 节点数,cpu数等;4. local execution vs parallel slaves execution的选择;5. function shipping机制 vs data sharing机制(实现类似MPP的并行查询,节点间同步function,而不是Cache Fusion机制的同步data,这样能大量的减少数据交互);
在Oracle RAC中:OLTP单个查询需要共享的数据量少,通过Cache Fusion Data sharing协议共享不同写节点上最新的buffer。本质上还是一个单机DB的逻辑,只不过原先从shared-disk上读取的数据现在从邻居的buffer中读取,过程中需要处理一致性读;OLAP需要扫描大量的数据,通过function shipping机制,计算跟着数据走,减少数据传输。本质上是一个类似MPP的分布式计算架构,RAC是2-DFO局部流水的,不能完全的pipeline,中间结果落盘;
5. Recovery in a RAC environment
RAC集群中只要有一个节点或者,服务就不中断。recovery的时间和失败节点数成正比。RAC能够加速recovery的关键点:1. 只replay失败节点的redolog;2. 在replay过程无需从共享存储上读取block了再应用了,通过data sharing协议从其他活着节点的buffer中读取;
只要扫描一遍redo并记录待恢复的页面集合,其他所有活着节点中的buffer cache组织成的Global Cache Resource就可以继续服务新的查询,RAC集群就可以立即进入可服务状态(遇到要读取待恢复的页面会等待),然后再真正开始redo的恢复。
多个节点并行的恢复,可以并发的读取共享盘,同时恢复过程中,或者的节点以及恢复中的节点中的buffer不断的被读取上来,后续对某个block的恢复也许就可以走Data Sharing协议,不断的减少存储IO,并行恢复会越来越快。
6. Conclusions
Oracle Cache Fusion是构建在shared-disk上的shared-cache机制。使得应用程序无需感知DB的分布式逻辑。核心思想是通过data sharing协议充分发挥网络IO,减少共享盘IO。read-sharing协议:基于Consistent-Read机制(current page undo)实现的多版本read-sharing协议允许直接从其他节点读取buffer,而无需节点间维护始终维护缓存一致性;write-sharing协议:允许直接从其他节点上拉取buffer到本地,然后再更新,其他这个页面当前是脏页。
高效的节点间通信允许RAC支持更大规模的集群。RAC的Parallel Execution能够时应用层对集群拓扑无感知就可以加速OLAP类的查询。RAC在recovery时可以从其他节点上读取buffer,进而加速recovery的过程。
译文参考:https://zhuanlan.zhihu.com/p/353771897
论文在很有限的篇幅中,将Oracle RAC的核心之一Cache Fusion讲解地很直白,还是值得反复研读和体会的。Oracle数据库的很多功能都是看起来很简单,实则蕴含了很深的设计造诣,大到Cache Fusion、各种进程、各种缓冲区技术,小到$ORACLE_HOME中的各种脚本逻辑,都是有很多值得借鉴和学习的。
现在我们的国产数据库迎来了前所未有的发展机会,Oracle作为一个强大的对手,如果只是闭门造车是无法超越的,而应该怀揣着"站在巨人肩膀"的态度,取经学习,逐渐强大我们自己的产品体系,经营好我们自己的技术生态环境,才能拉近和这些传统数据库厂商的差距。