使用 ZFS 和 AWS EBS 运行 PostgreSQL
本指南介绍如何使用 ZFS 文件系统运行 PostgreSQL。如果您还需要安装 ZFS,请参阅 在 Ubuntu 上安装 ZFS.
概述
使用 PostgreSQL 与 ZFS(而不是 ext4/xfs)的主要原因是数据压缩。使用 LZ4,您可以实现 2-3 倍的压缩率,这意味着您需要写入和读取的数据量减少 2-3 倍。ZSTD 提供了更好的压缩,但代价是 CPU 使用率略高。
第二个原因是自适应替换缓存 (ARC)。ARC 是一种页面替换算法,其特性略优于 Linux 页面缓存。由于它缓存压缩块,因此您也可以在相同的 RAM 中容纳更多数据。
基本 ZFS 设置
首先,您需要为 PostgreSQL 创建一个单独的池
zpool create -o autoexpand=on pg /dev/nvme1n1
以及 2 个用于 PostgreSQL 数据和预写日志 (WAL) 的数据集
# Move PostgreSQL files to a temp location.
mv /var/lib/postgresql/14/main/pg_wal /tmp/pg_wal
mv /var/lib/postgresql /tmp/postgresql
# Create datasets.
zfs create pg/data -o mountpoint=/var/lib/postgresql
zfs create pg/wal-14 -o mountpoint=/var/lib/postgresql/14/main/pg_wal
# Move PostgreSQL files back.
cp -r /tmp/postgresql/* /var/lib/postgresql
cp -r /tmp/pg_wal/* /var/lib/postgresql/14/main/pg_wal
# Fix permissions.
chmod 0750 /var/lib/postgresql
chmod 0750 /var/lib/postgresql/14/main/pg_wal
ZFS 配置
考虑从以下 ZFS 配置开始,并在您了解更多信息后对其进行调整
# same as default
zfs set recordsize=128k pg
# enable lz4 compression
zfs set compression=lz4 pg
# or zstd compression
#zfs set compression=zstd-3 pg
# disable access time updates
zfs set atime=off pg
# enable improved extended attributes
zfs set xattr=sa pg
# same as default
zfs set logbias=latency pg
# reduce amount of metadata (may improve random writes)
zfs set redundant_metadata=most pg
ZFS ARC 大小
默认情况下,ZFS 使用 50% 的 RAM 用于自适应替换缓存 (ARC)。您可以考虑将 ARC 增加到 RAM 的 70-80%,但请确保为 PostgreSQL shared_buffers
留下足够的内存
# set ARC cache to 1GB
echo 1073741824 >> /sys/module/zfs/parameters/zfs_arc_max
要使 ARC 大小更改在 Linux 重启后持久化,请创建 /etc/modprobe.d/zfs.conf
options zfs zfs_arc_max=1073741824
ZFS recordsize
recordsize
是 ZFS 将写入和读取的最大数据块大小。ZFS 会单独压缩每个块,并且对于更大的块,压缩效果更好。使用默认的 recordsize=128k
,如果需要更多 TPS(每秒事务数),请将其降低到 32-64k。
- 更大的
recordsize
意味着更好的压缩,如果您的查询读取/写入大量数据(数十兆字节),则会提高性能。 - 更小的
recordsize
意味着更高的 TPS。
将 recordsize=8k
设置为与 PostgreSQL 块大小匹配会降低压缩效率,而压缩效率是使用 ZFS 的主要原因之一。虽然 recordsize=8k
提高了 pgbench 报告的平均事务速率,但良好的 pgbench 结果并不代表良好的生产性能。在降低 recordsize
之前,请测量您的查询的性能。
ARC 和 shared_buffers
由于 ARC 缓存压缩块,因此优先使用它而不是 PostgreSQL shared_buffers
来缓存热点数据。但是,将 shared_buffers
设置得太小会对写入速度产生负面影响。因此,请考虑降低 shared_buffers
,只要您的写入速度没有受到太大影响,并将剩余的 RAM 留给 ARC。
TOAST 压缩
为了避免对数据进行两次压缩,您可以通过将列存储设置为 EXTERNAL
来禁用 PostgreSQL TOAST 压缩。但这并没有太大区别
- LZ4 非常快。
- LZ4 和 ZSTD 都有特殊的逻辑来跳过不可压缩(或已压缩)的数据部分。
对齐偏移
对于 Amazon Elastic Block Store 和其他云存储,请使用默认的 ashift
值,因为 EBS 卷不是单个物理设备,而是跨越多个分布式设备的逻辑卷。
但是,如果您知道驱动器的扇区大小,那么值得正确配置 ashift
zpool create -o ashift=12 -o autoexpand=on pg /dev/nvme1n1
ashift | 扇区大小 |
---|---|
9 | 512 字节 |
10 | 1 KB |
11 | 2 KB |
12 | 4 KB |
13 | 8 KB |
14 | 16 KB |
PostgreSQL 全页写入
由于 ZFS 始终写入完整块,因此您可以通过 full_page_writes = off
设置在 PostgreSQL 中禁用全页写入。
PostgreSQL 块大小和 WAL 大小
默认的 PostgreSQL 块大小为 8k,它与 ZFS 记录大小(默认情况下为 128k)不匹配。结果是,虽然 PostgreSQL 以 8k 块写入数据,但 ZFS 必须使用 128k 记录(称为写入放大)。您可以通过将 PostgreSQL 块大小增加到 32k,并将 WAL 块大小增加到 64k 来改善这种情况。这需要重新编译 PostgreSQL 并重新初始化数据库。
- 更大的
blocksize
会显着提高读取大量数据(数十兆字节)的查询的性能。这种效果并非特定于 ZFS,您也可以在其他文件系统中使用更大的块大小。 - 更小的
blocksize
意味着更高的每秒事务速率。
logbias
使用 logbias=latency
。
来自 @mercenary_sysadmin 的引用
如果您的工作负载是大量大块写入,那么没有 SLOG 的 logbias=throughput 可能会提高性能,而这种工作负载通常不会在性能方面遇到太多问题。
没有 SLOG 且小块写入的 Logbias=throughput 将导致最可怕的碎片,这将对您在初始写入时以及稍后从金属中重新读取该数据时造成不利影响。
来自 @taratarabobara 的另一个引用
logbias=throughput 会将每个块都碎片化。
通常,ZFS 会以接近顺序的方式写入数据和元数据,因此它们可以稍后通过单个读取 IOP 读取。间接同步 (logbias=throughput) 会导致元数据和数据间隔开,数据与数据间隔开。碎片化随之而来,以及非常多的池 IO 合并。
如果您想亲眼看看,请在另一个窗口中观察 "zpool iostat -r 1" 的同时执行 "zfs send dataset >/dev/null"。您将看到许多无法与其他任何内容聚合的 4K 读取。这是间接同步的代价,您在每次读取时都要为此付出代价。
它只应在非常特殊的情况下使用。
ZFS 快照
如果您要使用 ZFS 快照,请为 PostgreSQL WAL 文件创建一个单独的数据集。这样,主数据集的快照就会更小。不要忘记单独备份 WAL 文件,以便您可以使用 时间点恢复.
但通常,使用 pgbackrest 将备份存储在 S3 上更容易且更便宜。另一个流行的选择是 EBS 快照。