
# 分布式GTM-LITE备机读
提供强一致性读的能力。
从[三种回放模式及备机读](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/gaussdb-12-0833.html)可以了解到串并行模式下，GaussDB通过对比tuple和Snapshot中的xmin信息判断tuple的可见性。如果tuple来自CSN小于Snapshot中CSN的事务，则该tuple对当前查询可见。当查询语句在集群的CN节点获取快照后，会将一致性点下发给DN节点。相关的DN使用CN下发的快照中ConsistencyPointCSN判断tuple可见性。
启用GUC参数auto_csn_barrier进行barrier打点，通过CN内置的收集线程持续向备DN收集barrier点的CSN，计算出全局一致性CSN后对备机节点信息进行持久化存储与缓存处理。查询线程基于缓存数据筛选满足一致性点的备DN，将部分读请求下发至目标备节点执行。备DN通过读取备机一致性点的CSN执行查询操作，最终将查询结果回传至CN。
CN开启CSN打点并记录到GTM的流程如[图1]所示。
图1CN打点记录CSN   
![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/figure/zh-cn_image_0000002620720407.png "点击放大")
1. 启用GUC参数"auto_csn_barrier=on"开启barrier打点，生成barrier日志。各备节点回放时更新XlogMaxCSN。
2. CN节点后台线程standby_read_main（复用GTM_FREE后台线程）定时维护全局一致性点。该线程周期性向所有备DN发送消息查询日志回放进度。
3. 备DN收到来自CN的查询请求后返回各自维护的barrier日志回放的最大CSN，即XlogMaxCSN。
4. CN节点计算各分片XlogMaxCSN中位数，取所有分片中位数最小值作为一致性点CSN，并持久化到GTM。
该机制确保所有CSN小于一致性点的事务日志已在多数节点完成回放。完成收集后CN将一致性点CSN同步至GTM供查询使用。
#### 极致RTO场景
在极致RTO备机读机制中，快照新增read_lsn字段用于获取指定LSN版本的历史页数据。DN节点需维护CSN与LSN的映射关系，并记录xmin/xmax信息。当DN收到CN下发的全局一致性快照时，根据CSN查找对应LSN和xmin、xmax信息，构建完整快照。csn-lsn info结构如下图所示。
图2csn-lsn info结构   
![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/figure/zh-cn_image_0000002590360832.png "点击放大")
图3查询整体流程   
![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/figure/zh-cn_image_0000002620640533.png "点击放大")
查询流程：
1. 客户端启用enable_standby_read参数后发起查询请求给CN。
2. CN从GTM获取后台线程standby_read_main设置的一致性点CSN，注入本地快照。
3. CN根据快照CSN（极致RTO模式额外校验回收位置，即DN上的csn-lsn info链上是否保存对应快照）选择符合条件的备DN。
4. CN下发查询请求至备DN。
5. 备DN收到CN请求后，将接收到的CSN注入本地快照，确保CN与DN使用统一全局CSN进行查询。
6. 备DN执行查询并将结果返回CN
7. CN最终向客户端返回查询结果。
 
