暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL中的WAL:4.设置和调整

飞象数据 2022-01-18
3539

我们了解了缓冲区缓存的结构,并得出了结论,如果所有缓存内容由于故障而丢失,则需要预写日志(WAL)来恢复。由于不定期会执行检查点,所需WAL文件的大小和恢复时间会受到限制。

在之前的文章中,我们已经回顾了许多与WAL相关的重要设置。在本文(本系列的最后一篇)中,我们将讨论尚未解决的WAL设置问题:WAL级别及其用途,以及预写日志记录的可靠性和性能。

WAL日志级别

WAL的主要任务是确保故障后的恢复。但是,一旦我们必须维护日志,我们还可以通过向日志中添加更多信息使其适应其他任务。有几个日志记录级别。由wal_level
参数指定级别,下一个级别包括上一级别的所有内容以及新内容。

Minimal 级别

wal_level=minimal
是最低级别,确保仅在发生故障后恢复。为节省空间,不记录与批量数据处理相关的操作(如 CREATE TABLE AS SELECT
CREATE INDEX
)。相反,这些数据会立即写入磁盘,新对象会添加到系统目录中,并在事务提交时可见。如果在执行操作时发生故障,已写入的数据将保持不可见,并且不会违反一致性规则。如果操作完成后出现故障,所需的一切都已在磁盘上,不需要记录。

让我们看一看。首先,我们将设置必要的级别(为此,我们还需要更改另一个参数-max_wal_senders
)。

=> ALTER SYSTEM SET wal_level = minimal;
=> ALTER SYSTEM SET max_wal_senders = 0;
student$ sudo pg_ctlcluster 11 main restart

请注意,级别的更改需要重新启动服务。

让我们记住当前的WAL的位置:

=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
0/353927BC
(1 row)

现在,让我们执行一个表的创建(CREATE TABLE AS SELECT
),并再次记住WAL的位置。在这种情况下,SELECT
操作符检索的数据量根本不重要,因此一行就足够了。

=> CREATE TABLE wallevel AS
SELECT 1 AS n;
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
0/353A7DFC
(1 row)

让我们使用pg_waldump
程序查看WAL记录。

postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/353927BC -e 0/353A7DFC

当然,一些细节在不同的返回中可能会有所不同,但在本例中,我们得到以下信息。Heap2
管理器的记录与vacuuming有关,这里它位于系统目录表的页面vacuum中(系统对象很容易用肉眼通过rel
中的一个小数字来区分):

rmgr: Heap2       len (rec/tot):     59/  7587, tx:          0, lsn: 0/353927BC, prev 0/35392788, desc: CLEAN remxid 101126, blkref #0: rel 1663/16386/1247 blk 8 FPW

获取要创建的表的下一个OID的记录如下:

rmgr: XLOG        len (rec/tot):     30/    30, tx:          0, lsn: 0/35394574, prev 0/353927BC, desc: NEXTOID 82295

这是表的纯创建:

rmgr: Storage     len (rec/tot):     42/    42, tx:          0, lsn: 0/35394594, prev 0/35394574, desc: CREATE base/16386/74103

但是将数据插入到表中不会被WAL记录。多条记录跟随行插入到不同的表和索引中-通过这种方式,PostgreSQL将所创建表上的信息写入系统目录(以简写形式提供):

rmgr: Heap        len (rec/tot):    203/   203, tx:     101127, lsn: 0/353945C0, prev 0/35394594, desc: INSERT off 71, blkref #0: rel 1663/16386/1247 blk 8
rmgr: Btree len (rec/tot): 53/ 685, tx: 101127, lsn: 0/3539468C, prev 0/353945C0, desc: INSERT_LEAF off 37, blkref #0: rel 1663/16386/2703 blk 2 FPW
...
rmgr: Btree len (rec/tot): 53/ 2393, tx: 101127, lsn: 0/353A747C, prev 0/353A6788, desc: INSERT_LEAF off 10, blkref #0: rel 1664/0/1233 blk 1 FPW

最后是事务提交:

rmgr: Transaction len (rec/tot):     34/    34, tx:     101127, lsn: 0/353A7DD8, prev 0/353A747C, desc: COMMIT 2019-07-23 18:59:34.923124 MSK

Replica 级别

