找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3090

积分

0

好友

418

主题
发表于 5 小时前 | 查看: 2| 回复: 0

本文解读 PostgreSQL 19 中由 John Naylor 提交的关于校验和硬件加速的改进。这些改进主要涵盖两大平台:x86 平台的 AVX2 优化,以及 ARM 平台的 CRC32C 硬件加速支持。对于追求极致性能和数据安全性的 数据库 管理员和开发者而言,这是一个值得关注的重要更新。

相关 Commit

Commit 描述
5e13b0f24039 Use AVX2 for calculating page checksums where available
fbc57f2bc2ee Compute CRC32C on ARM using the Crypto Extension where available
effaa464afd3 Check for __cpuidex and __get_cpuid_count separately
f6bd9f0fe25a Skip common prefixes during radix sort

核心价值

  • 数倍性能提升:AVX2 提供 256 位寄存器,相比之下 SSE2 只有 128 位,理论上能带来翻倍的吞吐量。
  • 智能自动选择:运行时动态检测 CPU 硬件能力,自动选择最优的实现方案,对用户完全透明。
  • 全面的跨平台支持:不仅覆盖 x86 (AVX2),也支持 ARM (CRC32C/PMULL) 平台,确保了在不同 系统 架构上都能受益。

原理详解

1. 页面校验和算法

1.1 算法文件 (src/include/storage/checksum_block.inc.c)

页面校验和采用的核心算法是 并行 FNV-1a 哈希算法。其基本原理是将数据页面视为一个字节流,通过迭代计算产生一个32位的校验和值,用于后续的数据完整性验证。

/*
 * 页面校验和计算
 * 原理:将页面分成多个列,并行计算 32 个校验和,然后 XOR 在一起
 */
static inline uint32
checksum_block(const PageHeader page, BlockNumber block)
{
    uint32 sum = 0;
    int i;

    /* FNV-1a 哈希的种子 */
    const uint32 FNV_32_PRIME = 0x01000193;
    const uint32 FNV_32_OFFSET = 0x811c9dc5;

    /* 对页面每个字节进行哈希 */
    for (i = 0; i < BLCKSZ; i++)
    {
        sum = (sum ^ page[i]) * FNV_32_PRIME;
    }

    /* 混合块号 */
    sum ^= block;

    return sum;
}

1.2 改进版本(利用向量化)

上述 C 代码在编译时,如果启用了对应的编译器标志,可以被自动向量化。向量化级别取决于 CPU 支持的指令集:

SSE2:  128 位寄存器 → 同时处理 16 字节
AVX2:  256 位寄存器 → 同时处理 32 字节(性能提升 2 倍+)

关键在于,同样的源代码,针对不同的指令集目标编译,会生成效率迥异的机器码。PostgreSQL 19 的改进,正是为了充分利用 AVX2 等高级向量指令集。

2. AVX2 实现

2.1 分派函数 (src/backend/storage/page/checksum.c line 52-62)

PostgreSQL 采用了一种优雅的运行时分派机制。在首次需要计算校验和时,通过一个“选择器”函数检测当前 CPU 能力,并将计算函数指针指向最优的实现。

/*
 * 运行时选择最佳实现
 * 在首次调用时检测 CPU 能力并设置函数指针
 */
static uint32
pg_checksum_choose(const PGChecksummablePage *page)
{
    pg_checksum_block = pg_checksum_block_fallback;

#ifdef USE_AVX2_WITH_RUNTIME_CHECK
    if (x86_feature_available(PG_AVX2))
        pg_checksum_block = pg_checksum_block_avx2;
#endif

    return pg_checksum_block(page);
}

2.2 AVX2 函数声明 (line 39-45)

AVX2 版本的函数通过编译器属性明确指定了编译目标,确保编译器生成 AVX2 指令。

/*
 * 使用 pg_attribute_target 指示编译器为此函数生成 AVX2 指令
 * 同一份 C 代码会为 AVX2 目标编译出不同的机器码
 */
#ifdef USE_AVX2_WITH_RUNTIME_CHECK
pg_attribute_target("avx2")
static uint32
pg_checksum_block_avx2(const PGChecksummablePage *page)
{
#include “storage/checksum_block.inc.c”
}
#endif

2.3 备选函数 (line 30-34)

