开发者学堂课程【PostgreSQL快速入门:Londiste3对多的复制以及DDL排错】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/16/detail/71
Londiste3对多的复制以及dll排错
内容介绍:
一.压力测试
二.Compare 数据
三.启动进程
四.举例
接下来要介绍的还是 longdist,上一节已经将它的一些原理,它的使用以及从一个 provid 节点发送变更的消息到一个消息队列,一个订阅者去做消息的订阅,再到消费节点。
这一节要介绍的是 longdiste,包括 ddl 的触发,比如主节点的表结构要发生变更了,那么怎样在主节点以一个事物的形式,完成一次 ddl 之后并把 ddl 变更到所有的订阅节点?这里就涉及到通过 longdiste 来执行脚本的这样一个动作。
上一节介绍过的消费者,它的 schema 和表明,最好和主节点也就是provider要一致,如果不一致那么在执行这个脚本时,就会引发主节点执行成功,跟它一致的订阅节点也能执行成功,但是跟它不一致的比如 schema,或者表明不一致的这种节点就无法执行成功。
那么对于无法执行成 ddl 的这种节点,后续的表的变更也就没有办法 apply,通过查看状态就能够看到某一些节点失败的现象。
一.压力测试
在做这个之前可以去做一些压力测试,因为之前复制了有四个表,可以来看一下它的状态输入 longdistetables,status 状态,可以看到一个根节点,一个页节点,目标节点是四个表,原节点是四个表,Sequence 是没有原节点,dst1也没有,就没有做序列的复制。这里相当于是指复制,复制了四个表。
如下图:
可以把序列添加进去,来看一下这个序列的名字叫什么,输入 digoal_01,这个序列的名字是这两个:user_login_rec_id_seq
和 user_logout_rec_id_seq
,将它添加进,输入 add—sequencedigoal_01.user_login_rec_id_seq
,没有这条命令,再看一下可能是看错了,再输入—help,找到 addseq,再添加一个,在目标节点也去添加 dst1,将两个表 user_login_rec_id_seq,
user_logout_rec_id_seq
同时添加进去。那么现在看seq就能看到,src 也能看到,它的当前的值是一样的。
如下图:
接下来做一些压力测试,这个是模拟用户登录的一个 sql 语句。因为这里显示20万,所以这里 userid 随机的从一到二十万里面产生一个 userid,一个是查询一个是 insert 一个是 update。
这个可以通过函数来实现,这里还是要调函数,因为已经写好了 f_user_login 这个函数,调函数相当于就执行了这三步。输入Selectf_user_login(:userid)
作为一个传入。
那么 logout 就是调 logout 的函数。Login 和 logout,接着输入Selectf_user_logout(:userid
),登录完了之后马上退出。
来测试它,Login 和 logout 都放在一起。用几个线程呢?用八个线程,八个连接两个线程。连接到本地1921端口,执行120秒,那么就先来个30秒,这里输入,连接八个,线程两个。现在执行30秒。
这个时候就能够看到这里正在执行了,在这个复制节点就是 consumer 节点,它相当于是从3.150上面有个 passion 连过来,再做一些 Update,可以去看它的sql语句。
如下图:
输入 psql-h127.0.01,那些变更封装成sql语句在这里执行,输入select*frompg_stat_activity
,可以看到这个Sql 语句就是封装的一些 Sql 语句。比如它那边的一些变更会把pk记录下来,pasen 的 consumer 会把它封装成一条一条的sql语句。tick 会把这些事物,多个事物组合成一个大的组合,consumer 从这组合里面获取到这些id,根据这些id去检索到已经变更的数据,将它封装成 sql 语句,发送到 consumer 的节点,就是消费节点去执行。
比如这里有 insertupdate,又有一个 insert,logout_rec 是登出。
现在看到 passion 的进程就是它还在做这个变更,相当于这里还在做这种变更。Idleintransaction,这是以一个事物的形式再把数据传过来。
那这边已经执行完了,登录和登出的加起来的一个 tps 是3764。那我们看这个还在执行。通过这里去看它的状态,输入status,再输入 tables,去看当前的状态。看起来已经结束了,这里出现 ok 就表示已经结束。接着可以来 compare 一下。
Compare 之后看它两边的数据是不是一致的,现在来看这个行数以及 hash 数。对于这个user表是一致的,而绘画表在整个的测试当中会有更新的操也是一致,Login_rec 表也一致,还有 logout_rec 数据也一致。
比如从一个节点复制到另外一个节点数据是可以的,那么接下要添加另外一个节点了。就相当于是从一个节点复制到两个节点。现在 status 是一个根节点,一个页节点,也就是说从src_digoal_01(root)
复制到12s,Tick:1036,复制四个表。
那么接下来要做的是添加另外一个节点,在做这个之前,来测试一下 londiste 的应对网络故障或者是数据库的这种能力,比如这里继续做压力测试,再把那个背库就是这个订阅库关掉。输入pg_ct1stop-mfast
,订阅库关掉之后,来看一下,这里因为是一个集群,那也就不会被主动的 kill 了。
这里注意看它的日志,接着在这里复制,输入 pg93,londiste,上一节是把这个日志放在 logo 目录里面,dst1_digoal_01.log
这是目标库的日志。目标库中可以看到里面会报一些错误,就是当前八点五十五分,这个节点一直都有数据,但是在执行这个 job,也就是执行这个消费 job 时它就会报这个错误。
那么就 tail 它,tail 它之后会一直报这个错误,因为这个数据库连接不上,但是它要执行这一些job,将这个数据库start 起来,这边已经完成了。Start 起来,就像订阅节点,因为它是一个异步的,订阅节点挂掉之后,这里执行还是没有问题。
那么把订阅节点的库 start 起来之后,再看这个,它现在应该就不报错误了。现在已经在执行并且开始复制数据了再来看它的一个状态,下面这个是它的一个结果,就是总共消费了多少个事件,4.1096是时间。来看它的状态。Londiste3Tables,现在已经就已经复制完了。
先是 ok 的一个状态,表示它已经复制完了。
接着再验证一下,compare,可以看到显示 consumerlaggingtomuch 但是它这里的状态是一个 ok,这个状态显示有问题。正常情况下,它应该是一个 inthink 的状态,这个进程也还在进行。来看一下它的帮助。
当前它还是显示一个 ok 的状态,这里的还在做复制。输入 psql-h127.0.0.1,从这里来看他还在执行sql语句。还在执行这些 sqly 语句。
二.Compare 数据
接着输入wherepid<>pg_backend_pid<>;
,现在的状态是一个 idle 的状态。可以看到现在已经执行完了,那再来compare 就可以出结果了。也就是当这个消费节点单调,是不影响组库的一些操作。只是在主节点这个消息队列里面会堆积一些消息,因为这些消息还没有被消费掉,只有消费掉之后,它才会主动的去把这些消息清掉。那么现在
compare 之后,看到这个两个节点已经一致了,主节点和被节点已经一致,可以看到一些网络延迟。
也就是对于这种非常庞大的消息,使用这个来做还是不太合适的,相相比这个流附字,它的速度肯定不如流附字。它还有一个 tick 的一个间隔,总共现在有1000多个 tick 的。那么也就是网络故障对它不会有影响,只是会延迟一些复制,包括这个消息会变得更庞大,也就是中断的这段时间,它的消息没有办法回收掉。如果去查看这个 event 表,也能看到一些消息存在里面。
那么接下来要做第二个库了,也就是要再增加,再往它那里增加一个节点。也就是 Destination2。从主库复制到目标二这个数据库。那么这里因为3.1.5就是当前的这个版本,londiste 当前的这个版本它不支持对于条件去做过滤,以前的版本可以支持这种条件过滤,也就是对于这种带过滤条件,它的这种消息不会复制到节点,但是现在没有这个过滤条件。
所以现在只是在这个目标库2,也就是输入:digoal_02digoal_0
里面,这里用的 schema 和表明都不一样,跟这个组的表明也不一样,那么会出一些问题,这里是为了来演示一些问题。
为了把这个问题暴露出来,那么在使用时就尽量不要去用这种表明,schema名,不一样的这种情况。首先还是要创建它的配置文件叫 londiste2。那么这个配置文件也是放在这里,pg93londiste这里面,写上这些配置。连到3.39的5432端口。数据库名是 replika。
要注意的是,这个Q的名字跟前面也是一样的。那么现在这个配图文件就配置好了,再去启动这个进程。
这个是一个 provider 的一个配置,provider 的3.150,接下来这个是 subscriber,subscriber 的端口是55432,ip 是3.39。dst2同样也是创建页节点,配置文件是 pg93,那现在相当于就创建好了。如
下图所示:
三.启动进程
接下来要启动一个 work 进程,同样是指定配置文件,现在 work 进程已经启动起来了,就可以往里面添加这个要复制的表了,那么这里因为过滤条件不支持,所以将 copycondition 去掉。那么目标表是这样的,就是当目标表明不一样时,这里要指定目标表,后面带的这个参数就是对应的它的原表是叫什么。
比如这里指定目标表是 digoal02这下面,表明的 user_infol1
,它的原表是digoal_01.userinfo。
这里也是add_table
,这里也同样也是指定 pg93。那么这时就添加了一个表:digoal_01.user_info<digoal_02.user_info1>
。它对应的是在这个库里面的,是放在 digoal_02这个 schema下面的 user_info 这个表。同样的情况去添加这个 usersession,在这里就只添加了这两个表,usersession 也添加好了。那么现在相当于两个表,来看一下它的状态。
Statustables,显示 catching—up,就能够看到它当前已经在做拷贝了,他把 user_info 表和这个 user_session 表都要去做一个拷贝,将它们拷贝过来。两个数据都正在做同步,两个都完成了。那么现在相当于这两个表的数据都已经拷贝过来了,来看一下。
输入selectcount(*)fromuser_info1
,可以看到两万条已经过来了,还有 session1,这个两万条也已经过来了。当前的复制状态也已经完成了。
那么接下来同样还是要 compare 一下,看一下它们的数据是不是一致的。compare一下,输入 pg93,显然肯定是一致的,在 compare 时就能够看到一个 digoal01一个 digoal02,它的 count和checksum 也是一样的,那么也就表示这个数据是一样的。
这里如果是以前的版本,带了这种外条件的,会发现这边数据不一样,为什么?因为只过滤了 userid 小于100的这样一个数据,而在实际上,在原表里面它是有两万条记录,这是以前的一个版本的例子。那现在这个版本它不支持使用这种方式来做这种过滤,所以这里就不用演示这个场景,后面要演示的是一个执行ddl语句的场景,比如添加字段。这里如果要添加字段,往后看添加字段的一个测试,添加字段它是通过 londiste3去 execute 一个脚本来实现的。比如要往altertabledigoal_01.user_infoaddcolumnclint
这个表里面添加这些字段。
Vi在主节点,也就是有执行的londiste3这样一个节点,去创建一个addcolum的这样一个脚本,这个要放在一个事物里面,这样能够确保这个事物是完全正常的。
Digoal_01.user_info
,其实是因为使用的是post用户去连的,那使用pc用户去连时,这个表面一定要加上Digoal_01,因为所有的配置文件写的都是通过,比如 dst1,通过 postgre 这个用户去连的就是超均用户,所以这里要指定 schema 的名字要连到库区,再往里面添加一个 c1这样的字带。
添加这个字带,通过这个 Londiste 去调用这个脚本,一看就能够看出来,在主库和 digoal01这个库,也就是consumer 节点 digoal01这个库,去执行肯定没有问题,但是在这里去执行肯定有问题。因为根本就没有这个表,根本就没有 digoal01那个表,所以无法执行成功。
那怎么办?可以来执行一下,来实际操作一下就知道了。这里指定一个圆库,在 provider 上面去调用它。在consumer 这里去调研时,他会报 execute 这个错,使用 consumer2去执行时,它会报dst2_digoal_02.ini
这个错。但使 provider 去执行时不会犯这个错。
先来看一下,去执行 tmp,这个文件不是放在这里而是放在根这里,它这里显示已经执行成功了。那么再回到这里,Londiste3,输入 status,去看一下状态。因为去调用它时,其实它会自动帮你在所有的订阅节点也去执行 add_colum这个脚本。所以在 dst2_digoal_02这个库里面,相当于是刚执行的这个事物,它也会复制到其他的节点去,但是dst1_digoal_01这个节点是可以执行成功的,可以去看一下。输入digoal_01digoal_01
,去看这个 user_info 和user_sessiom,它已经增加了 c1这个字段。C1这个字段已经增加了。
但是在 digoal_02这个库里面,因为它调用的那个脚本,跟它一点关系都没有,所以它没有办法执行成功,那么怎么办呢?必须要让它执行成功,它的订阅才会往后走。因为它是顺序串行执行的,也就是所产生的这些消息,它是一个串行的消息。
那么这个消息无法执行成功,后面的比如 digoal_01主库这里,后面继续再执行Pgbench做压力测试时,把这个调出来,再去执行 Pgbench,在这个主库,再来看这个 status,就会发现这里的 tick 它还是卡在这里不动,因为一直都要去执行 sql 语句的脚本。outtable 加列加 c1的脚本,它就没有办法,只能卡着执行不下去。
但是对于叶子节点,因为它没有卡住,它一直都可以去执行。那这个tick在增长,这里一直在去把这些tick取过来再去执行。但是这里一直就卡在1120了,那怎么办?必须要修复这个事物,这个事物其实就相当于要让这几条 sql 语句:
Altertabledigoal_01.user_infoaddcolumclint
Altertabledigoal_01.user_sessionaddcolumclint
在 digoal02这个库让它能够成功的执行,那怎么办呢?让它能成功执行的同时还要确保它这里对应的表结构一致。所以先把这个表结构改掉。输入altertableuser_info1addcolumc1int
,再改为 usersession1,首先把 digoal_01这个表改掉,再去创建两个这样的表:
altertabledigoal01user_infoaddcolumc1int和altertable
digoal_01.user_sessionaddcolumc1int
。这样就能够让它成功的执行下去。
四.举例
比如要创建一个 schema,输入 schemadigoal1_01,首先要创建这个 Schema,再创建一个表,输入tabledigoal_01.user_info<idint>;,
这个表就是能够让它成功的执行 sql 语句,比如这里随便创建一个,只要让它能够成功的执行这两条sql语句就可以。
然后再 createtablesession,这样再回到这里来看它的状态,可以看到它这也就成功了。刚才那个就相当于是在新建的这两个表里面,已经把脚本去执行了,可以看到 C1已经加进去了。
那么这两个表就没有任何用处了,直接把它删除掉就好。输入droptabledigoal_01.user_info
还有usersession,再来看一下这些表上面的信息都已经在做这些tick,通过这个状态,看到这个tick就是第一个库和第二个库已经正常的同步完了,Tick 已经同步了,那么接下来就是要等待dst2_digoal_02
这个库,等待它的同步完成。这个 tick 正在执行。可以去看看。
那么就暴露出来这个问题,在调用这种脚本时就会发现它有这种问题。那么接下来,像这种脚本的修复一定要先去这里面加这个字段,为什么?
如果这个字段是后加的,那先去加这个字段,tick来执行完马上就会去做变更。User_session1 这里面的表要去做变更了,那么altertabledigoal_01.user_sessionaddcolumc1int
这里面的 c1字段其实是不存在的。
所以必须还是按照顺序,先加 user_info1和 user_session1这两
个,也就是脚本里面提到的这两个:
Altertabledigoal_01.user_infoaddcolumclint
Altertabledigoal_01.user_sessionaddcolumclint
对应的表,再让它能够成功的去执行脚本,这样能够确保数据一致。那么现在已经完全达到一致了,那么再来compare,现在去 compare 它三个节点应该都是一致的,这是原目标。因为用的是dst1_digoal_01
去做的compare,要用 src 去做 compare 就能够看到三个节点。
等它这里咨询完,用src就是用圆库去做 compare。那么200000是圆的,200000rows,checksum=7874这是 dst 目标库的。这个 srcdb 是圆的,这个200000是目标库的,dstdb 时圆的,user_ogout_rec
是目标库的。
再去看当调 dst2去 compare 时,它报了一个这样的错误。
Dst_digoal_02
,这里面的配置写错了,它就会去从圆和目标去做这种比较,比较出来的数据肯定是一致的。那么现在三个库里面的表的数据都是一致的。从这个例子,就能够看到尽量不要去使用这个不一致的这个状态,就包括schem 名不一致,表明也不一致,这种情况会导致一些比较棘手的问题,后面必须要去修复,如果不去修复那这个消息队列会越堆越长,导致这个队列库会很大。
这里的消息队列就会变得很大,比如schema里输入 londiste,看一下,这里应该是 pgq,这里面会有事件的一些消息。可以去看一下这些表的大小,这里就有两百多兆,event_1,它其实是好几个表,一直在做轮询。
当这个表,比如买了之后,要做ck了,直接把它ck,这样不用做dele操作,因为 dele 操作还会带来其他事物,那么三个表一直做轮询。Event_1_0这个表在做时,接下来就用 event_1_1这个表或者 event_1_2,它相当于三个表一直在做轮询。那这边就存出来一些消息,输入select*frompgq.event_1_0limit10,可以看到这里面存储的就是一些消息,比如这个消息对应的事件 id,这其实就是 txid,它的消息队列的队列名是什么,时间数据是什么,它的事件类型是什
么,都显示了。
再看这个 pgq_ext 里面,这里面是一些完成的 batch,然后就是完成的时间,对应的这个 node 里面就是一些订阅的信息,订阅节点的信息,输入pgq_node.subscriber_infe
,这个是不允许的,那必须要使用 postgre,输入digoal_01postgres
,使用超级用户可以去看,那这两个是订阅节点,可以看到订阅节点,订阅节点的信息,输入
nodeinfo 显示的这个这是原节点的信息。本地的一些状态,节点的 location 在什么地方?
接下来要举的这个例子是一个级联复制的一个例子。一定要注意londiste的健康,如果它有报错,一定要火速解决,为什么呢?否则三个事件表就会越来越大。相当于那个消息会堆在里面,不能够把这个消息提取出来。
那么从这里继续执行 pgbench,这里在执行 Pgbench 时去看这个 Pgq
的表,它肯定有变化,看到 event 这个表它越来越大,它的一个索引是指这个 event 的一个 transactionid,可以根据它倒序来排一下。输入select*frompgq.event_1_0orderbyev_txiddesclimit
10就能够看到这些事件。
如下图:
Userid=49674&logout_time=2014-01
这个是事件的一个信息。里面就包含了,比如说 pk,所有的一些记录。这一记录怎样通过这里呢?可以把它还原成一条 sql 语句。
比如说I表示一个 insert,u 表示一个 updat。pk 是什么,这里的pk是这个 id,下面 pk 是 userid。那update的里面的数据,对应的是一些什么样的数据,可以这样看更清晰一点。比如这个update里面包含了 useridlogintime 还有logincount,logouttime。它对应的是 usersession 这个绘画表,就比如正在更新Userid=49674&logout_time=2014-01
这个绘画,通过这个 userid 去更新这个绘画,那这样就通过这一条记录,就能够把这个 sql 语句还原出来。
对于这个 Eventid,注意这个 ID 一样的情况下,比如这两个是一样的,表示它是在一个事物当中。这个 txdid 在第一节其实就已经讲了,输入 select*fromtxidcurrent,这个其实就是当前的事故id,这里对应就是在执行这个 sql 语句时,它对应的一个事物 ID。
所以这些 sql 语句的还原也是可以通过这个,来当成一个事物来还原。
这种例子就是从一个节点复制到两个节点,看一下它的一个状态,输入 status,从一个节点复制到两个节点,尽量的注意不要使不同的 schema,就是你的目标表尽量跟原表去保持一致,包括表名和 sschema 名,如果不一致,比如就是在做修改表结构时,它会比较比较繁琐,像以下这个例子就必须要手工去修复它。
后面有一些参考,比如 londiste3它对应的配置文件,这里这个配置文件可以通过一些方式将它输出,所以包括了所有的配置,所有的一些配置都在这里。
如果是 pgqd,它也可以输出 Pgq 的一个配置。可以根据这里去做一些配置。后面是一些 py 的脚本,比如说 compare 用到的脚本在这个目录里面。
如果是添加删除列,添加触发器的过程,它是通过这里来实现的。
先在 provider 里面是 begin,再添加这个列在provide里面,再去执行这个provider的 refreshtrigger,移除出一个列,其实类似于删除列。changetriggerinsametransaction。再看这个延迟,再删除这个列 onsubscribe。
就是从一个节点复制到两个节点,并且里面的一些排错是怎么样来做的,可以再去试验一下。