当我们从备份中恢复PostgreSQL实例时,我们从文件系统的某些状态开始,通过回放归档的WAL记录,逐渐将数据带到恢复的目标点。这类记录的数量可能相当大(例如,几天的记录),也就是说,恢复期将跨越多个检查点,而不是一个检查点。因此,很明显,minimal
级别是不够的——如果一个操作没有记录,我们将不知道是否需要重做它。要支持从备份还原,WAL日志必须记录所有操作。

复制也是如此:未记录的所有内容都不会发送到副本,也不会重放。希望在副本上运行查询会使情况更加复杂。

首先,我们需要关于主上产生的独占咨询锁的信息,因为它们可能会与副本上的查询冲突。这些锁将被写入wal,然后startup进程将它们应用到副本上。

其次,我们需要创建数据快照,要做到这一点,正如我们所记得的,需要有关正在执行的事务的信息。对于副本,不仅意味着本地事务,还意味着主的事务。提供此信息的唯一方法是不时记录WAL日志(每15秒一次)。

确保从备份恢复和物理复制可能性的WAL级别由WAL_level=replica
的值设置。(在9.6版之前,有两个独立的级别可用—archive
hot_standby
—但后来它们合并了。)

从PostgreSQL 10开始使用这个级别作为默认级别(而之前是minimal
级别)。因此,让我们将参数恢复为默认值:

=> ALTER SYSTEM RESET wal_level;
=> ALTER SYSTEM RESET max_wal_senders;
student$ sudo pg_ctlcluster 11 main restart

删除表并重新执行与上次完全相同的步骤顺序:

=> DROP TABLE wallevel;
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
0/353AF21C
(1 row)
=> CREATE TABLE wallevel AS
SELECT 1 AS n;
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
0/353BE51C
(1 row)

现在让我们检查一下WAL记录。

postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/353AF21C -e 0/353BE51C

Vacuuming、获取OID、创建表并在系统目录中注册-与之前相同:

rmgr: Heap2       len (rec/tot):     58/    58, tx:          0, lsn: 0/353AF21C, prev 0/353AF044, desc: CLEAN remxid 101128, blkref #0: rel 1663/16386/1247 blk 8
rmgr: XLOG len (rec/tot): 30/ 30, tx: 0, lsn: 0/353AF258, prev 0/353AF21C, desc: NEXTOID 82298
rmgr: Storage len (rec/tot): 42/ 42, tx: 0, lsn: 0/353AF278, prev 0/353AF258, desc: CREATE base/16386/74106
rmgr: Heap len (rec/tot): 203/ 203, tx: 101129, lsn: 0/353AF2A4, prev 0/353AF278, desc: INSERT off 73, blkref #0: rel 1663/16386/1247 blk 8
rmgr: Btree len (rec/tot): 53/ 717, tx: 101129, lsn: 0/353AF370, prev 0/353AF2A4, …
rmgr: Btree len (rec/tot): 53/ 2413, tx: 101129, lsn: 0/353BD954, prev 0/353BCC44, desc: INSERT_LEAF off 10, blkref #0: rel 1664/0/1233 blk 1 FPW

这是新的东西。与Standby
管理器相关的排他锁记录-这里是事务ID上的锁:

rmgr: Standby     len (rec/tot):     42/    42, tx:     101129, lsn: 0/353BE2D8, prev 0/353BD954, desc: LOCK xid 101129 db 16386 rel 74106

这是表中的行插入记录(将文件号rel
CREATE
记录中的文件号rel
进行比较):

rmgr: Heap        len (rec/tot):     59/    59, tx:     101129, lsn: 0/353BE304, prev 0/353BE2D8, desc: INSERT+INIT off 1, blkref #0: rel 1663/16386/74106 blk 0

这是提交记录:

rmgr: Transaction len (rec/tot):    421/   421, tx:     101129, lsn: 0/353BE340, prev 0/353BE304, desc: COMMIT 2019-07-23 18:59:37.870333 MSK; inval msgs: catcache 74 catcache 73 catcache 74 catcache 73 catcache 50 catcache 49 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 snapshot 2608 relcache 74106 snapshot 1214

还有一条记录与Standby
管理器相关,该记录不时出现,通知此时正在执行的事务,与已完成的事务无关:

