Skip to content

Commit 637900c

Browse files
committed
new doc
1 parent d79dda8 commit 637900c

File tree

6 files changed

+334
-0
lines changed

6 files changed

+334
-0
lines changed

201706/20170612_02.md

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
## PG多节点(quorum based), 0丢失 HA(failover,switchover)方案
2+
3+
### 作者
4+
digoal
5+
6+
### 日期
7+
2017-06-12
8+
9+
### 标签
10+
PostgreSQL , 同步复制 , quorum based
11+
12+
----
13+
14+
## 背景
15+
PostgreSQL 10加入了quorum based的同步复制功能,用户可以配置若干standby节点,并配置需要将WAL发送多少份才返回给客户端事务结束的消息。
16+
17+
```
18+
ANY num_sync ( standby_name [, ...] )
19+
```
20+
21+
原理详见
22+
23+
https://www.postgresql.org/docs/10/static/runtime-config-replication.html#runtime-config-replication-master
24+
25+
https://www.postgresql.org/docs/10/static/warm-standby.html#synchronous-replication
26+
27+
例子
28+
29+
```
30+
s1,s2,s3为standby recovery.conf配置的application_name, 即standby的唯一标示
31+
32+
下面配置表示WAL需要复制到s1,s2,s3中的任意2个副本。
33+
34+
synchronous_standby_names = 'ANY 2 (s1, s2, s3)'
35+
36+
下面配置表示WAL需要复制到任意standby的任意2个副本。
37+
synchronous_standby_names = 'ANY 2 (*)'
38+
```
39+
40+
PostgreSQL的quorum base配置比较灵活,用户可以根据地域、延迟、保护级别等需求来配置synchronous_standby_names。
41+
42+
例如
43+
44+
master有4个standby分别是s1,s2,s3,s4,s1在同机房, s2,s3在某个同城45公里以内的机房, s4在其他城市。
45+
46+
那么可以这么配置
47+
48+
```
49+
下面配置表示WAL至少在一个其他机房有一份拷贝,防止整个机房的故障。
50+
51+
synchronous_standby_names = 'ANY 2 (s1,s2,s4)'
52+
```
53+
54+
未来甚至有更灵活的配置(畅想)
55+
56+
```
57+
下面配置表示s2,s3只算一次,但是它们任意一个feedback都算数。不失可靠性的情况下,提高可用性。
58+
59+
synchronous_standby_names = 'ANY 2 (s1,[s2,s3],s4)'
60+
```
61+
62+
但是问题来了,当master出现故障时,如何failover,如何switchover呢?
63+
64+
## 架构
65+
![pic](20170612_02_pic_001.jpg)
66+
67+
1、每个数据库实例对应一个静态IP,PostgreSQL master - slave搭建好时就固定下来。
68+
69+
2、每个数据库实例对应一个角色,master或slave。
70+
71+
3、每个角色对应一个域名,当实例为master角色时,对应的域名为master,当角色为slave时,对应slave的域名。
72+
73+
4、集群初次创建好之后,将IP和域名的对应关系写入DNS。
74+
75+
5、HA管理软件,使用master域名从DNS得到静态IP,连接到静态IP,并探测master是否正常。发生异常时,进入failover流程。(后面讲failover流程)
76+
77+
6、最终应用、PROXY,通过域名连接数据库。
78+
79+
例如,这是tom lane所在的crunchydata公司开源的一个PostgreSQL proxy,用golang写的,不做SQL解析,仅仅通过SQL HINT做简单的路由,够用。效率比较高。
80+
81+
https://github.com/CrunchyData/crunchy-proxy
82+
83+
如果使用客户端连接多实例的话,可以参考如下文章
84+
85+
[《PostgreSQL 10.0 preview 功能增强 - libpq支持多主机连接(failover,LB)让数据库HA和应用配合更紧密》](../201704/20170420_01.md)
86+
87+
7、failover结束后,master角色更替,ha管理软件通知DNS修改解析信息。
88+
89+
![pic](20170612_02_pic_002.jpg)
90+
91+
8、即使应用程序、PROXY因为DNS缓存,有可能在DNS TTL失效前,短暂的连接到错误的MASTER,也没有关系,因为master配置了quorum based sync replication,所以failover结束后,(即使old master突然好了)写请求下去是不会响应的。
92+
93+
读请求则可能受到影响,可能读到old master的data。(短暂影响)
94+
95+
## 配置
96+
97+
1、初始配置
98+
99+
初始配置,将master, slave配置好,以前面的图为例,master配置复制到3个副本。
100+
101+
slave配置,连接master域名,application_name配置为master ID+slave ID。
102+
103+
例子(关键配置)
104+
105+
DNS
106+
107+
```
108+
N1 : 192.168.1.100 : master
109+
N2 : 192.168.1.101 : slave1
110+
N3 : 192.168.1.102 : slave2
111+
N4 : 192.168.1.103 : slave3
112+
N5 : 192.168.1.104 : slave4
113+
N6 : 192.168.1.105 : slave5
114+
```
115+
116+
master(N1) postgresql.conf
117+
118+
```
119+
synchronous_standby_names = 'ANY 3 (N1_N2, N1_N3, N1_N4, N1_N5, N1_N6)'
120+
```
121+
122+
slave recovery.conf
123+
124+
```
125+
N2:
126+
primary_conninfo = 'host=master port=xx user=xx password=xx application_name=N1_N2'
127+
N3:
128+
primary_conninfo = 'host=master port=xx user=xx password=xx application_name=N1_N3'
129+
N4:
130+
primary_conninfo = 'host=master port=xx user=xx password=xx application_name=N1_N4'
131+
N5:
132+
primary_conninfo = 'host=master port=xx user=xx password=xx application_name=N1_N5'
133+
N6:
134+
primary_conninfo = 'host=master port=xx user=xx password=xx application_name=N1_N6'
135+
```
136+
137+
2、DNS配置
138+
139+
TTL设置(尽量缩短客户端的dns cache时间, 例如10秒),域名映射配置。
140+
141+
```
142+
192.168.1.100 : master
143+
192.168.1.101 : slave1
144+
192.168.1.102 : slave2
145+
192.168.1.103 : slave3
146+
192.168.1.104 : slave4
147+
192.168.1.105 : slave5
148+
```
149+
150+
3、ha管理软件配置
151+
152+
可以使用一个单独的数据库来存储,或者使用文件配置。
153+
154+
3\.1 IP,数据库监听端口和ID的关系(端口必须固定、相等)
155+
156+
```
157+
N1 : 192.168.1.100 , 1921
158+
N2 : 192.168.1.101 , 1921
159+
N3 : 192.168.1.102 , 1921
160+
N4 : 192.168.1.103 , 1921
161+
N5 : 192.168.1.104 , 1921
162+
N6 : 192.168.1.105 , 1921
163+
```
164+
165+
3\.2 角色与域名的关系
166+
167+
```
168+
master : master
169+
slave : slave1
170+
slave : slave2
171+
slave : slave3
172+
slave : slave4
173+
slave : slave5
174+
```
175+
176+
3\.3 数据库用户密码
177+
178+
```
179+
user : xx
180+
181+
pwd : xx
182+
```
183+
184+
3\.4 重试间隔,重试次数。
185+
186+
3\.5 quorum数 = 3 # (取自master postgresql.conf的配置synchronous_standby_names = 'ANY 3 (N1_N2, N1_N3, N1_N4, N1_N5, N1_N6)')。
187+
188+
3\.6 总节点数(包括master) = 6
189+
190+
## failover流程
191+
1、请求解析,从master角色的域名"master",得到IP。
192+
193+
2、探测IP,数据库监听端口连通性。(异常时,注意重试次数、超时)
194+
195+
异常时,重试若干次(设置好重试间隔、重试次数),重试若干次均不可用,则进入failover流程。一旦重启期间可用,则退出failover。
196+
197+
3、探测数据库是否可以正常登录。(异常时,注意重试次数、超时)
198+
199+
异常时,重试若干次(设置好重试间隔、重试次数),重试若干次均不可用,则进入failover流程。一旦重启期间可用,则退出failover。
200+
201+
4、登录数据库,探测数据库可用性,(数据库alive检测,封装成数据库函数,返回true or false表示数据库是否可用)。(异常时,注意重试次数、超时)
202+
203+
返回false,或者返回异常,则数据库不可用。
204+
205+
异常时,重试若干次(设置好重试间隔、重试次数),重试若干次均不可用,则进入failover流程。一旦重启期间可用,则退出failover。
206+
207+
### 孤立slave
208+
孤立slave的意思是让slave进入孤立状态,能接收读请求,但是wal receiver进程不工作,也不发feedback给master,和master脱离关系。
209+
210+
孤立必须具备持久性,例如,重启后依旧处于孤立状态。
211+
212+
### 实例操作方法
213+
failover过程中,会涉及实例的操作,可以通过多种方式实现
214+
215+
1、通过数据库UDF实现数据库实例的文件、脚本等操作。
216+
217+
2、通过在数据库主机部署agent软件实现数据库实例的文件、脚本等操作。
218+
219+
推荐使用agent。
220+
221+
![pic](20170612_02_pic_003.jpg)
222+
223+
### failover
224+
1、使用配置,获取slave角色对应的域名,获取域名对应的IP。后面连接SLAVE的操作,均使用IP。
225+
226+
ip - node name 已有映射关系。
227+
228+
2、孤立若干个slave,若干是如何计算的呢?
229+
230+
公式
231+
232+
```
233+
若干 = 总节点数(包括master) - quorum数 = 6-3 = 3
234+
```
235+
236+
3、孤立若干个slave后,如果还有未孤立的slave,则继续孤立。
237+
238+
第2步的slave即使孤立不成功,也继续。(假设N2也异常)
239+
240+
4、从已孤立的SLAVE中,选择一个LSN最大的slave(我们这里假设为N3),作为new master,修改postgresql.conf。
241+
242+
```
243+
synchronous_standby_names = 'ANY 3 (N3_N1, N3_N2, N3_N4, N3_N5, N3_N6)'
244+
```
245+
246+
5、重命名new master的recovery.conf
247+
248+
```
249+
rename recovery.conf to recovery.done
250+
```
251+
252+
6、修改其他已孤立的slave - recovery.conf
253+
254+
```
255+
N4:
256+
primary_conninfo = 'host=master port=xx user=xx password=xx application_name=N3_N4'
257+
N5:
258+
primary_conninfo = 'host=master port=xx user=xx password=xx application_name=N3_N5'
259+
N6:
260+
primary_conninfo = 'host=master port=xx user=xx password=xx application_name=N3_N6'
261+
```
262+
263+
7、通知dns修改域名映射
264+
265+
```
266+
-- 对调old master和new master
267+
268+
192.168.1.102 : master
269+
192.168.1.101 : slave1
270+
192.168.1.102 : slave2
271+
192.168.1.100 : slave3
272+
192.168.1.104 : slave4
273+
192.168.1.105 : slave5
274+
```
275+
276+
8、重启孤立实例。
277+
278+
9、解除孤立。
279+
280+
10、激活new master。
281+
282+
11、结束failover流程。
283+
284+
#### 注意事项
285+
任何步骤失败,解除孤立,从头再来。
286+
287+
## 修复异常节点
288+
### 实例回退修复
289+
通常不需要修复,因为不可能出现数据丢失的情况,对于已结束的事务,new master的wal内容和old master一定是一样的。
290+
291+
https://www.postgresql.org/docs/10/static/app-pgrewind.html
292+
293+
但是一些未结束事务,可能会在new master产生更多的WAL,所以new master可能需要rewind。参考pg_rewind的方法。
294+
295+
1、配置recovery.conf
296+
297+
old master:
298+
299+
```
300+
primary_conninfo = 'host=master port=xx user=xx password=xx application_name=N3_N1'
301+
```
302+
303+
N2(异常slave):
304+
305+
```
306+
primary_conninfo = 'host=master port=xx user=xx password=xx application_name=N3_N2'
307+
```
308+
309+
2、配置old master postgresql.conf
310+
311+
```
312+
注释
313+
314+
# synchronous_standby_names = ..........
315+
```
316+
317+
重启或启动修复实例
318+
319+
## 小结
320+
1、建议使用虚拟IP,否则管理软件还需要维护一套IP变更的方法。
321+
322+
2、当节点数不为6,或者quorum数不等于3时,都可以支持,使用前面提到的孤立公式即可。
323+
324+
## 参考
325+
https://www.postgresql.org/docs/10/static/runtime-config-replication.html#runtime-config-replication-master
326+
327+
https://www.postgresql.org/docs/10/static/app-pgrewind.html
328+
329+
http://dalibo.github.io/PAF/administration.html
330+
331+
https://github.com/digoal/PostgreSQL_HA_with_primary_standby_2vip
332+