为了保证兼容性,始终保留一个不依赖特定指令集的“后备”实现(fallback)。

/* scalar fallback 实现 */
static uint32
pg_checksum_block_fallback(const PGChecksummablePage *page)
{
#include “storage/checksum_block.inc.c”
}

3. CRC32C 硬件加速

除了通过向量化提升通用哈希计算速度,PostgreSQL 还支持使用 CPU 内置的专用指令进行 CRC32C 计算,这在 ARM 和部分 x86 平台上效率极高。

3.1 Intel SSE4.2/AVX-512 实现 (src/port/pg_crc32c_sse42.c)

对于支持 SSE4.2 或 AVX-512 的 Intel/AMD CPU,PostgreSQL 使用编译器内置函数(intrinsics)直接调用硬件指令。

/*
 * CRC32C 硬件加速(Intel SSE4.2)
 * 使用 CRC32 指令计算 32 位循环冗余校验
 */
static uint32
pg_comp_crc32c_sse42(uint32 crc, const void *data, size_t len)
{
    const unsigned char *p = data;
    const unsigned char *end = p + len;

    while (p < end)
    {
        /* 使用 CRC32B 指令(字节)*/
        crc = __builtin_ia32_crc32qi(crc, *p);
        p++;
    }

    return crc;
}

3.2 ARM CRC 扩展 (src/port/pg_crc32c_armv8.c line 29-83)

ARMv8 架构也提供了类似的 CRC 计算指令,PostgreSQL 对此进行了适配。

/*
 * ARMv8 CRC 扩展
 * 使用 __crc32cb, __crc32ch, __crc32cw, __crc32cd 等指令
 */
static inline uint32
pg_crc32c_armv8(uint32 crc, const void *data, size_t len)
{
    const unsigned char *p = data;

    while (len >= 4)
    {
        /* 使用 CRC32CW 指令(4 字节)*/
        crc = __crc32cw(crc, *(uint32_t *)p);
        p += 4;
        len -= 4;
    }
    // ... 处理剩余字节
}

3.3 ARM Crypto Extension - PMULL (src/port/pg_crc32c_armv8.c line 85-206)

对于更高级的 ARM 处理器,PostgreSQL 还能利用加密扩展中的 PMULL 指令进行加速。

/*
 * 使用 ARM Crypto Extension 的 PMULL(多项式长乘法)指令
 * 通过 PCLMUL(Carry-less Multiply)实现 CRC
 */
static uint32
pg_comp_crc32c_pmull(uint32 crc, const void *data, size_t len)
{
    /*
     * PMULL 指令可以并行执行多项式乘法
     * 用于加速 CRC 计算中的 GF(2) 多项式运算
     */
    // ...
}

4. 运行时检测机制

硬件加速功能的前提是准确识别 CPU 能力。PostgreSQL 在多个平台实现了健壮的检测机制。

4.1 x86 CPU 检测 (src/port/pg_cpu_x86.c line 100-139)

在 x86 平台,通过执行 CPUID 指令来获取 CPU 特性标识。

/*
 * 使用 CPUID 指令检测 CPU 特性
 */
static void
set_x86_features(void)
{
    int info[4];

    /* 获取 CPU 支持的特性 */
    __cpuid(1, info[0], info[1], info[2], info[3]);

    /* 检查 SSE4.2 (用于 CRC32C) */
    if (info[2] & (1 << 20))  /* SSE4.2 位 */
        x86_feature_enabled[X86_FEATURE_SSE42] = true;

    /* 检查 AVX */
    if (info[2] & (1 << 28))  /* AVX 位 */
        x86_feature_enabled[X86_FEATURE_AVX] = true;

    /* 检查 AVX2 */
    if (has_avx2())
        x86_feature_enabled[X86_FEATURE_AVX2] = true;
}

4.2 ARM 功能检测 (src/port/pg_crc32c_armv8_choose.c line 44-109)

在 ARM/Linux 平台上,通常通过读取 auxv(辅助向量)来获取硬件能力信息。

/*
 * 使用 getauxval() 获取硬件能力
 */
static bool
pg_crc32c_armv8_available(void)
{
    uint64_t hwcap = getauxval(AT_HWCAP);

    /* 检查 CRC32 扩展 */
    if (hwcap & HWCAP_CRC32)
        return true;

    return false;
}