rmgr: Standby     len (rec/tot):     50/    50, tx:          0, lsn: 0/353BE4E8, prev 0/353BE340, desc: RUNNING_XACTS nextXid 101130 latestCompletedXid 101129 oldestRunningXid 101130

Logical 级别

最后,最后一个级别由wal_level=logical
的值指定,额外再提供逻辑解码和逻辑复制。必须作为发布端打开它。

从WAL记录的角度来看,此级别实际上与replica
相同:添加与复制源相关的记录,以及应用程序可以添加到WAL的任意逻辑记录。但逻辑解码主要取决于正在执行的事务的信息,因为需要创建数据快照来跟踪系统目录的更改。

写日志的可靠性

显然,日志记录技术必须是可靠的,并确保在任何情况下都能恢复(当然,与数据存储介质的损坏无关)。许多事情会影响可靠性,其中我们将讨论缓存、数据损坏和写入的原子性。

缓存

多个缓存阻碍了数据进入非易失性存储器(如硬盘盘片)。

如果程序(本例中为PostgreSQL)要求操作系统(OS)在磁盘上写入内容,则操作系统会将数据传输到其RAM缓存。写入实际上是异步进行的,具体取决于操作系统的I/O调度程序的设置。

当操作系统决定写入数据时,它们会进入存储器(硬盘)的缓存。存储器的电子设备也可以推迟写入,例如,通过将更高效的数据分组在一起写入。如果使用RAID控制器,则会在操作系统和磁盘之间增加一个缓存级别。

因此,如果不采取特殊措施,数据何时以可靠的方式保存是绝对不清楚的。通常情况下,这没有什么区别,但在一些关键领域,PostgreSQL必须确保数据写入具有适当的可靠性。这主要是日志记录(如果WAL记录未到达磁盘,它将和其余缓存内容一起丢失)和检查点(我们必须确保脏页写入磁盘)。但也有其他情况,例如在minimal
级别执行不记录日志的操作等等。

操作系统提供了确保数据立即写入非易失性存储的功能。有几个选项,但可以把它们减少到两个主要选项:要么在写入后执行同步调用(fsync
fdatasync
),要么在打开文件(或写入文件)后设置一个特殊标志,指示需要同步,甚至需要绕过操作系统缓存直接写入。

至于WAL,pg_test_fsync
程序允许我们选择最适合特定操作系统和特定文件系统的方法,该方法在wal_sync_method
参数中指定。普通文件使用fsync
进行同步。

一个微妙的点是,在选择方法时,我们需要考虑硬件特性。例如:如果使用使用battery-backup unit(BBU)的控制器,我们没有理由不使用控制器的缓存,因为BBU可以在断电时保存数据。

在任何情况下,同步都是非常昂贵的,而且执行同步的次数最多是绝对必要的。

通常,您可以关闭同步(fsync
参数负责此操作),但在这种情况下,您必须忘记存储的可靠性。通过关闭fsync
,您同意数据可能会在任何时候不可逆转地丢失。可能,该参数唯一合理的使用情况是,当数据可以从不同的源轻松恢复时,通过关闭fsync
获得性能的临时提高。

日志损坏

硬件不完善,数据在存储器中损坏,等等。一些错误是在硬件级别处理的,但有些则不是。

为了快速检测问题,WAL记录中提供了校验和。

数据页也可以通过校验和进行保护。之前,这只能在集群初始化时完成,但在PostgreSQL 12中,可以通过 pg_checksums
程序打开和关闭校验和(但仅当服务关闭时,而不是“运行中”)。

在生产环境中,无论计算和验证校验和的开销如何,校验和都必须启用。这降低了未及时检测到损坏的可能性。

它减少了概率,但没有消除。首先,只有在页面被访问时才验证校验和;因此,在损坏进入所有备份之前,可能无法检测到它。正是由于这个原因,pg_probackup
在数据备份期间验证所有集群页面的校验和。其次,用零填充的页面被认为是正确的,因此,如果文件系统错误地“取消”了文件,这可以避免检测。第三,校验和只保护数据的主分支。其他分支和其他文件(例如,事务状态XACT)根本不受保护。

让我们看看它是如何工作的。首先,我们确保打开校验和(请注意,在类似Debian系统上安装的软件包中,默认情况下并非如此):