201706/20170612_02_pic_001.jpg

119 KB
Loading

201706/20170612_02_pic_002.jpg

125 KB
Loading

201706/20170612_02_pic_003.jpg

134 KB
Loading

201706/readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
### 文章列表
22
----
3+
##### 20170612_02.md [《PG多节点(quorum based), 0丢失 HA(failover,switchover)方案》](20170612_02.md)
34
##### 20170612_01.md [《PostgreSQL (varbit, roaring bitmap) VS pilosa(bitmap库)》](20170612_01.md)
45
##### 20170611_02.md [《PostgreSQL 并行写入堆表,如何保证时序线性存储 - BRIN索引优化》](20170611_02.md)
56
##### 20170611_01.md [《为什么PostgreSQL是"最先进的开源数据库"》](20170611_01.md)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ digoal's|PostgreSQL|文章|归类
2828

2929
### 未归类文档如下
3030
----
31+
##### 201706/20170612_02.md [《PG多节点(quorum based), 0丢失 HA(failover,switchover)方案》](201706/20170612_02.md)
3132
##### 201706/20170612_01.md [《PostgreSQL (varbit, roaring bitmap) VS pilosa(bitmap库)》](201706/20170612_01.md)
3233
##### 201706/20170611_02.md [《PostgreSQL 并行写入堆表,如何保证时序线性存储 - BRIN索引优化》](201706/20170611_02.md)
3334
##### 201706/20170611_01.md [《为什么PostgreSQL是"最先进的开源数据库"》](201706/20170611_01.md)

0 commit comments

Comments
 (0)