Skip to content

Commit 3d5baf3

Browse files
committed
apply delay
1 parent 96bba27 commit 3d5baf3

File tree

10 files changed

+837
-0
lines changed

10 files changed

+837
-0
lines changed

201506/20150619_01.md

Lines changed: 530 additions & 0 deletions
Large diffs are not rendered by default.

201506/readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
### 文章列表
22
----
33
##### 20150626_01.md [《PostgreSQL earth distance module》](20150626_01.md)
4+
##### 20150619_01.md [《online DDL (or NOWAIT DDL) in PostgreSQL》](20150619_01.md)
45
##### 20150615_01.md [《PostgreSQL trigger/rule based replication configure, DISABLE/ENABLE [ REPLICA | ALWAYS ] TRIGGER | RULE》](20150615_01.md)
56
##### 20150601_01.md [《PostgreSQL 数据库安全指南》](20150601_01.md)

201702/20170227_01.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,10 @@ pg_hba.conf
961961
host replication postgres 0.0.0.0/0 md5
962962
```
963963

964+
8\. 目前PostgreSQL逻辑复制不支持源、目标相同,但是"schema.表名"不完全相同的情形。 也不支持字段类型有不一致的源目标复制。 所以如果你想对一张大表修改表结构,或者处理数据,可以考虑用触发器的方式(俗称的ONLINE DDL)。
965+
966+
9\. 需要注意长事务(例如某些写事务,不关),这个事务之后的WAL在发布端不会被清除。
967+
964968
## 参考
965969
https://www.postgresql.org/docs/devel/static/logical-replication.html
966970

201703/20170301_01.md

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
## PostgreSQL 备库apply延迟原理分析与诊断
2+
3+
### 作者
4+
digoal
5+
6+
### 日期
7+
2016-03-01
8+
9+
### 标签
10+
PostgreSQL , 物理流复制 , IO不对称
11+
12+
----
13+
14+
## 背景
15+
开车的同学都喜欢一马平川,最好是车道很多,车很少,开起来爽。
16+
17+
![pic](20170301_01_pic_001.jpg)
18+
19+
大家想象一下,同样的车速,6车道每秒可以通过6辆车,而1车道每秒就只能通过1辆车。
20+
21+
好了,我们回到IO层面,我们在使用fio测试块设备的IO能力时,可以选择多少个线程进行压测,实际可以理解为开多少车道的意思。
22+
23+
只要没到通道或者设备本身的极限,当然开的车道(并发)越多,测出来的IO数据越好看。比如单线程可以做到每秒处理1万次请求,而开8个并发,可能处理能达到8万次请求。
24+
25+
这个可以理解之后,我们来看看PostgreSQL的物理复制,为了保证数据一致性,备库在APPLY时,目前只有一个startup进程,对于partial block从REDO中读取出块的变化,并从数据文件读出对应的完整块,在shared buffer中完成合并,最后bg writer会将shared buffer的dirty page write(异步写)到数据文件,对于FPW,则直接写入SHARED BUFFER,后期bg write会负责处理dirty page。
26+
27+
虽然PostgreSQL备库已经使用shared buffer减少了写操作(比如单个数据块的多次变更,只要对应的dirty page没有从shared buffer evict出去,就不需要多次读IO;写IO也可以降低(比如OS层IO合并,或者bgwrite调度机制也可以降低写IO)),但是这些技术在主库也存在,除非备库设置的shared buffer更大,那么备库的写IO也许能降低。
28+
29+
另一方面,备库在恢复非FPW块时,需要从数据文件读取数据块,进行合并,这个动作实际上会产生离散读,在bgwrite将数据块写出shared buffer时产生离散写。
30+
31+
小结一下,主库是多进程离散读写转换为单进程顺序写,而备库单进程顺序读转换为单进程离散读写。
32+
33+
主节点
34+
35+
下面指非分组提交、采用同步提交、开启FSYNC时的流程。
36+
37+
1\. 数据库后台进程wal writer,负责将wal buffer的REDO数据,批量写入(fsync) wal文件。
38+
39+
2\. 数据库后台进程bg writer,负责将shared buffer的dirty page数据(根据page lsn判断,该页WAL已fsync),写入(write) datafile。OS调度,将PAGE CACHE持久化写入数据文件(datafile)。
40+
41+
3\. 用户进程(S),将需要用到的数据页,读入shared buffer,当shared buffer不够用时,会evict一些page,和bg writer操作类似。
42+
43+
4\. 用户进程(S),非提交事务时,将产生的变更写入wal buffer,提交事务时,会触发XLogFlush(src/backend/access/transam/xact.c),将wal buffer写入(fsync)wal文件。
44+
45+
![pic](20170301_01_pic_002.jpg)
46+
47+
备节点
48+
49+
1\. wal receiver进程,负责将收到的wal写入wal buffer。
50+
51+
2\. wal writer进程,负责将wal buffer写入(fsync)wal文件。
52+
53+
3\. startup进程,从WAL文件读取日志,同时从数据文件读取对应数据块,合并(apply redo)后,写入shared buffer。
54+
55+
4\. bgwriter进程,将shared buffer的dirty page数据(根据page lsn判断,该页WAL已fsync),写入(write) datafile。OS调度,将PAGE CACHE持久化写入数据文件(datafile)。
56+
57+
![pic](20170301_01_pic_003.jpg)
58+
59+
对比主节点和备节点的操作,可以观察到一些不对等的地方。
60+
61+
1\. 写(fsync)WAL文件时,主节点有用户进程、wal writer并发的情况出现。而备节点只有wal writer单一进程。
62+
63+
2\. 写(write)数据文件时,主节点有用户进程、bg writer并发的情况出现。而备节点只有bg writer单一进程。
64+
65+
3\. 写shared buffer时,主节点有用户进程并发读写。而备节点只有startup单一进程。
66+
67+
由于以上不对称的情况(主库多数操作是多车道,备库多数操作是单车道),当主库产生的XLOG量非常庞大,或者包含一些非常耗时的操作(例如(大量离散IO,大量系统调用()))时,备库可能会出现延时。
68+
69+
## 哪些情况可能导致备库apply延迟
70+
通常来说备库接收日志不会有延迟,只要网络带宽比主库产生REDO的速度快。
71+
72+
延迟通常发生在apply阶段。前面分析了主库多数操作是多车道,备库多数操作是单车道,成为备库apply延迟的主要原因。
73+
74+
1\. 恢复时,需要消耗大量CPU时,例如开启了数据文件checksum时,会额外消耗startup进程的cpu。
75+
76+
2\. 主库频繁的离散IO操作,SEEK等。例如大量的索引变更,例如大量的索引VACUUM,例如大量的VACUUM操作。
77+
78+
3\. 频繁或者大量的系统调用,例如大批量删除对象,如drop schema。
79+
80+
[《PostgreSQL DaaS设计注意 - schema与database的抉择》](../201610/20161012_01.md)
81+
82+
4\. 冲突,例如备库开放用户查询,某些查询操作和replay操作冲突时,可能短暂的影响恢复。
83+
84+
## 如何避免apply延迟
85+
1\. checksum,除非你要防物理篡改,否则通常不需要开启checksum。checksum只是帮助你了解块是否损坏,并不能起到修复作用。(redo的checksum是默认强制打开的,但是数据文件的checksum可选)
86+
87+
2\. 删除没有必要使用的索引。
88+
89+
3\. 垃圾回收的调度,根据业务进行调整,默认是20%,越低越频繁,越频繁,垃圾越少。但是越频繁可能导致产生的VACUUM DIRTY PAGE会增加。可以选择一个较为折中的值,例如5%。
90+
91+
4\. 检查点拉长,可以减少FULL PAGE的量。FULL PAGE是指每次检查点后,第一次被更改的页,需要将这个页写入WAL日志,当数据库CRASH后,可以保证数据的完整性。但是由于FULL PAGE的引入,日志量会增加。
92+
93+
拉长检查点的间距,可以减少FULL PAGE。对于COW文件系统例如(zfs, btrfs),不需要开启FULL PAGE WRITE。
94+
95+
5\. 加大备库shared buffer,可以减少write datafile。
96+
97+
6\. 关闭IO时间的跟踪,可以提高IO操作效率。
98+
99+
7\. 备库使用IOPS能力更强、IO延迟更低的机器(例如NVME的SSD),从而抹平不对称的情况,注意,不建议使用RAID 5机器。
100+
101+
8\. 增加单个进程可打开的文件数,可以减少文件开启和关闭,特别是数据库的文件数很多时,可以有效的减少系统调用的时间耗费。
102+
103+
配置例子
104+
105+
```
106+
checkpoint_segments=1024
107+
track_io_timing=off
108+
wal_buffers=512MB
109+
synchronous_commit=off
110+
wal_writer_delay = 10ms
111+
max_files_per_process = 65536
112+
autovacuum_vacuum_scale_factor = 0.05
113+
```
114+
115+
## case
116+
我们可以根据以上分析,模拟一个场景,让备库处于apply延迟的状态,你可以使用perf , pstack , strace等工具分析是否符合我前面从原理或代码层面的分析。
117+
118+
假设主备已经搭建好了。
119+
120+
在主库创建几张表,这几张表涉及大量的索引。
121+
122+
```
123+
create table test(
124+
id int8 primary key,
125+
info text,
126+
crt_time timestamp,
127+
c0 serial8 unique check(c0>0) ,
128+
c1 serial8 unique check(c1>0) ,
129+
c2 serial8 unique check(c2>0) ,
130+
c3 serial8 unique check(c3>0) ,
131+
c4 serial8 unique check(c4>0) ,
132+
c5 serial8 unique check(c5>0) ,
133+
c6 serial8 unique check(c6>0) ,
134+
c7 serial8 unique check(c7>0) ,
135+
c8 serial8 unique check(c8>0) ,
136+
c9 serial8 unique check(c9>0) ,
137+
c10 serial8 unique check(c10>0) ,
138+
c11 serial8 unique check(c11>0) ,
139+
c12 serial8 unique check(c12>0) ,
140+
c13 serial8 unique check(c13>0) ,
141+
c14 serial8 unique check(c14>0) ,
142+
c15 serial8 unique check(c15>0) ,
143+
c16 serial8 unique check(c16>0) ,
144+
c17 serial8 unique check(c17>0) ,
145+
c18 serial8 unique check(c18>0) ,
146+
c19 serial8 unique check(c19>0) ,
147+
c20 serial8 unique check(c20>0) ,
148+
c21 serial8 unique check(c21>0) ,
149+
c22 serial8 unique check(c22>0) ,
150+
c23 serial8 unique check(c23>0) ,
151+
c24 serial8 unique check(c24>0) ,
152+
c25 serial8 unique check(c25>0) ,
153+
c26 serial8 unique check(c26>0) ,
154+
c27 serial8 unique check(c27>0) ,
155+
c28 serial8 unique check(c28>0) ,
156+
c29 serial8 unique check(c29>0) ,
157+
c30 serial8 unique check(c30>0) ,
158+
c31 serial8 unique check(c31>0) ,
159+
c32 serial8 unique check(c32>0) ,
160+
c33 serial8 unique check(c33>0) ,
161+
c34 serial8 unique check(c34>0) ,
162+
c35 serial8 unique check(c35>0) ,
163+
c36 serial8 unique check(c36>0) ,
164+
c37 serial8 unique check(c37>0) ,
165+
c38 serial8 unique check(c38>0) ,
166+
c39 serial8 unique check(c39>0) ,
167+
c40 serial8 unique check(c40>0) ,
168+
c41 serial8 unique check(c41>0) ,
169+
c42 serial8 unique check(c42>0) ,
170+
c43 serial8 unique check(c43>0) ,
171+
c44 serial8 unique check(c44>0) ,
172+
c45 serial8 unique check(c45>0) ,
173+
c46 serial8 unique check(c46>0) ,
174+
c47 serial8 unique check(c47>0) ,
175+
c48 serial8 unique check(c48>0) ,
176+
c49 serial8 unique check(c49>0) ,
177+
c50 serial8 unique check(c50>0) ,
178+
c51 serial8 unique check(c51>0) ,
179+
c52 serial8 unique check(c52>0) ,
180+
c53 serial8 unique check(c53>0) ,
181+
c54 serial8 unique check(c54>0) ,
182+
c55 serial8 unique check(c55>0) ,
183+
c56 serial8 unique check(c56>0) ,
184+
c57 serial8 unique check(c57>0) ,
185+
c58 serial8 unique check(c58>0) ,
186+
c59 serial8 unique check(c59>0) ,
187+
c60 serial8 unique check(c60>0) ,
188+
c61 serial8 unique check(c61>0) ,
189+
c62 serial8 unique check(c62>0) ,
190+
c63 serial8 unique check(c63>0) ,
191+
c64 serial8 unique check(c64>0) ,
192+
c65 serial8 unique check(c65>0) ,
193+
c66 serial8 unique check(c66>0) ,
194+
c67 serial8 unique check(c67>0) ,
195+
c68 serial8 unique check(c68>0) ,
196+
c69 serial8 unique check(c69>0) ,
197+
c70 serial8 unique check(c70>0) ,
198+
c71 serial8 unique check(c71>0) ,
199+
c72 serial8 unique check(c72>0) ,
200+
c73 serial8 unique check(c73>0) ,
201+
c74 serial8 unique check(c74>0) ,
202+
c75 serial8 unique check(c75>0) ,
203+
c76 serial8 unique check(c76>0) ,
204+
c77 serial8 unique check(c77>0) ,
205+
c78 serial8 unique check(c78>0) ,
206+
c79 serial8 unique check(c79>0) ,
207+
c80 serial8 unique check(c80>0) ,
208+
c81 serial8 unique check(c81>0) ,
209+
c82 serial8 unique check(c82>0) ,
210+
c83 serial8 unique check(c83>0) ,
211+
c84 serial8 unique check(c84>0) ,
212+
c85 serial8 unique check(c85>0) ,
213+
c86 serial8 unique check(c86>0) ,
214+
c87 serial8 unique check(c87>0) ,
215+
c88 serial8 unique check(c88>0) ,
216+
c89 serial8 unique check(c89>0) ,
217+
c90 serial8 unique check(c90>0) ,
218+
c91 serial8 unique check(c91>0) ,
219+
c92 serial8 unique check(c92>0) ,
220+
c93 serial8 unique check(c93>0) ,
221+
c94 serial8 unique check(c94>0) ,
222+
c95 serial8 unique check(c95>0) ,
223+
c96 serial8 unique check(c96>0) ,
224+
c97 serial8 unique check(c97>0) ,
225+
c98 serial8 unique check(c98>0) ,
226+
c99 serial8 unique check(c99>0)
227+
);
228+
229+
230+
create or replace function create_test(int,int) returns void as $$
231+
declare
232+
begin
233+
for i in $1..$2 loop
234+
execute 'create table test'||i||' (like test including all)';
235+
end loop;
236+
end;
237+
$$ language plpgsql strict;
238+
239+
240+
select create_test(1,16);
241+
```
242+
243+
创建了17张表,涉及1818个索引。
244+
245+
创建测试脚本
246+
247+
```
248+
vi test.sql
249+
250+
\set id random(1,100000000)
251+
insert into test values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
252+
insert into test1 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
253+
insert into test2 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
254+
insert into test3 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
255+
insert into test4 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
256+
insert into test5 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
257+
insert into test6 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
258+
insert into test7 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
259+
insert into test8 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
260+
insert into test9 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
261+
insert into test10 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
262+
insert into test11 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
263+
insert into test12 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
264+
insert into test13 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
265+
insert into test14 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
266+
insert into test15 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
267+
insert into test16 values (:id,'test',now()) on conflict(id) do update set info=excluded.info, crt_time=excluded.crt_time;
268+
```
269+
270+
压测
271+
272+
```
273+
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 64 -j 64 -T 1000
274+
```
275+
276+
观察延迟
277+
278+
```
279+
select pg_size_pretty(pg_xlog_location_diff(pg_current_xlog_insert_location(),sent_location)) sent_delay,
280+
pg_size_pretty(pg_xlog_location_diff(pg_current_xlog_insert_location(),replay_location)) replay_delay,
281+
* from pg_stat_replication ;
282+
```
283+
284+
分析
285+
286+
```
287+
pstack startup进程
288+
289+
perf record -avg
290+
291+
perf report --stdio
292+
```
293+
294+
## 参考
295+
[《PostgreSQL DaaS设计注意 - schema与database的抉择》](../201610/20161012_01.md)
296+

201703/20170301_01_pic_001.jpg

120 KB
Loading

201703/20170301_01_pic_002.jpg

55 KB
Loading

201703/20170301_01_pic_003.jpg

68.2 KB
Loading

201703/readme.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### 文章列表
2+
----
3+
##### 20170301_01.md [《PostgreSQL 备库apply延迟原理分析与诊断》](20170301_01.md)

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ http://pan.baidu.com/s/1pKVCgHX , 如果连接失效请通知我, 谢谢
1414
### digoal,德哥的PostgreSQL私房菜
1515
##### 163的老文章入口 : [进入](old_blogs_from_163/README.md)
1616
----
17+
##### 201703/20170301_01.md [《PostgreSQL 备库apply延迟原理分析与诊断》](201703/20170301_01.md)
18+
----
1719
##### 201702/20170228_01.md [《PostgreSQL Oracle 兼容性之 - SQL OUTLINE插件sr_plan (保存、篡改、固定 执行计划)》](201702/20170228_01.md)
1820
##### 201702/20170227_01.md [《PostgreSQL 10.0 逻辑复制原理与最佳实践》](201702/20170227_01.md)
1921
##### 201702/20170225_01.md [《PostgreSQL 向量化执行插件(瓦片式实现) 10x提速OLAP》](201702/20170225_01.md)
@@ -372,6 +374,7 @@ http://pan.baidu.com/s/1pKVCgHX , 如果连接失效请通知我, 谢谢
372374
##### 201507/20150703_01.md [《PostgreSQL Oracle 兼容性之 - orafce (包、函数、DUAL)》](201507/20150703_01.md)
373375
----
374376
##### 201506/20150626_01.md [《PostgreSQL earth distance module》](201506/20150626_01.md)
377+
##### 201506/20150619_01.md [《online DDL (or NOWAIT DDL) in PostgreSQL》](201506/20150619_01.md)
375378
##### 201506/20150615_01.md [《PostgreSQL trigger/rule based replication configure, DISABLE/ENABLE [ REPLICA | ALWAYS ] TRIGGER | RULE》](201506/20150615_01.md)
376379
##### 201506/20150601_01.md [《PostgreSQL 数据库安全指南》](201506/20150601_01.md)
377380
----
676 KB
Binary file not shown.

0 commit comments

Comments
 (0)