=> SHOW data_checksums;
data_checksums
----------------
on
(1 row)

data_checksums
参数是只读的。

这是我们表格所在的文件:

=> SELECT pg_relation_filepath('wallevel');
pg_relation_filepath
----------------------
base/16386/24890
(1 row)

让我们关闭服务器并用zero在页面上更改几个字节,例如:从标题中删除最后一条WAL记录的LSN。

student$ sudo pg_ctlcluster 11 main stop
postgres$ dd if=/dev/zero of=/var/lib/postgresql/11/main/base/16386/24890 oflag=dsync conv=notrunc bs=1 count=8
8+0 records in
8+0 records out
8 bytes copied, 0,0083022 s, 1,0 kB/s

通常,不需要关闭服务器。将页面刷新到磁盘并从缓存中逐出就足够了(否则,服务将继续在缓存中处理页面)。但这种情况更难再现。

现在我们启动服务器并尝试读取表。

student$ sudo pg_ctlcluster 11 main start
=> SELECT * FROM wallevel;
WARNING: page verification failed, calculated checksum 23222 but expected 50884
ERROR: invalid page in block 0 of relation base/16386/24890

但是,如果无法从备份中恢复数据,我们该怎么办?ignore_checksum_failure
参数允许尝试读取表,当然,这会有获取损坏数据的风险。

=> SET ignore_checksum_failure = on;
=> SELECT * FROM wallevel;
WARNING: page verification failed, calculated checksum 23222 but expected 50884
n
---
1
(1 row)

当然,在这种情况下一切都很好,因为我们只伤害了头,而不是纯数据。

还有一点需要注意。打开校验和时,将WAL记录提示位(我们在前面讨论过),因为对任何位(即使是不重要的位)的更改都会导致校验和的更改。当校验和关闭时,wal_log_hints
参数负责wal日志提示位。

对提示位的更改总是记录为FPI(完整页面图像),这几乎增加了WAL的大小。在这种情况下,使用wal_compression
参数打开FPI的压缩是有意义的(该参数是在9.5版中添加的)。稍后我们将查看具体数字。

写入的原子性

最后,还有一个写入的原子性问题。数据库页占用的空间不小于8KB(可能是16或32KB),在较低的级别上,写入是以块的形式完成的,块的大小通常较小(通常是512字节或4KB)。因此,在断电的情况下,可以部分写入数据页。很明显,在恢复过程中,对这样的页面应用常规的WAL记录是没有意义的。

为了防止出现这种情况,PostgreSQL允许WAL在检查点周期开始后第一次更改页面时记录完整的页面映像(提示位更改时也会记录相同的映像)。full_page_write
参数控制这一点,默认情况下它处于打开状态。

如果恢复过程在WAL中遇到FPI,它会无条件地将映像写入磁盘(无需LSN检查):FPI更可靠,因为它与每个WAL记录一样,受校验和保护。正是这张可靠的正确映像,常规的WAL记录被应用于此。

虽然在PostgreSQL中,FPI不包括可用空间(我们在前面讨论了块结构),但FPI大大增加了WAL记录的生成量。如前所述,通过压缩FPI(使用wal_compression参数),可以改善这种情况。

为了深入了解如何更改WAL的大小,让我们使用pgbench
程序进行一个简单的实验。执行初始化:

student$ pgbench -i test
dropping old tables...
creating tables...
generating data...
100000 of 100000 tuples (100%) done (elapsed 0.15 s, remaining 0.00 s)
vacuuming...
creating primary keys...
done.

full_page_writes
参数已打开:

=> SHOW full_page_writes;
full_page_writes
------------------
on
(1 row)

让我们执行一个检查点并立即运行一个测试30秒。

=> CHECKPOINT;
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
0/38E04A08
(1 row)
student$ pgbench -T 30 test
starting vacuum...end.
transaction type: TPC-B (sort of)
scaling factor: 1
query mode: simple
number of clients: 1
number of threads: 1
duration: 30 s
number of transactions actually processed: 26851
latency average = 1.117 ms
tps = 895.006720 (including connections establishing)
tps = 895.095229 (excluding connections establishing)
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
0/3A69C478
(1 row)

获取WAL记录的大小:

