PostgreSQL码农集散地

PostgreSQL 18 preview - 新增IO合并硬限制: io_max_combine_limit

PostgreSQL 18 preview - 新增IO合并硬限制: io_max_combine_limit

这两个patch都是在PostgreSQL 18引入了对AIO支持后的调整: 关于PostgreSQL数据库中I/O合并限制的改进,目的是提高I/O性能,并为未来的扩展做准备。

新增IO合并硬限制: io_max_combine_limit.  同时将IO合并软限制参数 io_combine_limit 最大值增加到 1MB.

https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=10f6646847515b1ab02735c24b04abaf1996f65f

Introduce io_max_combine_limit.  
author Thomas Munro <[email protected]>   
Tue, 18 Mar 2025 22:40:56 +0000 (11:40 +1300)  
committer Thomas Munro <[email protected]>   
Wed, 19 Mar 2025 02:23:54 +0000 (15:23 +1300)  
commit 10f6646847515b1ab02735c24b04abaf1996f65f  
tree e49ca63aec12d99997f4fa91a6d4e3a11dbbe858 tree  
parent 17d8bba6dad12e14a7cafca9ef5eef21e577e9c3 commit | diff  
Introduce io_max_combine_limit.  

The existing io_combine_limit can be changed by users.  The new  
io_max_combine_limit is fixed at server startup time, and functions as a  
silent clamp on the user setting.  That in itself is probably quite  
useful, but the primary motivation is:  

aio_init.c allocates shared memory for all asynchronous IOs including  
some per-block data, and we didn't want to waste memory you'd never used  
by assuming they could be up to PG_IOV_MAX.  This commit already halves  
the size of 'AioHandleIov' and 'AioHandleData'.  A follow-up commit can  
now expand PG_IOV_MAX without affecting that.  

Since our GUC system doesn't support dependencies or cross-checks  
between GUCs, the user-settable one now assigns a "raw" value to  
io_combine_limit_guc, and the lower of io_combine_limit_guc and  
io_max_combine_limit is maintained in io_combine_limit.  

Reviewed-by: Andres Freund <[email protected]> (earlier version)  
Discussion: https://postgr.es/m/CA%2BhUKG%2B2T9p-%2BzM6Eeou-RAJjTML6eit1qn26f9twznX59qtCA%40mail.gmail.com  

https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=06fb5612c970b3af95aca3db5a955669b07537ca

Increase io_combine_limit range to 1MB. master github/master  
author Thomas Munro <[email protected]>   
Wed, 19 Mar 2025 02:23:12 +0000 (15:23 +1300)  
committer Thomas Munro <[email protected]>   
Wed, 19 Mar 2025 02:40:35 +0000 (15:40 +1300)  
commit 06fb5612c970b3af95aca3db5a955669b07537ca  
tree a3cfb560033bfd70e68f72e8d58ddc99250f5afb tree  
parent 10f6646847515b1ab02735c24b04abaf1996f65f commit | diff  
Increase io_combine_limit range to 1MB.  

The default of 128kB is unchanged, but the upper limit is changed from  
32 blocks to 128 blocks, unless the operating system's IOV_MAX is too  
low.  Some other RDBMSes seem to cap their multi-block buffer pool I/O  
around this number, and it seems useful to allow experimentation.  

The concrete change is to our definition of PG_IOV_MAX, which provides  
the maximum for io_combine_limit and io_max_combine_limit.  It also  
affects a couple of other places that work with arrays of struct iovec  
or smaller objects on the stack, so we still don'
t want to use the  
system IOV_MAX directly without a clamp: it is not under our control and  
likely to be 1024.  128 seems acceptable for our current usage.  

For Windows, we can't use real scatter/gather yet, so we continue to  
define our own IOV_MAX value of 16 and emulate preadv()/pwritev() with  
loops.  Someone would need to research the trade-offs of raising that  
number.  

NB if trying to see this working: you might temporarily need to hack  
BAS_BULKREAD to be bigger, since otherwise the obvious way of "a very  
big SELECT" is limited by that for now.  

Suggested-by: Tomas Vondra <[email protected]>  
Discussion: https://postgr.es/m/CA%2BhUKG%2B2T9p-%2BzM6Eeou-RAJjTML6eit1qn26f9twznX59qtCA%40mail.gmail.com  

AI 解读

这两个patch都是关于PostgreSQL数据库中I/O合并限制的改进,目的是提高I/O性能,并为未来的扩展做准备。下面是详细的中文解读:

