开发者学堂课程【PostgreSQL 快速入门:13PostgreSQL 表级复制-Londiste3级联复制以及 provider 的切换】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/16/detail/72
13 PostgreSQL 表级复制-Londiste3级联复制以及 provider 的切换
内容介绍:
一、londiste 3 级联复制
二、测试环境
三、级联复制测试
四、角色切换
一、londiste 3 级联复制
1、下面的例子讲解 londiste 3 级联复制。首先,讲解术语。set 指的是一组节点,在单个 queue 上的一组节点。这一组节点可以做 serverche over 之类的操作,保持同样的 batch boundaries and tick_ids。下面叫一组节点,在一个队列上复制的一些集合,是对节点组的定义。
Set::
Group of nodes that distribute a single queue. In addition to
copying events around, they also keep same batch boundaries and
tick_ids.
Node 是指单个节点,比如说在整个队列里面,set 里面有很多个 node,Node 指的是往队列里面添加节点的信息。
node::
A databaze that participates in cascaded copy of a queue.
在整个级联复制里面,还包括 Provider,Provider 指的是往队列里面写数据,是一个数据的提供者。
provider::
Node that Providers queue data to another.
Subscriber 在队列里面取数据,相当于是 Provider往队列里面放数据,subscriber 从队列里面取数据。在整个级联复制里,Provider 和 Subscriber 都可以有很多个。
subzcriber::
Node that receives queue data fron another.
2、在级联的队列当中,在上一节当中,只使用了创建根节点(root)和叶子节点(left),在级联复制当中,会有分支节点(branch)的概念。分支节点指的是它既是一个 Provider,又是一个 Subscriber,分支节点还有可能转化为根节点,就是分支节点和根节点之间可以相互切换。但是 left 节点,就是叶子节点,不能提升为根节点,因为它里面没有消息对列,没有 queue。但是分支节点含有 queue,有完整队列的功能,同时它又是一个消费者,从根节点的队列里取数据,在 branch 里面消费,消费的同时又有自己的队列,就是消费的同时还会往队列里面写数据,其它节点还可以从这个队列中取数据。叶子节点没有队列,只是从别的节点当中取数据,把数据合并到自己节点中,所以叫叶子节点。
root::
Place where queue data is generated.
branch::
* Carries full contents of the queue.
* (Londiste) May subscribe to all/sone/none of the tables
* (Londiste) Can be provider for initial copy only if subscribes to table
leaf::
Date-only node. Events are replayed, but no queue, thus cannot
be provider to other nodes
Nodes where sets from partitions are merged together are also
tagged 'leaf', because
in per-partition set it cannot be provider to other nodes.
二、测试环境
1、测试环境用到五个节点,
skytools3.0.3
root:172.16.3.33:1919:digoal PostgreSQL 9.2betal
branch: 172.16.3.39:1921:digoal PostgreSQL 9.1.3
branch: 172.16.3.40:1921:digoal PostgreSQL 9.1.3
branch: 172.16.3.150:1919:digoal PostgreSQL 9.1.3
branch: 172.16.3.176:1921:digoal PostgreSQL 9.1.3
在的环境中只用了三个节点,分别是3.39、3.150、3.33。3.150做为根节点。另外,再创建一个分支节点和叶子节点,在这个测试环境中,上面五个都是分支节点。
2、已经有了一个根节点 src_digoal_01,有两个叶子节点,分别是 dst1_digoal_01 和 dst2_digoal_02,再往里面添加两个分支节点。分支节点创建在3.39和3.33上,3.33创建一个库,叫做digoal_03,复制 create role digoal nosuperuser nocreatedb nocreaterole noinherit login encrypted password'digoal’;,粘贴到 3.33上。然后再创建一个库,也叫做 digoal_03。复制 create database digoal encoding 'UTF8’ template templateO owner digoal,把 digoal_03 这个库做为分支节点。复制 create schema digoal authorization digoal:,创建 schema,schema 用了 digoal_01 schema。
根节点 schema 是 digoal_01,叶子节点是 digoal_02,修改表结构的时候出现了一些问题,必须手工的去修复。
3、在所有节点上创建表
set search_path=digoal_01,把当前的 sql 路径改成 digoal_01,否则会创建在publish(优先)。在 digoal_01下,这些表就创建好了。
4、初始化数据不用创建,因为已经有一个根节点了。需要创建函数,
一会儿要做角色切换,只添加 branch。
alter role digoal_03 set searcher_path = digoal_01,public;
再连到 digoal_03 下的 digoal_03,就能默认收到路径,否则会有问题。
三、级联复制测试
1、queue 已经创建好了,根节点也有了,接下来要创建 destination3,就是创建3.33这台的配置文件。londiste 3/,vi dst3_digoal_03.in i,把下面配置写上去。
vi /home/pg92/skylondist/etc/digoal_33.ini
[londiste3]
job_name = digoal_33
db =host=172.16.3.33 port=1919 user=londiste dbname=digoal
password=londiste
queue name =replika
logfile=/hone/pg92/skylondist/log/%(job_name)s.log
pidfile =/home/pg92/skylondist/pid/%(job_name)s. pid
pgq_autoconmit=1
pgq_lazy_fetch =0
parallel_copiez = 16
配置可以沿用里面的配置。直接拷贝
修改成下图形式
2、创建 branch 节点,复制
londiste3 -v /home/pg92/=kylondist/etc/digoal_39.ini
creale-branch digoal_39 “host=172.16.3.39 port=1921
user=londiste
dbname=digosl password=londiste“--provider=“host=
172.16.3.33 port=1919 user=londiste dbname=digoal
password=londiste
Provider 还是150,端口是1921,数据库名是 digoal_01,branch 端口是5432,名字用 dst3_digoal_03,配置文件放在 /home/pg93/londiste3 中。
报错,因为需要安装3.33,从3.39拷贝,Python-2.7.6.tgz 也要安装。
复制 ./configure --enable-shared。还要安装 Python-dev 和 git autoconf automake asciidoc xmlto libtool,复制 git autoconf automake asciidoc xmlto libtool,使用样本安装,安装到3.33。复制 asciidoc_8.6.9-1.el5.rf.noarch.rpm,
把 Python 加到 3.39中,复制 /opt/python2.7.6/lib,粘贴到3.33中。
3、接下来安装 psycopg2,复制 psycopg2-2.4.5。
复制 git clone git://github.com/markokr/skytools.git,克隆下来,克隆完以后进行安装。复制git submodule init 和 git submodule update,主要目的是生成 SO文件。下图地方需要改,
到底安装在哪里?su - postgres,which pg_config,config,指定 python2.7.6/bin/python,安装到 opt/skytoolsdev,检查目录是否一致,这样 SO 文件就有了。再去150添加 branch 节点就不会有问题。等下图所示地方执行完成
安装完成,去确定一下。
SO 文件已经安装好了。
4、在去150中添加节点,现在添加就成功了。有一个报错
public connect string points to different database than local connect string
,在添加 branch 节点的时候,连接到的原库是 Provider digoal_01,但是下图光标所示地方配置应该是 digoal_03,
和配置文件不同,把它改为 digoal_03。密码认证失败,是因为下图所示地方写错了,
把 londiste 改为 postgres,现在就是正确的了。
一定要记住这个配置,配置文件一定要写成一致的,库名要一致,注意 Provider 不要写错。现在看 status,就能看到有 branch 节点。
当前还没有做同步,没有同步到一个时间点。dst_digoal_03.in i tables,现在还没有加表。
5、下面要做的是启动worker 进程,刚刚只是把 branch 节点创建出来。通过下面配置启动,
hondiste3-d /home/pg92/skylondist/etc/digoal 33.ini worker
londiste3-d/home/pg92/skylondist/etc/digoal_39.ini worker
londiste3-d /home/pg92/skylondist/etc/digoal_40.ini worker
londiste3-d/home/pg92/skylondist/etc/digoal_150.ini worker
londiste3-d/home/pg92/skylondist/etc/digoal_176.ini worker
londiste3 -d/home/pg93/londiste3/ dst_digoal_03.in I worker
现在三个 worker 已经同步,查看它的状态,如图
状态是一致的。在 branch 节点没有加表,所以可以看到一些 misting 的一些状态。现在要把表加进去,londiste3 -v /home/pg93/londiste3/dst3_digoal_03.ini add-table
digoal_01.user_info digoal_01.user_session digoal
_01.user_login_rec digoal_01.user_logout_rec
,添加的时候报的错,
相当于连到3.150上 select local,执行 from londiste.local_add
_table 添加,注意缺少组件。每个表都要有主键才能添加进来,
user_info 表没有主键,login、logout 表都有主键,user_session 表没有主键,所以这两张表没办法添加。
6、需要创建主键,复制
alter table user_info add constraint pk_user_info primary key
(userid):
alter table user_session add constraint pk_user_session
primary key (userid):
添加上去就可以创建,下图所示,已经添加完成。
下面看 destination 3 的 tables。
还没有做同步,把 seq 加进去,复制 user_login_rec_id_seq,注意是 schema 下的 seq。把 user_logout_rec_id_seq 也添加进去,现在就有 seq 了。
tables 正在拷贝,seq 已经同步了。
当前可以看到数据量正在变化,incopy,从3.150连过来,3.150上连了四个,运行在拷贝。现在数据已经完全拷贝好了。
Count(*)from digoal_01.user_info,数据就已经过来了。Compare 也能看到 dst3_digoal_03 数据。src_digoal_01 compare,数据库一致了。
四、角色切换
1、现在有一个根节点,两个叶节点,一个分支节点。可以做一些角色切换的动作,branch 和 branch 、branch 和 root 的互相切换。目前 branch 只加了一个,可以在加第二个 branch。创建一个叫 digoal_04 的库,先创建角色,create role digoal,角色创不创建都可以。直接创建 create database digoal_04 owner digoal_01,这样连到 digoal_04 里面直接创建 schema,不需要修改 search path。它们两个 schema 名一样的情况下,默认 search path,默认先搜索跟用户名同名的 schema 名,现在不需要再修改 search path。创建表,注意PK要建上去,在建立第一个 digoal_03 忘记把主键带上,复制alter table user_info add constraint pk_user_info primary key (userid):
alter table user_session add constraint pk_user_session primary key (userid):
函数也要加上,这样做完角色切换之后,可以演示一下。
2、现在环境已经部署好了,只需要添加配置文件,cp dst_digoal_03.in i dst 的配置文件,把它拷贝到dst 4。这个库叫 digoal_04,job 名也改成4,dbname 连到第四个库。
然后创建 branch,create branch 跟 Provider 一样,相当于这个 Provider 是 root 节点。
把 Provider 的改成3.33也可以,因为3.33上有branch 节点,数据库名 digoal_03。
这个时候去看 status 结构,相当于 dst3_digoal_03 branch 下面又挂了一个 dst4_digoal_04 branch。
因为刚刚创建它的时候,Provider 写的是 branch 的 Provider, dst3_digoal_03 branch 后面还可以加 branch,因为它是一个分支节点,它们两个还可以交换,还可以做一些压力测试。
3、做着压力测试做切换的时候,一定要达到同步之后,现在 branch 还没有添加表,所以先启动 worker 进程,再往里面添加表。branch 做切换的话,里面订阅的内容要一样,否则没有办法做切换。现在要加表和序列,先加序列。序列加好了,如图
再加表,表加好了如图
Destination tables 已经有了,tables 还没有做同步,tables 一会会做同步,同步完以后就可以做角色切换了。
4、角色切换,一个是 branch 和 branch 的切换,还有一个是 branch 和 root 节点的互换。现在有一个状态是这样的,
出现一个问题,在主节点是有 c1 列的,但是在两个 branch 节点都没有 c1,需要手工的修复它,把列加上去。
都加上去之后,事件成功。等待它完全同步之后做角色切换,并且会加锁,不允许做变更。角色切换相当于固化,不允许做变更了,branch 和 root 同步之后,把 branch 切换成 root,root 才可以切换成 branch,要确保这两个节点数据一定要一致。
5、总共添加的表和序列一样,两个 branch 和 root 节点一样。
把 digoal_40 的 Provider 从 digoal_33 改成 digoal_150,就是把 dst3_digoal_03 provider 变成 root。变法很简单,change Provider,londiste3/home/pg93/londiste3/dst4_digoal_04.in i change-Provider --Provider=src_digoal_04,当前就在做 provider 的操作。
dst4_digoal_04 原来相当于在 dst3_digoal_03 中注册,现在注册去掉了。dst4_digoal_04 provider 已经挂到 src_digoal_01 里去了。从 src 取队列数据,原来是从 dst3_digoal_03 branch 中取,这是它的输出,会有一个暂停的动作,标记为当前不消费,本来是一个消费者,现在不消费。再把location 注册到 src 中,再从 dst3_digoal_03 节点中把原来注册的 dst4_digoal_04 去掉。相当于分四步,第一步暂停,注册,resumed 表示恢复,第四个从原来的节点中取消注册,现在结构变成如图所示
6、当前的状态全部同步完成,tick 已经达到一致。Compare 的时候能看到数据一致。
一个是 Provider 的互换,就是从一个 Provider 换成另一个 Provider,包括叶子节点,也可以修改它的 Provider。dst3 是一个branch 节点,把destination1 的 Provider 改到 destination3 上,destination1 已经挂到destination3 上了。
原来是挂在 root 节点下。
7、切换 root 角色,使用 takeover 切换 root 节点,把 digoal_39 切换成 root 节点,在切换前,在 digoal_39 上执行一条更新 user_info 操作的 sql,看看能否成功。比如说现在一个 branch 节点,dst3_digoal_03 branch 把它切换成 root 节点。首先连到 destination 3上,当前是一个 branch 节点,就是分支节点。分支节点直接对表做 update,表会不允许做这个操作,TRUNCATE 也是不允许的。如果要删除一台记录,begin,delete from user_info where userid=1,它会告诉你违反了一个约束,约束来自于 pgq.insert_event 函数。因为当前是 branch 节点,所以不允许做这个操作。
所有的 DNL ,包括 TRUNCATE 只能在 root 节点上操作。 使用 takeover,把 destination 03 改为 root,复制 londiste3-v /home/pg93/londiste3/ dst3_digoal_03.ini worker src_digoal_01。相当于把 destination 3 升级为 root,原来的 root 降级为 branch,一个降级,一个升级的过程。所有的 worker 进程都要允许做这个操作,才可以进行下去。现在destination3 变成了 root,
原来挂在 src 下面的节点还是在 src 下,只不过 src 跟 destination3 做了角色切换。
8、现在到 src 中就不能做 DML 语句,begin,delete from user_info where userid=1,这个操作就变得不允许了。
原来是一个根节点,现在是一个 branch 节点,从这能够看出 src 已经变成了一个 branch 节点,这是 takeover 的操作。现在 update 在已经变成 root 节点上的节点就可以操作了,begin,update user_info set mod_time =now〈〉 where userid=1,就可以执行这个操作了。查 select mod_time from user_info where userid=1,有没有同步到其它的节点上。比如说04节点,04这个节点上数据还没有同步,mod_time 还没有复制过来,是一个空的状态,这个复制是默认一分钟的切片。ticker_period 是1秒,一秒一个切片。
先看一下状态,
destinations4 相当于是从 src 里复制,先连在 src上,复制 select mod_time from user_info where userid=1,下图所示地方还没复制过来。
9、compar 之前会做一次同步,这里出现一个错误,laggin too much,和原库产生了延迟。
destinations4 和destination3 中间隔了一道 src,看 src 同步的时候,会不会把数据从根节点转换成 destination3 的根节点,把数据同步过来。su - pg93,londiste3 /home/pg93/londiste3/
dst4_digoal_04.in i tables,状态没有变化。
去看一下日志,检查问题出在哪里,日志配在 log 目录里,看一下 src 的日志,看里面有没有报错之类的。
现在没有报错,最后一次报出来在10:17:54。destination3 是主节点,连到主节点看一下,su - postgres,select *from,从3.150连到 digoal_03 上面,在3.33上面开一个 pgage,复制
\setrandom userid 1 200000
select f_user_login(:userid>;
select f_user_logout(:userid);,
执行30秒,
在150里面 top -c -u pg93,在启动 worker 进程的时候没有用到简密易输出,所以不会输出 derg 信息。
10、目前 tick 没有反应
pgq 进程还在,worker 进程有五个。
tables 没有变化,tick 没有变化。
上图中已经执行完了,从当前的结果来看,数据是不一致的。tick 一直没有改变,跟踪日志看一下,cd londiste3/,less pgqd.log,less dst_digoal_03.log,destination3 的日志。
这是在切换角色的时候输出的允许日志。下图是在做 compare 的时候输出的日志。
是十点二十二分输出的空闲300秒的日志。看一下挂在 src 下的 destination1的日志,
也是显示空闲300秒的日志。
11、这样相当于 tick 根本没有进行下去,要看queue的进程是不是有问题。要把 queue 改一下,queue 是放在3.150里面,在更改完 root 后一定要更改 pgqd 的连接,queue 是消息队列,消息队列在3.150上,下图已经不产生信息了。
现在要先停掉 pgq,pgqd -s ./pgqd.in i,再启动它,pgqd -d ./pgqd.in i。现在 pgq 已经放在3.33上面了,端口是5432,也就是 destination3 库在3.33上,现在在启动 pgq。整个集群的队列开始起作用了,在整个架构当中,branch、root 这些节点上最好队列起来,还可以再写一个 pgq 的节点,pgqd.in i pgqd150.in i,后面在做角色切换的时候就不需要改了,因为已经有两个队列。
现在有两个 pgq,下图三个节点已经复制完了,
tick 复制到1378,后面两个节点还在复制,它们两个节点来自 src,src 上必须也有一个消息队列,否则没有办法做复制。lag 表示延迟,现在五个节点已经完全同步了。
12、这个时候在 compare 就没问题了,做的更新查询也已经完全同步了,现在数据正常。注意,在使用级联复制的时候,所有的 branch 节点,包括 root 节点上面都要起一个 pgq,destination3、destination4 放在同一个集群下,只需要起一个pgq。会在整个 dst3 里面起 pgq,只要不做过滤。有一个 database_list=,list 限制哪些库需要做 tick,不限制的话,destination3、destination4 都在3.33下,没有限制,这两个库都会有 tick。所以他们只需要取一个 pgq,对一个集群来说,起一个 pgq 就可以了。这里起了两个 pgq,一个是 src _digoal_01,一个是 destination3 和 destination4 对应的3.33,5432端口数据库起的 pgq。这样在做角色切换的时候就不会担心遇到不同步的情况。在做一个 takeover,把 dst4 升级,跟 dst3 做切换。使用 takeover,londiste3 -v /home/pg93/
londiste3/dst_digoal_04.in i takeover,互换完以后,再做一些 pgbans 的压力测试,先看一下他的状态。
destination 4 变成了根节点,destination 3 做了互换之后,相当于挂在 dst4 下。destination 3 原来是一个根节点,现在挂在 destination 4 ,原来挂在destination 3 下保存不变,现在结构如上图。
13、再做 DNL 的时候,要连到 destination4 这个库做,用户 destination1 做压力测试。现在 root 节点变成了 digoal_04,查看它的状态,tick 增长了。
做大量复制的动作,
复制完之后看它的状态。同时,src 要换回去,只要做 takeover,把 src 跟 dst_digoal_04 做 takeover。这就是级联复制以及角色切换的一些例子,DDL 已经讲过了,新增复制节点也讲过了。在源码的 doc 里面有一个 faq,skytools 是由三个组件组成,分别是 pgq、londiste3、WMA manergen。pgq 组件是队列的东西,当数据变更之后,会把数据、组件这种状态记录在3个门循的表中,叫做 event_1 和 event_2。对变更产生的记录,带有事物状态,这个队列是完全的 遵循 ACID 的消息队列。目前只有 pgq 完成这样的功能,没有其他的队列有这种功能。
londiste 消费节点就是去 pgq 产生消息队列,把数据取出来,做一些复制操作。
14、pgq 设计来源于 slony-1 的思想,但是跟 slony-1 又完全不一样,因为 pgq 是不依赖于数据库版本的插件,是用 C/plpsql 写的,pgqd 进程是用 c 写的,用来做一些时间节点,事物的切片。
如果要做数据库的还原,把它还原到一个新的环境当中,txid 也就是 eventid 不能大于当前数据库的 txid。主库变成了 digoal_04,txid_current,当前是 1335662 这个id,事件存在 pgq 中,event_1_0 有一个 ev_txid,这个事件的事物 id。
这个事务id就是 1335662 这个数据库一直在产生的id,这个 id 是 int8 的类型。把整个数据库包括事件导出,导入到另外一个库的时候,另外一个库的事物 ID 比当前产生的事物 ID 要小的情况,会有问题。因为新产生的事物记录在 ev_txid,反而比导过来的小,在同步的时候会出现一些问题。本来这是一个关于自增的,导过去之后,为了不产生这种问题,新产生的事物能够大于所有到入的 txid,可以通过改数据库的 epoch。比如说现在这个数据库的 epoch 是零,
150 数据库的 epoch了也是零,
假如导出来的不是零,是二,导入是零的库里,可以通过 pg_resetxlog 把新的 epoch 设置进去。就是指定下一个事物的 epoch,这样能达到是新的数据库的 epoch 变大,大于导过来的所有数据的 event txid,londiste 可以继续使用下去,否则会有问题。
15、londiste 是基于触发器异步的单组的复制,跟 slony-1 一样,但是有一个区别,slony-1 跟 posgree 的源代码绑定,9.3的版本必须用对应9.3的 slony,要编译到 posgree 里面去。使用 skytool 相当于是一个松散的东西,只要在里面创建对应的触发器就可以了。触发器用什么写都可以,因为它是完全独立的,能达到同样的目的就可以。londiste3 整合了这一类东西。
对于数据库宕机,还有网络异常等都没有问题,可能会对 provider 节点队列产生膨胀,但是只要及时把这些节点修复,这些膨胀都可以消失。如果确实不能修复,也可以把节点删除,对于正常的节点,只要已经复制的消息都可以删除,不会导致表膨胀的很大。
就是这三个,不会膨胀的很大,这是使用的注意事项以及它的 ICQ。 londiste3 讲了一对多的复制、级联复制,后面再讲数据的拆分和数据的合并,也是通过 londiste3 来实现。