
# TaurusDB主备复制原理解析
#### 背景介绍
在数据库架构中，主机、备机以及只读节点是实现高可用性、负载均衡和数据备份的核心组件。
MySQL的主备使用Shared-nothing的架构，通过Binlog实现主备复制。TaurusDB与MySQL的主备架构不同，TaurusDB使用的共享存储架构，主备之间通过Redo日志实现同步，仅需传递少量的元数据信息。备机从共享存储读取Redo日志，通过失效旧的数据页面、回放元数据锁、更新事务快照、缓存日志等处理，实现了主备快速同步。
本章节主要介绍TaurusDB主从复制的实现原理与主要流程。
#### TaurusDB主备架构
TaurusDB是一种Shared-storage的架构，主备之间也不使用Binlog来实现同步。
在MySQL社区中，备机指传统主从复制中的从库。它从主库接收二进制日志并应用，保持数据一致，主要用于故障切换（Failover）和数据冗余。只读节点被专门配置为不接受任何写操作的MySQL实例，通常用来横向扩展读能力，分担主库的读压力。它可以是备机，也可以是级联的从库，甚至是一个独立的不参与高可用切换的节点。
对于TaurusDB，不区分备机和只读节点。
如图所示，TaurusDB使用一种Shared Everything的理念，通过Redo日志来实现主机和只读节点之间的同步，而且主机与只读节点之间不直接传输Redo日志数据，仅同步少量必须的元数据，这部分元数据主要用于从共享存储系统正确地读取数据。
主机写入存储池的只有Redo日志，数据页面的生成及修改由共享存储系统来进行。主机写Redo日志到共享存储系统，只读节点从共享存储系统读取主机写的Redo日志，来实现主备同步。对于数据页面，主机和只读节点都是从共享存储系统来读取。
这种架构的优势在于，新增只读节点不受数据量大小影响，支持快速新增只读节点，且只读节点与主节点之间的时延低、传输的数据带宽占用低；只读节点可以通过快速在线升主来恢复主节点故障。
图1TaurusDB的主备架构   
![](https://support.huaweicloud.com/bestpractice-taurusdb/figure/zh-cn_image_0000002689974920.png "点击放大")
#### 主备复制流程
1. **主备通信**
   TaurusDB的只读节点通过处理Redo日志来实现主备同步。主备同步是为了主机新增或修改的数据，只读节点能够尽可能快地查询到。由于TaurusDB的主备使用的是Shared-storage的架构，Redo日志并不需要从主机来获取，而是从共享存储读取。主备之间传递的仅是一些必须的元数据信息。
   这些主备之间直接同步的元数据信息主要包括以下方面。
   - 主机发送给只读节点：
     1. Redo日志在共享存储系统中的位置、大小等元信息，用于读取Redo日志。
     
     2. 表空间数据页面和共享存储系统分片的映射关系，用于读取数据页面。
     
     3. 初始的事务快照信息，用于事务的可见性判断，会通过解析Redo日志来不断更新。
      
   
   - 只读节点发送给主机： 自身不再使用的Redo日志的位点信息（Recycle LSN），主机使用这些数据来确定回收数据的范围，避免将只读节点仍可能使用的历史数据进行回收。
     
   
   
   只读节点会启动一个与主机通信的线程handle_slave_sync，用于从主机接收和向主机发送所需的元数据信息。
   
2. **处理Redo日志**
   通过主备通信线程，只读节点获取了所需的信息，可以进行对于Redo日志的读取和处理。
   1. 只读节点主备复制后台线程 为了加快处理Redo日志，只读节点会启动多组后台线程进行流水线式的并行处理，线程数量可配置。
      这些线程包括reader、dispatcher、parser、advancer、impeller等，它们之间通过event来通知唤醒，以协同完成处理。当主备通信线程通过同步信息发现新的Redo日志时，会通知reader线程从共享存储开始读取Redo日志，随后启动一系列处理流程。
      图2TaurusDB只读节点的主备复制后台线程   
      ![](https://support.huaweicloud.com/bestpractice-taurusdb/figure/zh-cn_image_0000002630263744.png "点击放大")
      - Log reader线程：等待主备通信线程handle_slave_sync的通知，从共享存储系统读取Redo日志数据。
      
      - Log dispatcher线程：等待log reader线程的通知，分发日志到各个log parser线程。
      
      - Log parser线程：等待 log dispatcher 线程的通知，用于解析Redo日志，主要包括两方面的工作：一是将针对具体数据页面的Redo日志按页面 ID 分组整理；二是将其他类型的Redo日志进行解析处理，以供后续使用，例如汇总已提交的事务ID等。
      
      - Log impeller线程：主要用于处理修改数据页面的Redo日志，将log parser处理后的日志，针对每个不同的页面ID，合并组织到一起；失效缓冲池（Buffer Pool）中的数据页面，并缓存Redo日志到相应的称为Log Directory的目录结构中，用于后续生成新版本的数据页面。
      
      - Advancer线程：处理其他主备同步动作，主要包括：元数据锁（Metadata Lock，MDL）的处理，避免表的元数据的修改与数据查询产生冲突；事务快照的更新，获取新的活跃事务和已提交的事务，用于事务可见性判断。
       
   
   2. 只读节点解析Redo日志 Redo日志有多种类型，除了直接针对数据页面内容的修改之外，还包含一些其他信息，需要只读节点进行解析、处理，用于和主机保持同步。对于从共享存储系统读入的每一块Redo日志，都会经过下图中的处理过程。
      图3TaurusDB只读节点解析处理日志   
      ![](https://support.huaweicloud.com/bestpractice-taurusdb/figure/zh-cn_image_0000002660662925.png "点击放大")
      直接针对数据页面内容的修改所产生的Redo日志，会以页面ID为key汇总至一个hash表中。一方面，该机制用于使Buffer pool中相同ID的页面失效，从而使只读节点获知。对于该页面ID，后续需要使用时应获取新版本的数据页面。另一方面，这些日志会被缓存在全局的Log Directory中，以便只读节点在需要新版本数据页面时，能够基于已在Buffer pool中的旧版本页面，自行回放对应的Redo日志以生成新版本的数据页面。这样可减少从共享存储中读取页面的开销，从而提升性能。
      只读节点还需要从各种类型的Redo日志中，解析、处理如下信息。
      - 已提交事务的ID列表 通过Redo日志中的已提交的事务ID信息，可以更新活跃事务列表，用于可见性判断。例如对于可重复读隔离级别，已提交的事务，其修改是可见的，而ID值处于这些已提交事务之间的，Redo日志中没有记录其已提交，属于活跃事务，其修改不可见。
        
      
      - 事务Purge No 用于获取最大的事务ID，以及只读节点事务快照的low limit no的确定。
        
      
      - 更新和删除的表空间的信息 用于更新space cache，如已删除或大小发生扩展。
        
      
      - 元数据锁(MDL)相关请求 Redo日志中记录了DDL语句对于MDL锁的申请和释放等，只读节点模拟同样的申请和释放流程，避免元数据的修改和查询之间产生冲突。例如正在使用的索引，发生了删除。
        
      
      - 需要失效的内存对象，如Query Cache等
      
      - 需要失效的Undo表空间信息 用于处理主机执行的undo truncate等操作。
        
      
      - 需要更新的统计信息
      
      
      通过以上处理，只读节点完成对主机的同步，可对外提供基于最新数据的查询服务。但是TaurusDB并不是强一致性的，从原理上来讲，只读节点和主机之间是存在一定时延的。在上述介绍的Redo处理流程，当前这一轮完成前，只读节点只能提供截止到上一次同步完成的Redo日志的位点（Log sequence number, LSN）的数据查询。
      
    
 