static bool
pg_pmull_available(void)
{
    uint64_t hwcap2 = getauxval(AT_HWCAP2);

    /* 检查 PMULL(Carry-less Multiply)扩展 */
    if (hwcap2 & HWCAP2_PMULL)
        return true;

    return false;
}

5. 函数指针分派机制

为了实现“一次检测,终身受用”,PostgreSQL 采用了函数指针分派模式。全局的函数指针初始指向一个“选择器”,首次调用时完成检测并替换为最优的实现函数。

5.1 CRC32C 分派 (src/port/pg_crc32c_sse42.c line 193)

/*
 * 初始化时指向 choose 函数
 * 首次调用时 choose 会检测并替换为最优实现
 */
pg_crc32c (*pg_comp_crc32c)(pg_crc32c crc, const void *data, size_t len) = pg_comp_crc32c_choose;

5.2 分派函数 (src/port/pg_crc32c_sse42.c line 170-191)

选择器函数包含了完整的检测逻辑,并设置了合理的后备方案。

/*
 * 首次调用时检测 CPU 特性并替换为最优实现
 */
static pg_crc32c
pg_comp_crc32c_choose(pg_crc32c crc, const void *data, size_t len)
{
    /* 设置软实现作为 fallback */
#ifdef USE_SSE42_CRC32C_WITH_RUNTIME_CHECK
    pg_comp_crc32c = pg_comp_crc32c_sb8;
#endif

    /* 检测 SSE4.2 */
    if (x86_feature_available(PG_SSE4_2))
        pg_comp_crc32c = pg_comp_crc32c_sse42;

#ifdef USE_AVX512_CRC32C_WITH_RUNTIME_CHECK
    /* 检测 AVX-512 */
    if (x86_feature_available(PG_AVX512_VL) &&
        x86_feature_available(PG_AVX512_VPCLMULQDQ))
        pg_comp_crc32c = pg_comp_crc32c_avx512;
#endif

    return pg_comp_crc32c(crc, data, len);
}

6. 性能提升对比

那么,这些硬件加速手段究竟带来了多少性能提升呢?下面的表格提供了一个理论上的对比:

实现 寄存器宽度 每周期处理 相对性能
Scalar 32 位 1 字节 1x
SSE2 128 位 16 字节 ~8x
SSE4.2 CRC 128 位 + 硬件 CRC 16 字节 + CRC 指令 ~15x
AVX2 256 位 32 字节 ~16x
AVX-512 512 位 64 字节 ~32x

注意:上表为理论最大值,实际性能受内存带宽、CPU微架构、编译器优化等多种因素影响。


使用实践

1. 查看校验和功能是否启用

在 PostgreSQL 中,你可以通过以下 SQL 命令检查集群是否启用了数据页校验和功能。

-- 检查数据校验和是否启用
SHOW data_checksums;

2. 验证 CPU 支持

在部署 PostgreSQL 19 之前,可以先确认你的服务器 CPU 是否支持相应的硬件加速指令。

# x86 检测 AVX2
grep -q avx2 /proc/cpuinfo && echo “AVX2 supported”

# ARM 检测 CRC32
grep -q crc32 /proc/cpuinfo && echo “CRC32 supported”

3. 使用 pg_test_timing 测试

PostgreSQL 自带一个名为 pg_test_timing 的工具,可以用来测试计时器的精度,新版中也可能包含对校验和性能的测试选项(请以实际版本手册为准)。

# 校验和性能测试(如果该版本支持此参数)
pg_test_timing --checksums

4. 监控校验和计算开销

启用校验和会引入额外的 CPU 计算。以下是一个概念性的性能开销估算(基于 pgbench 基准测试,scale factor=100):

基准测试(pgbench -s 100):
- 无校验和: ~5% CPU 用于写入
- 有校验和(SSE2): ~6% CPU 用于写入
- 有校验和(AVX2): ~5.5% CPU 用于写入

实际影响取决于工作负载和 CPU

可以看到,硬件加速(AVX2)能有效降低因启用校验和而带来的额外 CPU 开销,使其更接近于“无校验和”的状态。

5. 在不同平台上验证

x86_64 Linux

# 编译时查看支持的特性(部分信息)
pg_config --cflags

