GPDB · 特性分析 · Segment事务一致性与异常处理

本文涉及的产品
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS SQL Server,基础系列 2核4GB
简介: 事务一致性 这篇月报Primary和Mirror同步机制讲了Primary和Mirror之间各种数据和文件的同步过程。这些数据和文件的同步,看似彼此独立。那么如何保证某个时间点Mirror的所有数据是一致的,即任何时间点发生HA切换,Mirror都能达到一致性状态并对外提供服务?正如我们前面提到的

事务一致性

这篇月报Primary和Mirror同步机制讲了Primary和Mirror之间各种数据和文件的同步过程。这些数据和文件的同步,看似彼此独立。那么如何保证某个时间点Mirror的所有数据是一致的,即任何时间点发生HA切换,Mirror都能达到一致性状态并对外提供服务?正如我们前面提到的,这主要靠同步的顺序来保证:

  1. AO表的同步是个强同步的过程,数据在更新发生时即同步到Mirror,Primary和Mirror时刻保持着数据一致性。另一方面,事务提交时会更新AO表文件的尾指针信息,并同步到备库。这样事务提交后,Mirror肯定可以看到所有已提交的数据。所以Primary failover切换后AO表数据不会丢失,不影响事务的一致性。

  2. Heap表的数据的更新会写入相应的Xlog,同时确保Heap表数据页的同步必须在对应的Xlog同步完成后才能进行。

    你可能有疑问:Heap表的数据更新同步到Mirror是个异步的过程,那么会不会出现一个事务提交了,Heap表数据更新还没有来得及同步到Mirror,这时候Primary failover切换到Mirror,导致Heap表数据丢失?当然不会的,虽然Heap表数据同步是一个异步的过程,但是Xlog的同步却是一个强同步的过程。Primary failover切换到Mirror之后,Mirror上面的Xlog和Primary的Xlog完全一致,这时候Mirror会启动startup进程,进入Recovery模式,应用Xlog,所以即便Heap表数据没有同步过来,Mirror也会通过Xlog将这些没有过来的数据恢复。

  3. 其他文件的更新,其实不需要实时同步。切换时Mirror会回放Xlog,恢复这些文件的内容。

这样,如果Primary与Mirror保持同步,上述顺序保证了任何时间点Mirror节点的数据都能达到一致。但是,假如出现了网络阻塞或者Mirror宕机的情况,数据就无法及时同步到Mirror。此时,如果Primary在更新数据时,等待数据被同步到Mirror,就会被阻塞,而无法继续提供服务。为解决这个问题,Greenplum提供了异常处理机制。

异常处理

此处所说的对网络阻塞或Mirror宕机异常情况的处理,其实就是Greenplum所谓的Change Tracking机制。在Primary向Mirror同步数据的时候,如果Mirror长时间没有回应Primary,那么Primary将会认为Mirror挂掉,这个时候Master将会把系统表gp_segment_configuration里面的Mirror状态(status)置为’d’,同时把Primary的模式(mode)置为’c’,而这个’c’模式就是ChangeTracing Mode。Primary进入Change Tracking状态后,不会再试图同步数据到Mirror(每次有数据更改时会调用FileRepPrimary_IsMirroringRequired进行检查是否需要同步)。

Primary进入ChangeTracing Mode之后,每次更新数据产生的Xlog,会被立即解析成数据更新记录,放入到Change log文件里面(参见ChangeTracking_AddRecordFromXlog函数)。Change log文件在pg_changetracking/目录下:

-rw------- 1 bgadmin bgadmin    196608 Apr 10 18:22 CT_LOG_FULL
-rw------- 1 bgadmin bgadmin    403991 Apr 10 18:22 FILEREP_CONFIG_LOG
-rw------- 1 bgadmin bgadmin 105119744 Apr 10 18:22 FILEREP_LOG

ChangeTracing信息具体记录在CT_LOG_FULL文件里面,文件记录的核心信息是当前的数据更新在Xlog中的位置,文件内容经过解析之后如下:

postgres=# select * from gp_changetracking_log(0) limit 3;
 segment_id | dbid | space |  db   | rel  |   xlogloc    | blocknum | persistent_tid | persistent_sn
------------+------+-------+-------+------+--------------+----------+----------------+---------------
          0 |    2 |  1663 | 10893 | 5043 | (0/40000160) |        0 | (2,36)         |           538
          0 |    2 |  1663 | 10893 | 5043 | (0/400001C0) |        0 | (2,36)         |           538
          0 |    2 |  1663 | 10893 | 5043 | (0/40000220) |        0 | (2,36)         |           538

Change log里面只存放Heap表的修改信息。AO表的更新信息存放在特定的表中,无需另外记录。而Xlog和普通文件,都完整保存在Primary的数据目录下,无需记录修改信息(具体原因在异常恢复的说明中有介绍)。

Change log其实是存放的Heap表更改页的元数据信息,并未存放更改的实际内容。所以它并不占用太多存储空间,另外它还可以对相同页上的修改进行合并,进一步压缩大小,减少恢复时间(参见FileRepPrimary_RunChangeTrackingCompacting函数)。