#### 特性约束
- 分布式GTM-Lite支持备机读的规格与约束：
  - 前置条件：需要开启barrier打点功能，通过参数auto_csn_barrier控制。
  
  - 回放冲突类约束
    - 当查询和回放有锁相关的冲突时（主要为表的DDL操作），与串并行回放备机读相同，取消查询由参数max_standby_streaming_delay（参见《参考》中"数据库运行参数说明 \> GUC参数说明 \> 双机复制 \> 备服务器"章节）控制，报错信息是"canceling statement due to conflict with recovery"。
    
    - 查询时间超出了参数standby_max_query_time（参见《参考》中"数据库运行参数说明 \> GUC参数说明 \> 双机复制 \> 备服务器"章节），报错信息是"User query has exceeded the duration threshold."。
    
    - 触发了备机读文件的强制回收，由参数max_standby_base_page_size、max_standby_lsn_info_size和standby_force_recycle_ratio（参见《参考》中"数据库运行参数说明 \> GUC参数说明 \> 双机复制 \> 备服务器"章节）控制。
    
    - 备机回放段页式物理空间收缩操作相关日志。
    
    - 主机执行REINDEX DATABASE时，备机上的查询和relmap类型日志回放有冲突，报错信息是"User query might have needed to see row versions that must be removed"。
    
    - 主机执行DROP DATABASE、DROP TABLESPACE操作时，备机在回放相应的日志时可能会跟查询冲突，报错信息是"canceling statement due to conflict with recovery"。
     
  
  - 快照类约束
    - 在极致RTO备机读场景下，查询快照信息是由CN发来，可能有滞后，一些数据信息已经更新，在以下几种场景中可能会报错：
      - REINDEX DATABASE等操作，导致relmap变更，报错"snapshot lsn is smaller than lastReplayedConflictLsn due to relmap redo"。
      
      - 节点启动，报错"snapshot lsn is smaller than lastReplayedConflictLsn due to starting action"。
      
      - 备机读文件强制回收，报错"snapshot lsn is smaller than lastReplayedConflictLsn due to force recycle"。
       
    
    - 在极致RTO备机读场景下，集群启停或故障、极致RTO模式下由于某种原因（磁盘空间不足、修改GUC参数、某些节点回放没有跟上等）触发了强制回收，均可能导致备机读报错结束查询（ERROR：snapshot csn is not in csn-lsn list, DETAIL：snapshot csn is 123456789）。
    
    - 在串行/并行回放备机读场景下，主机频繁VACUUM会导致备机读查询的数据被提前清理掉等问题，可能出现查询和回放冲突，报错并结束查询（ERROR: gtm csn small: gtm csn 123456789, lastReplayedConflictCSN 123456999）。
    
    - 在对表进行DDL操作时，CN上的系统表已经被修改，但是备DN可能还未回放这些DDL日志，当使用老快照查询该表时，CN和备DN的元数据可能会不一致。CN在检测到该场景后，会结束访问该表的查询并报错，需要业务进行重试（ERROR: current snapshot is invalid for this relation/partition）。
    
    - 在CN与DN连接时，需根据当前全局一致性点计算对应分片中可用的DN副本，以下场景可能出现报错：
      ```
      LOG: Current slice cannot select a normal standby for standby read, slice: <int>  ...  
      ERROR: pooler_get_dn_handles invalid Datanode number: -1, dnNum: <int>, u_sess->pgxc_cxt.NumDataNodes[<int>]
      ```
      - 场景一：同一数据副本在两个DN上的回放速度不一致，且回放较快的DN发生重启。 处理方法：全局一致性点将停止推进，需等待慢速分片追赶至全局一致性点，报错将自动消除。
        
      
      - 场景二：容灾映射刚完成构建，但尚未完成CN/DN回放与回收信息采集。 处理方法：该报错仅在启动初期短暂出现，等待数秒后不再触发。
        
      
      - 场景三：在极致RTO模式下，不同分片的回放进度差异显著，导致回放较快的分片其回收点超过了全局一致性点。 处理方法：等待业务高峰过后，各分片回放差异减小，报错消失；如果仅个别CN回放较慢，可通过执行cm_ctl stop命令手动停止该CN以规避问题。
        
       
     
  
  - 资源管控
    - 磁盘空间：当前极致RTO备机读文件的空间占用已有阈值保护，参考max_standby_base_page_size、max_standby_lsn_info_size、standby_force_recycle_ratio（参见《参考》中"数据库运行参数说明 \> GUC参数说明 \> 双机复制 \> 备服务器"章节）。
    
    - 内存和IO：需求支持备机读独立BufferPool和独立刷脏，参考enable_standby_bufferpool（参见《参考》中"数据库运行参数说明 \> GUC参数说明 \> 双机复制 \> 备服务器"章节）。
    
    - CPU：当前无资源管控能力，在当节点CPU资源充足的场景下（不超过70%），备机读和回放没有影响。
    
    - 对于小规格（8U以下）场景，极致RTO回放会占用较多资源，不建议打开极致RTO备机读。
     
  
  - 性能规格
    - 打开选AZ（standby_read_use_az_info）或者负载均衡功能（standby_read_use_load_balance），由于需要遍历所有可用备DN节点，再去从中做选择，所以会导致查询性能下降。
    
    - 当主机频繁执行DDL操作时，除了会导致备机上的查询与回放冲突被取消外，还会导致备机上的查询变慢。
    
    - 在96U768G物理机环境下，3C3D混合部署，独立数据盘，配置出口参数，初始数据100G，主备100并发读，主机100并发update、100并发insert，备机读性能达到主机读性能的80%。
     
  
  
  
  - 主机在进行在线重建索引、删除索引、重建索引、删除分区时，CN下发到备机的查询可能会访问到已经被删除的索引或还未被创建的索引，可能会报以下错误，此时业务需要重试。主要的报错类型包括但不限于以下内容：
    - 普通表："Could not open relation with OID xxx."。
    
    - "contains unexpected zero page at block xxx."。
    
    - "index xxx is not a btree."。
    
    - "Tuple has wrong number of attributes in index xxx."。
    
    - "xxx doesn't contain target block."。
    
    - "The target page is not btree."。
    
    - "index xxx is not valid on standby."。
    
    - 分区表："Partition oid xxx is invalid when opening partition current tid."。
    
    - "could not open block during recovery delete object, please try again later."。
    
    - "search sys cache failed when get index list, indexrelid xxx, indrelid xxx, relation xxx"。
    
    - "partition xxx does not exist on relation xxx"。
     
  
  - 在主机并发执行指定位置增删列语句（如ALTER TABLE ADD FIRST/AFTER等）时，若系统表并发修改，备机查询可能出现列类型不匹配的错误，需业务重试。
  
  - 备机回放系统表时不通过延迟DDL。当备机故障恢复并重新对外提供服务后，CN可能下发旧的快照，导致查询时出现找不到历史版本的报错："standby_read_buf can't find history version page"。
  
  - 在分布式场景下，主机执行DDL且备机执行Stream计划的查询时，可能出现CN和备DN看到的表定义不一致，从而导致查询报错。其原因为：分布式备机读执行Stream计划查询时，计划在CN生成，然后下发给备DN执行；而CN作为主机角色，生成Stream计划时使用SnapshotNow访问系统表，备DN执行时则使用SnapshotMvcc读取数据，因此会出现数据与计划中的表定义不一致的问题，引发查询报错。分布式容灾读场景不涉及该问题，因为灾备集群的CN也是备机，使用SnapshotMvcc访问系统表。这类DDL主要包括：增删列、重命名列、扩展列类型varchar大小、修改列类型numeric精度、修改ENUM/SET列定义、重命名表、增删分区、重命名表空间、创建索引CONCURRENTLY、重建索引CONCURRENTLY等。当遇到这类报错时，备机读业务需要重试。主要的报错类型包括但不限于以下内容：
    - attribute xxx has wrong type.
    
    - invalid attribute number xxx；
    
    - attribute number xxx exceeds number of columns xxx.
    
    - pg_class entry for relid xxx vanished during xxx.
    
    - cache lookup failed for index xxx.
     
   
 