=> SELECT pg_size_pretty('0/3A69C478'::pg_lsn - '0/38E04A08'::pg_lsn);
pg_size_pretty
----------------
25 MB
(1 row)

现在,让我们关闭full_page_writes

=> ALTER SYSTEM SET full_page_writes = off;
=> SELECT pg_reload_conf();

我们重复这个实验。

=> CHECKPOINT;
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
0/3A69C530
(1 row)
student$ pgbench -T 30 test
starting vacuum...end.
transaction type: TPC-B (sort of)
scaling factor: 1
query mode: simple
number of clients: 1
number of threads: 1
duration: 30 s
number of transactions actually processed: 27234
latency average = 1.102 ms
tps = 907.783080 (including connections establishing)
tps = 907.895326 (excluding connections establishing)
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
0/3BE87658
(1 row)

获得WAL记录的大小:

=> SELECT pg_size_pretty('0/3BE87658'::pg_lsn - '0/3A69C530'::pg_lsn);
pg_size_pretty
----------------
24 MB
(1 row)

是的,尺寸减小了,但不明显。

问题是集群是用数据页上的校验和初始化的,因此,当提示位发生变化时,必须记录FPI。这些数据(在上述情况下)约占总数的一半,您可以通过查看统计数据来确定:

postgres$ /usr/lib/postgresql/11/bin/pg_waldump --stats -p /var/lib/postgresql/11/main/pg_wal -s 0/3A69C530 -e 0/3BE87658
Type N (%) Record size (%) FPI size (%)
---- - --- ----------- --- -------- ---
XLOG 1721 ( 1,03) 84329 ( 0,77) 13916104 (100,00)
Transaction 27235 ( 16,32) 926070 ( 8,46) 0 ( 0,00)
Storage 1 ( 0,00) 42 ( 0,00) 0 ( 0,00)
CLOG 1 ( 0,00) 30 ( 0,00) 0 ( 0,00)
Standby 4 ( 0,00) 240 ( 0,00) 0 ( 0,00)
Heap2 27522 ( 16,49) 1726352 ( 15,76) 0 ( 0,00)
Heap 109691 ( 65,71) 8169121 ( 74,59) 0 ( 0,00)
Btree 756 ( 0,45) 45380 ( 0,41) 0 ( 0,00)
-------- -------- --------
Total 166931 10951564 [44,04%] 13916104 [55,96%]

删除零行以使表更紧凑。注意汇总行(总计),并将完整图像的大小(FPI大小)与正常记录的大小(记录大小)进行比较。

仅当文件系统和硬件本身确保写入原子性时,才能关闭full_page_writes
参数。但是,正如我们所看到的,它没有多大意义(只要打开校验和)。

现在让我们看看压缩有什么帮助。

=> ALTER SYSTEM SET full_page_writes = on;
=> ALTER SYSTEM SET wal_compression = on;
=> SELECT pg_reload_conf();

重复同样的实验。

=> CHECKPOINT;
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
0/3BE87710
(1 row)
student$ pgbench -T 30 test
starting vacuum...end.
transaction type: TPC-B (sort of)
scaling factor: 1
query mode: simple
number of clients: 1
number of threads: 1
duration: 30 s
number of transactions actually processed: 26833
latency average = 1.118 ms
tps = 894.405027 (including connections establishing)
tps = 894.516845 (excluding connections establishing)
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
0/3CBD3EA8
(1 row)

获取WAL记录的大小:

=> SELECT pg_size_pretty('0/3CBD3EA8'::pg_lsn - '0/3BE87710'::pg_lsn);
pg_size_pretty
----------------
13 MB
(1 row)

结论:如果有许多FPI(由于校验和或full_page_writes
,几乎总是这样),尽管压缩会使用到cpu,但使用压缩还是有意义的。

性能

在正常工作期间,WAL文件按照一个接一个的顺序被连续写入。由于没有随机访问,即使是HDD磁盘也能正常工作。但是,这种负载与访问数据文件时的负载有很大不同。

因此,将WAL存储在单独的物理磁盘(或磁盘阵列)上通常是有益的。必须创建指向相应目录的符号链接,而不是$PGDATA/pg_wal
目录。

