回滚段
Redrock Postgres 维护事务操作的记录,统称为撤消数据。Redrock Postgres 使用撤消数据来执行以下操作:
- 回滚活跃事务
- 恢复异常结束的事务
- 提供读一致性
Redrock Postgres 将撤消数据存储在数据库中而不是外部日志中。撤消数据存储在与数据块一样更新的块中,对这些块的更改会产生重做。 通过这种方式,Redrock Postgres 可以高效地访问撤消数据,而无需读取外部日志。
撤消数据存储在独立的表空间中。Redrock Postgres 提供了一种完全自动化的机制,称为自动回滚段管理模式,用于管理回滚段和存储空间。
当事务开始时,数据库将事务绑定(分配)到回滚段,因此绑定到事务表。
多个活动事务可以同时写入相同的回滚段或不同的回滚段。 例如,事务 T1 和 T2 都可以写入回滚段 U1,或者T1可以写入 U1 而 T2 写入回滚段U2。
从概念上讲,一个回滚段中的撤消数据区形成一个环。事务先写到一个撤消数据区,然后写到环中的下一个撤消数据区,如此等等,循环往复。图 1 显示两个事务 T1 和 T2,从一个回滚段的第三个撤消数据区开始写入,然后继续往第四个撤消数据区 (E4)写入。
在任何给定时间,一个事务仅在一个回滚段上的某个撤消数据区上顺序写入,该区称为事务的当前撤消数据区。多个活动事务可以同时写入同一个当前撤消数据区或不同的当前撤消数据区。图 1 显示事务 T1 和 T2 同时写入撤消数据区 E3。在一个撤消数据区内某个数据块仅包含一个事务的数据。
当前撤消数据区被填充后,第一个需要空间的事务将检查环中分配的下一个撤消数据区的可用性。如果下一个撤消数据区不包含活动事务数据,则该撤消数据区将成为当前撤消数据区。现在,需要空间的所有事务都可以写入新的当前撤消数据区内。在图 2 中, 当E4填满时,T1 和 T2 继续写入 E1,覆盖在E1中的非活动撤消数据。
如果下一个撤消数据区确实包含某个活动事务的数据,则数据库必须分配一个新的撤消数据区。图 3 显示 T1 和 T2 同时写入 E4 的场景。当 E4 填满时,这两个事务无法继续写入 E1,因为 E1 包含活动的撤消记录。因此,数据库为该回滚段分配新的撤消数据区 (E5)。事务继续写入 E5。
当执行 ROLLBACK
语句时,数据库使用撤消记录回滚未提交事务对数据库所做的更改。 在恢复期间,数据库回滚从联机重做日志应用到数据文件的任何未提交的更改。撤消记录通过在另一个用户正在更改数据的同时为访问数据的用户维护数据的前映像来提供读取一致性。
初始化数据库实例数据目录后,数据库中默认会产生 4 个实例级回滚段和8个数据库级别的回滚段,其中实例级回滚段主要用来修改全局系统表的数据,如 pg_database。默认的回滚段数目是在使用initdb
初始化你的 PostgreSQL 数据库实例时选择的。比如:
initdb -u 20
把默认的回滚段数目设置为 20,包括 4 个实例级回滚段和 16 个数据库级别的回滚段。如果你喜欢用长选项字符串,你可以用--undos
代替-u
。
回滚段是使用 CREATE UNDO 命令创建的,并且用 DROP UNDO 命令删除的。要获取当前数据库中现有回滚段的信息,可以查看系统表 pg_undo,例如:
SELECT c.relname, c.relisshared, u.undoid, u.undrelid,
pg_size_pretty(pg_relation_size(c.oid)) AS undsize
FROM pg_undo u
LEFT JOIN pg_class c ON u.undrelid = c.oid;
回滚段支持的事务数取决于回滚段中的事务槽位数。回滚段的首个页面用于存储事务表项信息,单个回滚段同时最多能存放约 200 个事务项。如果单个回滚段同时处理的事务数较多(比如超过 8 个),可能会因为事务表并发访问冲突产生对应的页面访问等待事件。此时,就可以使用 CREATE UNDO 命令在当前数据库中创建更多的回滚段,以提升系统的事务处理性能。