在容器中加速 PostgreSQL

发布: (2026年1月20日 GMT+8 07:45)
8 min read
原文: Dev.to

I’m ready to translate the article for you, but I don’t see the text you’d like translated—only the source link. Could you please provide the article content (or the portion you want translated) so I can convert it into Simplified Chinese while preserving the formatting and code blocks?

问题

在一台磁盘较慢的旧 CI 机器上运行测试套件时,发现 PostgreSQL 成为主要瓶颈。每次测试运行耗时 超过 1 小时

罪魁祸首是什么?测试执行了大量的数据库操作,并使用 TRUNCATE 命令在每个测试后清理数据。由于磁盘 I/O 缓慢,PostgreSQL 大部分时间都在 将数据同步到磁盘——在 临时 CI 环境 中,这些操作完全没有必要,因为数据持久化并不重要。

在测试执行期间运行 top,显示了关键线索:

242503 postgres  20   0  184592  49420  39944 R  81.7   0.3   0:15.66 postgres: postgres api_test 10.89.5.6(43216) TRUNCATE TABLE
  • PostgreSQL 为一次 TRUNCATE 表消耗了 81.7 % CPU
  • 单次 TRUNCATE 运行时间 超过 15 秒

在磁盘慢的机器上,PostgreSQL 正在等待内核确认数据已写入物理存储——即使该操作仅需在测试之间清空表。

Source:

修复方案 – 三个简单的 PostgreSQL 调整

services:
  postgres:
    image: postgres:16.11-alpine
    environment:
      POSTGRES_INITDB_ARGS: "--nosync"
      POSTGRES_SHARED_BUFFERS: 256MB
    tmpfs:
      - /var/lib/postgresql/data:size=1g

1. --nosync 标志

  • POSTGRES_INITDB_ARGS: "--nosync" 告诉 PostgreSQL 在数据库初始化期间 跳过 fsync() 调用
  • 在 CI 环境中我们不关心持久性——如果容器崩溃,我们只会重新启动。
  • 这可以消除导致数据库搭建变慢的昂贵磁盘同步操作。

2. 增大 shared_buffers

  • POSTGRES_SHARED_BUFFERS: 256MB(相较默认约 128 MB)为 PostgreSQL 提供了更多内存用于缓存经常访问的数据。
  • 在测试频繁访问相同表时非常有帮助。

3. 将数据目录挂载到 tmpfs

tmpfs:
  - /var/lib/postgresql/data:size=1g
  • tmpfs 为 PostgreSQL 的数据目录创建了一个 基于内存的文件系统
  • 所有数据库操作都在 RAM 中完成,显著加速:
    • TRUNCATE 操作 – 在测试之间实现瞬时清理
    • 索引更新 – 无需磁盘寻道
    • WAL(预写日志)写入 – 纯内存操作
    • 检查点操作 – 不必等待磁盘刷新

1 GB 的大小限制对大多数测试数据库来说已经相当宽裕;可根据数据量自行调整。

结果

