PostgreSQL 18 preview - bitmap Heap Scan支持AIO批量读
PostgreSQL 18 preview - bitmap Heap Scan支持AIO批量读
HeapBitmapScan 是一种扫描表的方法。它首先通过一个或多个索引找到匹配条件的行的物理位置(TID - Tuple Identifier),将这些 TID 收集到一个位图(Bitmap)中。然后,根据这个位图(排序后的block number)去访问表(堆)中对应的页面,并获取实际的行数据(元组)。
Bitmap Heap Scan 启用 AIO 批量模式后,当 Bitmap Heap Scan 需要从磁盘读取大量数据页面时,其 I/O 性能有望得到提升。
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=459e7bf8e2f8
Remove HeapBitmapScan's skip_fetch optimization
author Andres Freund <[email protected]>
Wed, 2 Apr 2025 18:25:17 +0000 (14:25 -0400)
committer Andres Freund <[email protected]>
Wed, 2 Apr 2025 18:54:20 +0000 (14:54 -0400)
commit 459e7bf8e2f8ab894dc613fa8555b74c4eef6969
tree d89ead863ddc22c0615d244c97ce26d3cf9cda32 tree
parent 0dca5d68d7bebf2c1036fd84875533afef6df992 commit | diff
Remove HeapBitmapScan's skip_fetch optimization The optimization does not take the removal of TIDs by a concurrent vacuum into
account. The concurrent vacuum can remove dead TIDs and make pages ALL_VISIBLE
while those dead TIDs are referenced in the bitmap. This can lead to a
skip_fetch scan returning too many tuples.
It likely would be possible to implement this optimization safely, but we
don't have the necessary infrastructure in place. Nor is it clear that it's
worth building that infrastructure, given how limited the skip_fetch
optimization is.
In the backbranches we just disable the optimization by always passing
need_tuples=true to table_beginscan_bm(). We can't perform API/ABI changes in
the backbranches and we want to make the change as minimal as possible.
Author: Matthias van de Meent <[email protected]>
Reported-By: Konstantin Knizhnik <[email protected]>
Discussion: https://postgr.es/m/CAEze2Wg3gXXZTr6_rwC+s4-o2ZVFB5F985uUSgJTsECx6AmGcQ@mail.gmail.com
Backpatch-through: 13
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=67be093562b6b345c170417312dff22f467055ba
Use AIO batchmode for bitmap heap scans
author Melanie Plageman <[email protected]>
Thu, 3 Apr 2025 22:23:02 +0000 (18:23 -0400)
committer Melanie Plageman <[email protected]>
Thu, 3 Apr 2025 22:23:02 +0000 (18:23 -0400)
commit 67be093562b6b345c170417312dff22f467055ba
tree 1dfc38467f362e64f01f6e3dc86d4de8526dd1ba tree
parent 54a3615f15b9b2e1433bc47cfb150317885d7c1f commit | diff
Use AIO batchmode for bitmap heap scans Previously bitmap heap scan was not AIO batchmode safe because of the
visibility map reads potentially donefor the "skip fetch" optimization
(which skipped fetching tuples from the heap if the pages were all
visible and none of the columns were used in the query).
The skip fetch optimization implementation was found to have bugs and
was removed in 459e7bf8e2f8, so we can safely enable batchmode for
bitmap heap scans.
AI 解读
补丁 1:Remove HeapBitmapScan's skip_fetch optimization (移除堆位图扫描的 skip_fetch 优化)
核心内容: 这个补丁移除了 HeapBitmapScan(堆位图扫描)中的一个名为skip_fetch的优化。HeapBitmapScan简介: 这是一种扫描表的方法。它首先通过一个或多个索引找到匹配条件的行的物理位置(TID - Tuple Identifier),将这些 TID 收集到一个位图(Bitmap)中。然后,根据这个位图(排序后的block number)去访问表(堆)中对应的页面,并获取实际的行数据(元组)。skip_fetch优化是什么: 这个优化试图在某些情况下避免实际去堆(heap)中读取数据页。具体条件是:如果这两个条件都满足,优化器就认为不需要真正去读取这个堆页面,只需根据位图中指向该页面的 TID 数量来计数即可,从而节省 I/O。
查询本身不需要从表中获取任何列的数据(例如 SELECT COUNT(*))。位图扫描器检查对应的堆页面的可见性映射(Visibility Map, VM),发现该页面被标记为 ALL_VISIBLE(表示页面上所有元组对所有当前事务都可见)。
并发 VACUUM 的问题: 这个优化没有正确处理与并发运行的 VACUUM命令之间的交互。竞争条件(Race Condition):
一个 Bitmap Heap Scan开始执行,它扫描索引并构建了一个 TID 位图。这个位图可能包含一些指向当时“死亡”(dead)但尚未被物理移除的元组的 TID。与此同时(并发地),一个 VACUUM命令开始清理同一个表。VACUUM会物理移除这些死亡的元组,并可能将它们所在的页面标记为ALL_VISIBLE(因为页面上所有剩余的元组现在都可见了)。Bitmap Heap Scan继续执行,它检查到某个页面的 VM 状态是ALL_VISIBLE,并且(如果查询不需要列数据)决定启用skip_fetch优化。此时,它会根据 原始位图 中指向该页面的 所有 TID 进行计数。但问题在于,这些 TID 中有一部分可能刚刚被 VACUUM清理掉了!结果: skip_fetch优化导致扫描返回了错误的、过多的元组数量(因为它计算了那些已经被并发VACUUM删除的 TID)。
补丁作者认为,虽然理论上可能实现一个安全的 skip_fetch优化,但目前 PostgreSQL 缺乏必要的基础设施(例如更精细的锁或同步机制来处理这种情况)。考虑到构建这种基础设施的复杂性以及 skip_fetch优化本身带来的好处有限,决定直接移除这个有问题的优化。
table_beginscan_bm() 函数时,强制将 need_tuples 参数设置为 true。这实际上就禁用了 skip_fetch 优化,因为该优化只在 need_tuples 为 false 时才可能触发。补丁 2:Use AIO batchmode for bitmap heap scans (为位图堆扫描启用 AIO 批量模式)
核心内容: 这个补丁为 Bitmap Heap Scan启用了 AIO(异步 I/O)的批量模式(batchmode)。AIO 批量模式是什么: 这是一种 I/O 优化技术。操作系统可以一次性接收多个读写请求(一个批次),然后异步地、可能并行地处理它们,而不是一个接一个地同步等待。这通常能提高 I/O 密集型操作的性能,尤其是在读取大量分散的磁盘块时。 为什么以前不能为 Bitmap Heap Scan 启用 AIO 批量模式: 正如第一个补丁所讨论的,之前的 Bitmap Heap Scan包含skip_fetch优化。这个 skip_fetch优化需要在扫描过程中读取可见性映射(VM)来判断页面是否ALL_VISIBLE。这种对 VM 的读取操作,与 AIO 批量模式主要针对批量读取 堆页面 的逻辑存在冲突或不兼容,使得在存在 skip_fetch的情况下安全地实现 AIO 批量模式变得困难或不可能。为什么现在可以启用了: 关键在于第一个补丁(提交号 459e7bf8e2f8)已经移除了有问题的skip_fetch优化。由于不再需要在扫描过程中读取 VM 来做 skip_fetch决策,阻碍Bitmap Heap Scan使用 AIO 批量模式的主要障碍被清除了。好处: 启用 AIO 批量模式后,当 Bitmap Heap Scan需要从磁盘读取大量数据页面时,其 I/O 性能有望得到提升。
总结关系:
这两个补丁是相互关联的:
第一个补丁 发现并移除了 Bitmap Heap Scan中一个存在并发问题的skip_fetch优化。第二个补丁 利用第一个补丁移除 skip_fetch优化(及其伴随的 VM 读取)这一事实,为Bitmap Heap Scan启用了一项重要的性能优化——AIO 批量模式,这在之前是不安全的。
简单来说,第一个补丁修复了一个 bug 并移除了一项有问题的优化,这为第二个补丁安全地引入另一项性能改进(AIO 批量模式)铺平了道路。