Patch 1: Introduce io_max_combine_limit (引入 io_max_combine_limit)

这个patch引入了一个新的配置参数 io_max_combine_limit,它与现有的 io_combine_limit 协同工作,但目的和行为有所不同。

  • 背景和动机:

    • 现有的 io_combine_limit 允许用户修改,用于控制单个I/O操作可以合并的最大块数。
    • aio_init.c 文件为异步I/O分配共享内存,包括每个块的数据。为了避免浪费内存,需要限制可以合并的最大块数。  之前,程序假设可以合并的块数最大为 PG_IOV_MAX,这可能导致不必要的内存分配。
    • 这个patch的目标是减少 AioHandleIov 和 AioHandleData 的大小,以便后续的patch可以扩大 PG_IOV_MAX,而不会显著增加内存占用。
  • 主要功能:

    • io_max_combine_limit 的引入:  这是一个在服务器启动时固定的参数,作为用户设置的 io_combine_limit 的上限。
    • io_combine_limit 的行为改变: 用户设置的 io_combine_limit 现在被存储在一个名为 io_combine_limit_guc 的变量中,这是一个“原始”值。  实际使用的 io_combine_limit 是 io_combine_limit_guc 和 io_max_combine_limit 中的较小值。
    • 目的:io_max_combine_limit 充当一个“静默钳制器”,确保用户设置的 io_combine_limit 不会超过系统允许的最大值。  这主要是为了控制内存使用,并为未来的扩展提供灵活性。
  • 为什么需要两个参数?

    • PostgreSQL的GUC(Grand Unified Configuration)系统不支持参数之间的依赖关系或交叉检查。  因此,无法直接限制用户设置的 io_combine_limit。  引入 io_max_combine_limit 提供了一种间接的方式来强制执行上限。
  • 总结:  这个patch引入了 io_max_combine_limit,作为用户可配置的 io_combine_limit 的上限,主要目的是控制内存使用,并为后续扩大 PG_IOV_MAX 做准备。

Patch 2: Increase io_combine_limit range to 1MB (将 io_combine_limit 范围增加到 1MB)

这个patch扩展了 io_combine_limit 的允许范围,允许更大的I/O合并,从而可能提高性能。

  • 背景和动机:

    • 之前的 io_combine_limit 的上限是32个块。
    • 其他关系型数据库管理系统(RDBMS)似乎将它们的多块缓冲区池I/O限制在类似的数量级。
    • 允许更大的I/O合并可能是有益的,因此需要扩展 io_combine_limit 的范围。
  • 主要功能:

    • PG_IOV_MAX 的修改:PG_IOV_MAX 定义了 io_combine_limit 和 io_max_combine_limit 的最大值。  这个patch增加了 PG_IOV_MAX 的值,允许更大的I/O合并。
    • 范围扩展:  默认值保持不变(128kB),但上限从32个块增加到128个块,除非操作系统的 IOV_MAX 太低。
    • Windows 特殊处理:  由于Windows不支持真正的scatter/gather I/O,因此继续使用自定义的 IOV_MAX 值(16),并使用循环来模拟 preadv() 和 pwritev()。  提高Windows上的 IOV_MAX 需要进一步的研究。
  • 注意事项:

    • 即使增加了 io_combine_limit 的范围,BAS_BULKREAD 的大小也可能成为性能瓶颈。  可能需要临时修改 BAS_BULKREAD 的大小,才能充分利用更大的 io_combine_limit。
    • 即使系统 IOV_MAX 很大(例如1024),PG_IOV_MAX 仍然被限制为128,因为在某些情况下,PostgreSQL在堆栈上使用 iovec 结构体数组,过大的值可能会导致堆栈溢出。
  • 总结:  这个patch通过增加 PG_IOV_MAX 的值,扩展了 io_combine_limit 的允许范围,允许更大的I/O合并,从而可能提高性能。  同时,它也考虑了Windows平台的特殊性,并提醒注意其他潜在的性能瓶颈。

总而言之,这两个patch共同作用,旨在提高PostgreSQL的I/O性能,并为未来的扩展提供更大的灵活性。  第一个patch引入了 io_max_combine_limit 来控制内存使用,而第二个patch则扩展了 io_combine_limit 的范围,允许更大的I/O合并。


好的,让我们来详细了解一下 BAS_BULKREAD 在 PostgreSQL 中的作用,以及它与 I/O 性能的关系。

