【尚学堂】Java300集零基础适合初学者视频教程_Java300集零基础教程_Java初学入门视频基础巩固教程_Java语言入门到精通_哔哩哔哩_bilibili
前言
对 PostgreSQL 中数据的并发访问由多版本并发控制 (MVCC) 模型管理。 为每个 SQL 语句维护数据快照,以便它们始终获得一致的数据,即使其他事务正在同时对其进行修改。 当行已被一个或多个事务修改时,这将导致管理同一行的多个版本。 从用户的角度来看,可能只有一行数据,但 PostgreSQL 内部可能维护该行的一个或多个版本。
行版本是否对事务可见是通过堆中的行数据来维护的。 为了优化可见性信息的获取,PostgreSQL 还维护了一个“_vm”关系分支,它跟踪那些只包含对所有活跃事务可见的元组的页面。
对任何事务不再可见的死版本将由 vacuum 进程清除。 在此之前,索引和堆页面可能包含大量死元组(这实际上取决于您的工作负载的性质)。 对于更新密集型工作负载,这可能是一个巨大的数字!
乍一看似乎无害,但这种死索引元组的累积会产生级联效应,从而导致性能显着下降。 在 PostgreSQL 13 中完成重复数据删除工作后,下一个合乎逻辑的步骤是通过减少页面拆分来防止 btree 索引膨胀。
物理数据存储
PostgreSQL 将数据保存在被称为页面的固定大小存储单元中。 页面的大小是在 PostgreSQL 服务器编译过程中定义的。 默认页面大小为 8k,但可以将其更改为更高的值。 虽然更改页面大小会使事情变得复杂,因为其他工具可能也需要重新编译或重新配置。
每个表和索引都存储在页数组中。 将数据插入表中时,会将数据写入具有足够可用空间的页面。 否则,将创建一个新页面。
然而,索引有点不同。 索引中的第一页是一个元页面,其中包含有关索引的控制信息。 也有一些特殊的页面来维护索引相关的信息。 对于 btree 索引,数据必须根据索引列和堆元组 ID(元组在表中的物理位置)进行排序。 因此,插入和更新必须在正确的页面上进行,以保持排序顺序。 如果页面没有足够的空间容纳传入的元组,则必须创建新页面,并将溢出页面中的一些项目移动到新页面。 如果需要,这些叶子页面的父页面会被递归拆分。
避免页面分裂
当新的元组或新的非 HOT 元组版本要添加到索引中时,会发生 B-Tree 索引页拆分。 HOT 是“heap only tuple”的缩写。 基本而言,它是一种删除给定页面上的死行(碎片整理)并因此为新行腾出空间的方法。 通过避免或延迟页面拆分,我们可以避免或减慢索引扩展,从而减少膨胀。 现在这很令人