那么,Mirror在宕机后重启或网络拥塞发生后恢复正常了,重新连接到Primary后,如何通过Change log恢复同步呢?

  • 异常恢复

    在异常情况消失后,需要通过执行gprecoverseg -a命令来恢复同步。下面我们还是把数据分成4类来说明这个过程总数据同步恢复的过程。

  • Heap表恢复

    当我们执行gprecoverseg -a命令恢复Mirror时,Primary的Resync进程通过Change log信息,再结合Xlog,将积累的数据变更再次同步给Mirror。

    具体步骤如下:

    1. Resync manager进程通过调用一系列函数FileRepResyncManager_InResyncTransition->PersistentFileSysObj_MarkPageIncrementalFromChangeLog->PersistentFileSysObj_MarkPageIncremental->PersistentFileSysObj_UpdateTuplePageIncremental,将需要更新的Page的信息加入到ResyncEntry同步队列中。

    2. Resync worker进程从ResyncEntry队列中取出需要恢复到Mirror的任务,通过调用一系列函数FileRepPrimary_ResyncBufferPoolIncrementalWrite->smgrwrite将Page写到缓冲区中,再通过上面的方法构造消息发送到Mirror。

  • AO表恢复

    因为AO表不写Xlog,所以Change log中并没有记录AO表更新操作。那么Mirror是如何将AO表恢复到和Primary数据一致呢?其实在GP catalog中有一张gp_persistent_relation_node表,里面记录了AO表同步到Mirror的最后一次文件位置(mirror_append_only_loss_eof),以及当前文件位置(mirror_append_only_loss_eof)。AO表文件位置的更新是在FinishPreparedTransaction函数完成的(执行COMMIT PREPARED or ROLLBACK PREPARED)。最后Resync worker进程将会把未同步到Mirror的增量数据再次通过消息同步给它,最终达到Primary和Mirror的数据一致性。

      postgres=# select * from gp_persistent_relation_node where relfilenode_oid=25386;
      -[ RECORD 1 ]-------------------------------------+----------
      tablespace_oid                                    | 1663
      database_oid                                      | 10893
      relfilenode_oid                                   | 25386
      segment_file_num                                  | 1
      relation_storage_manager                          | 2
      persistent_state                                  | 2
      create_mirror_data_loss_tracking_session_num      | 0
      mirror_existence_state                            | 3
      mirror_data_synchronization_state                 | 1
      mirror_bufpool_marked_for_scan_incremental_resync | f
      mirror_bufpool_resync_changed_page_count          | 0
      mirror_bufpool_resync_ckpt_loc                    | (0/0)
      mirror_bufpool_resync_ckpt_block_num              | 0
      mirror_append_only_loss_eof                       | 301742600
      mirror_append_only_new_eof                        | 310752552
      relation_bufpool_kind                             | 0
      parent_xid                                        | 0
      persistent_serial_num                             | 754
      previous_free_tid                                 | (0,0)
    
  • Xlog文件的恢复

    我们知道,Xlog文件其实都保存在Primary的pg_xlog目录下。在异常恢复时,把改目录下的文件直接发送到Mirror即可。需要注意的是,异常期间是可以正常删除或规定Xlog文件的,这是因为,所有的Heap表和AO表的修改都已经被保存下来了,无需另外保留一份Xlog来恢复数据。

  • 其他文件的恢复

    其他文件(pg_control, pg_clog, pg_mutitrans等文件)也和Xlog文件一样,在恢复时直接全部发送到Mirror,具体参见FileRepResyncManager_ResyncFlatFiles函数。

目录
相关文章
|
存储 Oracle 关系型数据库
从源码角度,分析PG事务号分配机制和防止事务号回卷
从源码角度,分析PG事务号分配机制和防止事务号回卷
从源码角度,分析PG事务号分配机制和防止事务号回卷
|
存储 缓存 Go
Boltdb学习笔记之三--事务与并发控制
Boltdb学习笔记之三--事务与并发控制
274 0
|
SQL 存储 关系型数据库
MySQL · 引擎特性 · InnoDB 事务锁系统简介
前言 本文的目的是对 InnoDB 的事务锁模块做个简单的介绍,使读者对这块有初步的认识。本文先介绍行级锁和表级锁的相关概念,再介绍其内部的一些实现;最后以两个有趣的案例结束本文。 本文所有的代码和示例都是基于当前最新的 MySQL5.7.10 版本。 行级锁 InnoDB 支持到行级别
2197 0
|
数据库
Database · 理论基础 · 关于一致性协议和分布式锁
关于一致性协议, 分布式锁以及如何使用分布式锁 最近看antirez 和 Martin 关于redlock 的分布式锁是否安全的问题的争吵, 非常有意思 http://martin.kleppmann.
1871 0
|
存储 关系型数据库 数据库
|
缓存 关系型数据库 数据库
|
人工智能 JavaScript 算法
MySQL · 引擎特性 · Innodb 锁子系统浅析
锁类型Innodb 的锁从锁粒度上大致可以分为行锁和表锁,之前接触过的Berkeley DB(MySQL 5.1前的事务储存引擎,后被 Innodb 取代)只对存储格式为 Hash 的定长数据支持行锁,对于 Btree 格式的仅支持页锁,作为 KV 类型的存储引擎,锁的类型也相对简单。
4118 0