在某些情况下,不仅需要写入WAL文件,还需要读取WAL文件。第一个是发生故障后恢复的明确案例。第二个不那么琐碎。如果使用流复制,WAL记录仍在主的OS缓冲区中时,副本延迟接收WAL记录。在这种情况下,walsender
进程必须从磁盘读取必要的数据。进行复制时,我们将对此进行更详细的讨论。

WAL以以下两种方式之一写入:

  • 同步 — 在事务提交时,直到该事务的所有WAL记录都进入磁盘后,才能继续工作。

  • 异步 — 事务立即完成,并且WAL在后台写入。

缺省情况下,synchronous_commit
参数处于打开状态,用于设置同步模式。

由于同步与实际的(即较慢的)输入/输出有关,因此尽可能少地进行同步是有好处的。为此,完成事务并写入WAL的后端进程会稍作暂停,这由commit_delay
参数定义。但这仅在系统具有不少于commit_siblings
活动事务的情况下才会发生。此行为依赖于这样的期望:在等待时间内,一些事务将完成,并且可以一次性同步它们。这类似于你摁住电梯门的方式,以便有人有时间进入。

默认情况下,commit_siblings = 5
commit_delay = 0
,因此实际上没有等待。仅对于执行大量OLTP事务的系统,更改commit_delay
的值才有意义。

然后,将WAL的一部分刷新到所需的LSN位置(如果在等待时间内添加了新记录,则刷新更多)。刷新之后,事务被视为完成。

同步写入确保了持久性(ACID字母缩写中的字母D):如果事务已提交,其所有WAL记录都已在磁盘上,并且不会丢失。但一个缺点是同步写入会增加响应时间(COMMIT命令在同步结束之前不会返回控制),并降低系统性能。

您可以通过设置sync_commit = off
(或local
)来使写入变成异步。

当异步写入时,WAL记录将由wal writer进程刷新,该进程将交替工作并等待(等待时间由wal_writer_delay
参数指定,默认值为200 ms)。

该进程被唤醒后,它将检查自上次以来是否出现了完全填充的WAL页面。如果确实出现了,则该进程将忽略当前未填充到最后的页面,仅写入完全填充的页面。(但是,并非一次完成:写入到达高速缓存末尾时会停止,而下一次从高速缓存起始点开始。)

但是,如果没有任何页面被填满,则该进程将刷新当前的WAL页面(未填充到末尾)—要不然,唤醒后干嘛呢?

该算法旨在尽可能避免多次同步同一页面,这对于大量更新至关重要。

异步写入比同步写入更有效,因为更改的提交不等待WAL页面的写入。但是可靠性降低了:如果在提交和失败之间经过的时间少于3×wal_writer_delay
单位,则提交失败的数据可能会在失败的情况下丢失(使用默认设置,该时间稍长于半秒)。

在效率和可靠性之间并非一个简单的选择,要取决于系统管理员。

请注意:与关闭同步(fsync = off
)不同,异步模式不会使恢复变得不可能。万一发生故障,系统将恢复一致状态,但也许某些最后的事务将不存在。

可以为单独的事务设置synchronous_commit
参数。这样可以通过仅牺牲某些事务的可靠性来提高性能。例如金融交易必须同步进行,而聊天消息却可能使用异步。

实际上,两种模式可以协同工作。即使使用同步提交,长事务的WAL记录也将被异步写入,以释放WAL缓冲区。而且,如果在从缓冲区高速缓存中刷新页面期间,似乎还没有在磁盘上存储相应的WAL记录,那么它将以同步模式立即被刷新。

为了深入了解异步提交的好处,让我们尝试在此模式下重复pgbench
测试。

=> ALTER SYSTEM SET synchronous_commit = off;
=> SELECT pg_reload_conf();
student$ pgbench -T 30 test
starting vacuum...end.
transaction type: TPC-B (sort of)
scaling factor: 1
query mode: simple
number of clients: 1
number of threads: 1
duration: 30 s
number of transactions actually processed: 45439
latency average = 0.660 ms
tps = 1514.561710 (including connections establishing)
tps = 1514.710558 (excluding connections establishing)

使用同步提交,我们每秒大约获得900个事务(tps),使用异步提交则获得1500 tps。不用说,在实际系统中的实际负载下,所占的比例会有所不同,但是很明显,对于短期交易而言,其影响可能是相当大的。

关于WAL日志的系列文章到此结束。


文章转载自飞象数据,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论