# 运行时查看数据库版本及编译选项
SELECT version();

ARM (aarch64)

# 检查 ARM 特性
cat /proc/cpuinfo | grep -E “Features|CPU architecture”

6. 启用数据校验和

如果你正在初始化一个新的集群,或者想为现有集群启用校验和(PostgreSQL 11+ 支持在线启用,但建议在维护时段操作):

# PostgreSQL 17+ 支持在线启用校验和
pg_checksums --enable -D $PGDATA

# 或者离线启用(在初始化集群时)
initdb -k -D $PGDATA

7. 性能调优建议

7.1 高写入场景

在校验和计算可能成为瓶颈的高写入负载场景下,可以考虑以下组合优化:

# 如果校验和计算成为瓶颈,考虑:
# 1. 使用支持 AVX2/CRC32C 的 CPU(硬件基础)
# 2. 调整 wal_compression 为 lz4(减少需校验的 I/O 数据量,且 lz4 本身CPU开销低)

相应的 postgresql.conf 配置:

wal_compression = lz4

7.2 I/O 密集场景

对于 I/O 是主要瓶颈的场景,校验和带来的额外 CPU 开销通常影响甚微。此时,优化重点应放在 I/O 本身。

# 校验和会轻微增加 CPU 开销
# 对于 I/O 瓶颈场景影响较小

# 可以增加 shared_buffers 提升缓存命中率,间接减少I/O和校验计算
shared_buffers = 8GB

8. 故障排除

8.1 校验和不匹配

如果存储系统出现静默错误,PostgreSQL 会在读取数据页时校验失败并记录错误。

-- 如果发现校验和不匹配,会在错误日志中报告
-- 检查 postgresql 日志
SELECT pg_read_file(‘postgresql.log’);

8.2 CRC32C 硬件指令错误

在极少数情况下,如果 CPU 报告支持某指令但实际执行失败(可能是虚拟机模拟问题或 CPU bug),可能会导致非法指令错误。

# 如果 CRC32C 硬件有问题,可能报非法指令错误
# 检查 dmesg 或系统日志
dmesg | grep -i “illegal instruction”

总结

PostgreSQL 19 中针对校验和计算的硬件加速改进,是数据库内核持续性能优化与现代化的重要一步。它巧妙地将编译时优化、运行时检测和函数指针分派结合在一起,在提升数据安全性的同时,最大限度地压榨了现代 CPU 的硬件潜力。

关键要点

  1. AVX2 向量化:在 x86 平台利用 256 位宽寄存器,相比传统的 SSE2 实现,理论数据处理吞吐量翻倍。
  2. 专用指令支持:在 ARM 平台利用 CRC32 和 PMULL 扩展,在 Intel/AMD 平台利用 SSE4.2/AVX-512 的 CRC32C 指令,实现超高效的校验计算。
  3. 智能运行时选择:无需用户干预,数据库自动检测并选用当前硬件上的最优算法,平衡了性能与兼容性。
  4. 显著的性能收益:硬件加速能将校验和计算的开销降低数倍,使得“启用数据校验和”这一安全特性的代价变得更低。
  5. 对运维完全透明:DBA 无需进行额外配置或更改管理流程,升级后即可自动获益。

对实际运维的影响

  • 在已启用校验和的集群中,写入操作的 CPU 开销将进一步降低。
  • 与 PostgreSQL 后期版本提供的在线启用校验和功能配合更佳,因为启用过程中的大量计算也能被加速。
  • 现有的监控、备份和恢复流程不受任何影响。

这项改进体现了 PostgreSQL 社区对性能细节的执着追求。随着 ARM 服务器在数据中心日益普及,以及 x86 CPU 向量指令集的不断演进,此类底层硬件优化对于保持数据库竞争力至关重要。如果你正在管理高负载或对数据完整性要求极高的 PostgreSQL 集群,那么 PostgreSQL 19 的这项特性无疑值得你期待和尝试。

欢迎在 云栈社区 交流更多关于数据库性能优化的话题。




上一篇:SpaceX上市估值1.75万亿,若与特斯拉合并市值将挑战微软全球第四
下一篇:Anthropic发布Claude Mythos Preview:网络安全能力超人类专家,暂不对外提供访问
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-9 06:53 , Processed in 0.621659 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表