指标之前之后改进
总测试运行时间~60 min~10 min快 6 倍
单个测试(例如 API::FilamentSupplierAssortmentsTest#test_create_validation_negative_price25.5 s0.47 s≈ 快 55 倍
平均每个测试时间(24 个测试)27 s0.45 s≈ 快 60 倍

示例输出

优化前 tmpfs

API::FilamentSupplierAssortmentsTest#test_create_validation_negative_price = 25.536s
API::FilamentSupplierAssortmentsTest#test_list_with_a_single_assortment = 29.996s
API::FilamentSupplierAssortmentsTest#test_list_missing_token = 25.952s

优化后 tmpfs

API::FilamentSupplierAssortmentsTest#test_list_as_uber_curator = 0.474s
API::FilamentSupplierAssortmentsTest#test_list_as_assistant = 0.466s
API::FilamentSupplierAssortmentsTest#test_for_pressman_without_filament_supplier = 0.420s

为什么这样有效

  • TRUNCATE – PostgreSQL 正在将空表状态同步到磁盘。
  • 数据库初始化 – 每次 CI 运行都会重新创建数据库。
  • INSERT – 创建测试夹具(用户、角色,…)。
  • 事务提交 – 每个测试在事务中运行,事务会回滚。
  • 频繁的小写入 – 在测试执行期间发生。

在慢速磁盘上,即使是创建测试用户或截断表这样简单的操作也会 级别而不是 毫秒 级别。上面的 top 输出显示单个 TRUNCATE 在等待磁盘 I/O 时消耗了 81.7 % CPU。将其乘以数百个测试,就会导致 CI 运行耗时数小时。

实用指南

  • Production – 保持启用 fsync 并使用保守的持久性设置。
  • CI – 数据是 短暂的;速度比持久性更重要。
  • Profile your pipeline – 我们发现磁盘 I/O 是瓶颈,而不是 CPU 或内存。
  • tmpfs 是终极的磁盘‑I/O 消除器——所有内容都在 RAM 中,意味着没有磁盘瓶颈。
  • Memorytmpfs 会占用 RAM;确保你的 CI runner 有足够的内存(≥ 1 GB 用于数据库)。

完整的 PostgreSQL 服务配置

services:
  postgres:
    image: postgres:16.11-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: dbpgpassword
      POSTGRES_DB: api_test
      POSTGRES_INITDB_ARGS: "--nosync"
      POSTGRES_SHARED_BUFFERS: 256MB
    ports:
      - 5432
    tmpfs:
      - /var/lib/postgresql/data:size=1g

注意: tmpfs 字段在 Woodpecker CI 的后端官方支持(参见 pipeline/backend/types/step.go)。如果遇到模式验证警告,可能是文档过时——该功能如预期工作。

要点

  • 小的配置调整可以对 CI 速度产生 巨大的影响
  • 对于临时测试数据库,优化 速度而非耐久性 是合适的。
  • 使用 tmpfs 可以消除磁盘 I/O 瓶颈,将数小时的测试运行时间缩短至 几分钟

祝测试愉快! 🚀

CI + tmpfs:简单、有效、无需代码更改

CI 通过原生 Docker 支持让这变得轻而易举——只需添加一个 tmpfs: 字段即可完成。
如果你使用 GitHub Actions、GitLab CI 或其他平台,可能需要使用 docker run--tmpfs 标志或自定义 runner 配置等变通办法。

TL;DR: 我试过了。tmpfs 仍然更快 更简单。

能通过激进的 PostgreSQL 调优匹配 tmpfs 吗?

在看到 tmpfs 带来的显著提升后,我产生了以下疑问:

“我们是否可以通过激进地调优 PostgreSQL 设置来实现类似的性能?”

这在 tmpfs 不可用或 RAM 受限的环境中会非常有用。

实验:禁用所有持久性特性

services:
  postgres:
    command:
      - postgres
      - -c
      - fsync=off                 # Skip forced disk syncs
      - -c
      - synchronous_commit=off    # Async WAL writes
      - -c
      - wal_level=minimal         # Minimal WAL overhead
      - -c
      - full_page_writes=off      # Less WAL volume
      - -c
      - autovacuum=off            # No background vacuum
      - -c
      - max_wal_size=1GB          # Fewer checkpoints
      - -c
      - shared_buffers=256MB      # More memory cache

即使使用了所有这些激进设置,tmpfs 仍然更快。

功能/方面基于磁盘(即使 fsync=off基于 tmpfs
文件系统开销 – ext4/xfs 元数据操作
磁盘寻道 – 机械延迟/受限 IOPS
内核缓冲区缓存 – 内存拷贝
Docker overlay2 – 存储驱动开销
配置复杂度(7+ 设置)
纯 RAM 操作 – 无物理存储
零磁盘 I/O
最大性能 – 没有比 RAM 更快的

奖励:其他 PostgreSQL CI 优化可考虑

如果你仍在寻找更多加速方法,请尝试以下调整。

禁用查询日志

command:
  - postgres
  - -c
  - log_statement=none                # Don't log any statements
  - -c
  - log_min_duration_statement=-1    # Don't log slow queries

其他调整

  • fsync=offpostgresql.conf – 禁用同步写入(类似于 --nosync)。仅在临时、非持久化环境中使用。
  • 增加 work_mem – 为每个查询提供更多内存,可加速测试中的复杂操作。
Back to Blog

相关文章

阅读更多 »

将 Pulumi 操作加速至最高 20 倍

今天我们推出了一项改进,可将操作速度提升至最高 20 倍。在每一次操作以及操作中的每一步,pulumi 都会保存一个 snap……