BAS_BULKREAD 的作用

BAS_BULKREAD 是 PostgreSQL 源代码中定义的一个常量,它控制着后端进程(backend process)在一次操作中可以从磁盘读取的最大字节数。 简单来说,它限制了后端进程一次性读取的数据量。

更详细的解释:

  • Bulk Read(批量读取):BAS_BULKREAD 涉及的是 PostgreSQL 的批量读取机制。当需要从磁盘读取大量数据时(例如,执行一个大的 SELECT 查询,或者扫描一个大的表),PostgreSQL 会尝试使用批量读取来提高效率。批量读取意味着一次性读取多个数据块,而不是逐个读取。

  • 限制读取大小:BAS_BULKREAD 定义了单次批量读取操作可以读取的最大字节数。  如果需要读取的数据量超过 BAS_BULKREAD,PostgreSQL 会将读取操作分解为多个较小的批量读取操作。

  • 与 io_combine_limit 的关系:io_combine_limit 控制的是单个 I/O 操作可以合并的最大块数。  BAS_BULKREAD 则限制了 总共 可以读取的字节数。  即使 io_combine_limit 允许合并多个块,如果这些块的总大小超过 BAS_BULKREAD,实际读取的数据量仍然会受到 BAS_BULKREAD 的限制。

BAS_BULKREAD 的重要性

BAS_BULKREAD 的值直接影响 PostgreSQL 的 I/O 性能,尤其是在处理大型数据集时。

  • 性能瓶颈: 如果 BAS_BULKREAD 的值太小,即使磁盘和 I/O 子系统具有很高的吞吐量,PostgreSQL 也可能无法充分利用它们。  因为读取操作会被分解为许多小的批量读取,这会增加 I/O 操作的开销(例如,磁盘寻道时间)。

  • 内存使用:BAS_BULKREAD 的值也与内存使用有关。  更大的 BAS_BULKREAD 值意味着后端进程需要分配更大的缓冲区来存储读取的数据。  因此,需要根据系统的可用内存来调整 BAS_BULKREAD 的值。

如何调整 BAS_BULKREAD

BAS_BULKREAD 不是一个可以通过 GUC 参数直接配置的选项。它通常在编译时通过 C 预处理器定义。这意味着要更改 BAS_BULKREAD 的值,需要修改 PostgreSQL 的源代码,然后重新编译。

修改 BAS_BULKREAD 的步骤(仅供参考,请谨慎操作):

  1. 找到定义 BAS_BULKREAD 的位置:  在 PostgreSQL 源代码中搜索 BAS_BULKREAD 的定义。它通常位于 src/include 目录下的某个头文件中(例如,src/include/storage/bufpage.h 或类似的)。

  2. 修改定义:  修改 BAS_BULKREAD 的值。  例如,如果原来的定义是 #define BAS_BULKREAD (8192 * BLCKSZ),你可以将其修改为 #define BAS_BULKREAD (16384 * BLCKSZ),将读取大小翻倍(假设 BLCKSZ 是块大小,通常是 8192 字节)。

  3. 重新编译 PostgreSQL:  使用标准的 PostgreSQL 编译过程重新编译 PostgreSQL。

  4. 部署新版本:  停止 PostgreSQL 服务器,替换可执行文件,然后重新启动服务器。

警告:

  • 修改源代码有风险:  修改 PostgreSQL 的源代码可能会引入错误,导致系统不稳定。  请务必在测试环境中进行充分的测试,然后再在生产环境中使用。
  • 了解你的系统:  在修改 BAS_BULKREAD 之前,请务必了解你的系统的硬件配置和工作负载。  不正确的 BAS_BULKREAD 值可能会降低性能,甚至导致系统崩溃。
  • 考虑其他优化方法:  在修改 BAS_BULKREAD 之前,请考虑其他可能的优化方法,例如调整 shared_buffers、work_mem 等 GUC 参数,或者优化查询语句。

总结

BAS_BULKREAD 是 PostgreSQL 中控制批量读取大小的一个重要常量。它直接影响 I/O 性能,尤其是在处理大型数据集时。虽然可以修改 BAS_BULKREAD 的值,但需要谨慎操作,并充分了解系统的硬件配置和工作负载。在修改 BAS_BULKREAD 之前,请考虑其他可能的优化方法。

希望这个解释能够帮助你更好地理解 BAS_BULKREAD 的作用。