Postgres2015全国用户大会将于11月20至21日在北京丽亭华苑酒店召开。本次大会嘉宾阵容强大,国内顶级PostgreSQL数据库专家将悉数到场,并特邀欧洲、俄罗斯、日本、美国等国家和地区的数据库方面专家助阵:

  • Postgres-XC项目的发起人铃木市一(SUZUKI Koichi)
  • Postgres-XL的项目发起人Mason Sharp
  • pgpool的作者石井达夫(Tatsuo Ishii)
  • PG-Strom的作者海外浩平(Kaigai Kohei)
  • Greenplum研发总监姚延栋
  • 周正中(德哥), PostgreSQL中国用户会创始人之一
  • 汪洋,平安科技数据库技术部经理
  • ……
 
  • 2015年度PG大象会报名地址:http://postgres2015.eventdove.com/
  • PostgreSQL中国社区: http://postgres.cn/
  • PostgreSQL专业1群: 3336901(已满)
  • PostgreSQL专业2群: 100910388
  • PostgreSQL专业3群: 150657323

PostgreSQL 的流复制素来以高效,实时,稳定著称;为企业解决了很多问题,例如容灾,备份,HA,读写分离等等。
但是流复制有一个无法克服的弊端,下游节点只能做到只读,并且只能复制整个集群(使用walbouncer可以做到基于表空间或库级别的物理流复制)。
http://www.cybertec.at/en/products/walbouncer-enterprise-grade-partial-replication/
如果用户确实有表级或行级的复制需求,我们不得不使用其他手段来实施,例如londiste3, dblink, trigger, bucardo, slony-I等。
这些插件或工具是基于触发器的,所以对上游节点的性能影响比较大,而且复制效率一般般。
PostgreSQL社区一直在努力将逻辑复制加入到PG的内核中,同样使用的是XLOG,从XLOG中解出row,在下游节点回放。有点类似于MySQL的binlog复制方案。
在逻辑复制加入PostgreSQL内核代码前(预计9.6的版本可能会加入),用户可以使用2nd提供的bdr插件来实现逻辑复制。
如果做单向的复制,使用9.4或以上的PostgreSQL版本即可,而如果要使用双向复制(多主),则需要使用2nd提供的PostgreSQL版本。
地址:
https://github.com/2ndQuadrant/bdr
本文以单向复制为例,即UDR,讲解一下这个插件的使用。
下载插件,我们需要的是bdr-plugin的稳定分支。
# git clone -b bdr-plugin/REL0_9_STABLE git://git.postgresql.org/git/2ndquadrant_bdr.git bdr-plugin
安装UDR插件
# export PATH=/opt/pgsql/bin:$PATH
# cd bdr-plugin
# ./autogen.sh
# ./configure BUILDING_UDR=1
# make; make install

修改BUG
# cd /opt/pgsql/share/extension
[root@digoal extension]# cat bdr.control |grep default_version
default_version = '0.9.2.0'

# vi bdr--0.9.2.0.sql 
-- 注释掉这行,应该是bdr的BUG,这个函数依赖的C函数在2nd改版的postgresql下面。
-- CREATE OR REPLACE FUNCTION bdr.bdr_internal_sequence_reset_cache(seq regclass)
-- RETURNS void LANGUAGE c AS 'MODULE_PATHNAME' STRICT;

配置上游和下游节点
上游节点
$ vi postgresql.conf
listen_addresses='0.0.0.0'
port=1921
max_connections=100
unix_socket_directories='.'
ssl=on
ssl_ciphers='EXPORT40'
shared_buffers=512MB
huge_pages=try
max_prepared_transactions=0
max_stack_depth=100kB
dynamic_shared_memory_type=posix
max_files_per_process=500
shared_preload_libraries='bdr'
max_worker_processes=8
wal_level=logical
fsync=off
synchronous_commit=off
wal_sync_method=open_datasync
full_page_writes=off
wal_log_hints=off
wal_buffers=16MB
wal_writer_delay=10ms
checkpoint_segments=8
archive_mode=off
archive_command='/bin/date'
max_wal_senders=10
max_replication_slots=10
hot_standby=on
wal_receiver_status_interval=1s
hot_standby_feedback=on
enable_bitmapscan=on
enable_hashagg=on
enable_hashjoin=on
enable_indexscan=on
enable_material=on
enable_mergejoin=on
enable_nestloop=on
enable_seqscan=on
enable_sort=on
enable_tidscan=on
log_destination='csvlog'
logging_collector=on
log_directory='pg_log'
log_truncate_on_rotation=on
log_rotation_size=10MB
log_checkpoints=on
log_connections=on
log_disconnections=on
log_duration=off
log_error_verbosity=verbose
log_line_prefix='%i
log_statement='none'
log_timezone='PRC'
autovacuum=on
log_autovacuum_min_duration=0
autovacuum_vacuum_scale_factor=0.0002
autovacuum_analyze_scale_factor=0.0001
datestyle='iso,
timezone='PRC'
lc_messages='C'
lc_monetary='C'
lc_numeric='C'
lc_time='C'
default_text_search_config='pg_catalog.english'
bdr.conflict_logging_include_tuples=true
bdr.log_conflicts_to_table=true
bdr.temp_dump_directory='pg_bdr_temp_dump_dir'

$ vi pg_hba.conf
# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
#host    all             all             ::1/128                 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     postgres                                trust
host    replication     postgres 127.0.0.1/32            trust

配置下游节点
1. 只有postgresql.conf中配置的监听端口不一样,其他一样。
2. 创建逻辑备份目录。在下游节点初始化订阅时,需要用来存储从上游节点dump的整个被订阅的数据库的数据,所以这个目录的空间要足够大。
mkdir $PGDATA/pg_bdr_temp_dump_dir
启动数据库。

# 假设我的上游节点是1921端口,下游节点是1922端口。
pg_ctl start -D /data01/pgdata_1921
pg_ctl start -D /data01/pgdata_1922

# 在上游节点,我有一个数据库为up,我需要将这个数据库复制到下游节点的数据库down中。
# 创建上游数据库,并且在up库创建bdr扩展。
postgres@digoal-> psql -h 127.0.0.1 -p 1921
psql (9.4.4)
Type "help" for help.
postgres=# create database up;
CREATE DATABASE
postgres=# \c up
You are now connected to database "up" as user "postgres".
up=# create table tb(id int,info text);
CREATE TABLE
up=# insert into tb select generate_series(1,100);
INSERT 0 100
up=# create extension btree_gist;
CREATE EXTENSION
up=# create extension bdr;
CREATE EXTENSION

# 创建测试表,测试数据类型,测试函数,测试视图
postgres=# \c up
You are now connected to database "up" as user "postgres".
up=# create table t1(id int primary key,info text);
CREATE TABLE
up=# create or replace function f1() returns void as $$
declare
begin
  raise notice '%', now();
end;
$$ language plpgsql;
CREATE FUNCTION
up=# create view v1 as select count(*) as cnt from t1;
CREATE VIEW

up=# create type dt as (c1 int,c2 int,c3 int);
CREATE TYPE
up=# insert into t1 select generate_series(1,100);
INSERT 0 100
up=# create table t2(id int,c1 dt);
CREATE TABLE
up=# insert into t2 values (1,'(1,1,1)');
INSERT 0 1
up=# insert into t2 values (2,'(1,1,1)');
INSERT 0 1
up=# insert into t2 values (2,'(1,1,1)');
INSERT 0 1
up=# insert into t2 values (2,'(1,1,1)');
INSERT 0 1
up=# insert into t2 values (2,'(1,1,1)');
INSERT 0 1

创建bdr扩展后,新建的表会自动添加TRUNCATE触发器
up=# \d t1
      Table "public.t1"
 Column |  Type   | Modifiers 
--------+---------+-----------
 id     | integer | not null
 info   | text    | 
Indexes:
    "t1_pkey" PRIMARY KEY, btree (id)
Triggers:
    truncate_trigger AFTER TRUNCATE ON t1 FOR EACH STATEMENT EXECUTE PROCEDURE bdr.queue_truncate()

# 创建下游节点的数据库down,同时也在这个数据库中创建bdr扩展。
postgres@digoal-> psql -h 127.0.0.1 -p 1922
psql (9.4.4)
Type "help" for help.
postgres=# create database down;
CREATE DATABASE
postgres=# \c down
You are now connected to database "down" as user "postgres".
down=# create extension btree_gist;
CREATE EXTENSION
down=# create extension bdr;
CREATE EXTENSION
down=# create database up; -- 务必创建哦

为什么在下游节点还需要创建一个up库(虽然我们不是将数据订阅到up库),但是没有这个库,还原会报错,例如:
这显然是个BUG。
Dumping remote database "hostaddr=127.0.0.1 port=1921 dbname=up user=postgres fallback_application_name='bdr (6203675445083668497,1,16385,): init_replica dump'" with 1 concurrent workers to "pg_bdr_temp_dump_dir/postgres-bdr-000C837A-1.8393"
Restoring dump to local DB "hostaddr=127.0.0.1 port=1922 dbname=down user=postgres fallback_application_name='bdr (6203675445083668497,1,16385,): init_replica restore' options='-c bdr.do_not_replicate=on -c bdr.permit_unsafe_ddl_commands=on -c bdr.skip_ddl_replication=on -c bdr.skip_ddl_locking=on'" with 1 concurrent workers from "pg_bdr_temp_dump_dir/postgres-bdr-000C837A-1.8393"
pg_restore: [archiver (db)] Error while PROCESSING TOC:
pg_restore: [archiver (db)] Error from TOC entry 3265; 1262 16385 SECURITY LABEL up postgres
pg_restore: [archiver (db)] could not execute query: ERROR:  database "up" does not exist
    Command was: SECURITY LABEL FOR bdr ON DATABASE up IS '{ "bdr" : true }';
pg_restore to hostaddr=127.0.0.1 port=1922 dbname=down user=postgres fallback_application_name='bdr (6203675445083668497,1,16385,): init_replica restore' options='-c bdr.do_not_replicate=on -c bdr.permit_unsafe_ddl_commands=on -c bdr.skip_ddl_replication=on -c bdr.skip_ddl_locking=on' failed, aborting
在上游节点开启一个更新的压力测试,以测试在上游节点有DML操作时可以复制数据。相互不干扰。
postgres@digoal-> vi t.sql
\setrandom id 1 100
update t1 set info=now()::text where id=:id;

postgres@digoal-> pgbench -M prepared -n -r -P 1 -f ./t.sql -c 8 -j 8 -T 10000 up
progress: 1.0 s, 30818.6 tps, lat 0.243 ms stddev 0.414
progress: 2.0 s, 32295.2 tps, lat 0.246 ms stddev 0.328

......
在下游节点定于上游节点的up数据库。
postgres@digoal-> psql -h 127.0.0.1 -p 1922 down
psql (9.4.4)
Type "help" for help.
down=# select bdr.bdr_subscribe(local_node_name:='down_1922', subscribe_to_dsn:='hostaddr=127.0.0.1 port=1921 dbname=up user=postgres', node_local_dsn:='hostaddr=127.0.0.1 port=1922 dbname=down user=postgres');
 bdr_subscribe 
---------------
 
(1 row)
查看订阅状态
down=# select * from bdr.bdr_nodes;
     node_sysid      | node_timeline | node_dboid | node_status |      node_name       |                     node_local_dsn                     |                  node_init_from_dsn                  
---------------------+---------------+------------+-------------+----------------------+--------------------------------------------------------+------------------------------------------------------
 6177143025216388117 |            10 |      70522 | r           | down_1922            |                                                        | 
 6177143025216388117 |            10 |      81996 | i           | down_1922-subscriber | hostaddr=127.0.0.1 port=1922 dbname=down user=postgres | hostaddr=127.0.0.1 port=1921 dbname=up user=postgres
(2 rows)
i表示正在初始化.
在上游节点可以查看到对应的slot已经被创建了
postgres=# select * from pg_replication_slots ;
                slot_name                 | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn 
------------------------------------------+--------+-----------+--------+----------+--------+------+--------------+-------------
 bdr_70522_6177143025216388117_10_83065__ | bdr    | logical   |  70522 | up       | t      |      |    661974801 | 2B/22A50A70
(1 row)
postgres=# select * from pg_stat_replication ;
 pid  | usesysid | usename  |             application_name             | client_addr | client_hostname | client_port |         backend_start         | backend_xmin |  state  | sent_location | write_location | flush_location | replay_loca
tion | sync_priority | sync_state 
------+----------+----------+------------------------------------------+-------------+-----------------+-------------+-------------------------------+--------------+---------+---------------+----------------+----------------+------------
-----+---------------+------------
 6170 |       10 | postgres | bdr (6177143025216388117,10,83065,):init | 127.0.0.1   |                 |       50711 | 2015-10-09 23:09:54.935301+08 |              | startup | 0/0           |                |                |            
     |             0 | async
(1 row)
一会,我们可以看到数据已经拷贝到下游节点的down库了。
down=# \dt
        List of relations
 Schema | Name | Type  |  Owner   
--------+------+-------+----------
 public | t1   | table | postgres
 public | t2   | table | postgres
 public | tb   | table | postgres
(3 rows)
down=# \dv
        List of relations
 Schema | Name | Type |  Owner   
--------+------+------+----------
 public | v1   | view | postgres
(1 row)
down=# \df f1
                        List of functions
 Schema | Name | Result data type | Argument data types |  Type  
--------+------+------------------+---------------------+--------
 public | f1   | void             |                     | normal
(1 row)
down=# \dT 
          List of data types
 Schema |     Name      | Description 
--------+---------------+-------------
 public | dt            | 
测试DDL,上游节点加了event,禁止直接使用DDL,所以我们需要通过函数来执行DDL。
postgres@digoal-> psql -h 127.0.0.1 -p 1921
psql (9.4.4)
Type "help" for help.
postgres=# \c up

up=# select * from bdr.bdr_replicate_ddl_command('create table public.new(id int)');  -- 注意必须制定schema
 bdr_replicate_ddl_command 
---------------------------
 
(1 row)

bdr对应的表,管理函数。查看:
http://bdr-project.org/docs/0.9.0/functions.html
http://bdr-project.org/docs/0.9.0/catalogs-views.html
删除订阅的方法:
1. 在上游节点删除slot,处理DDL队列,例如truncate bdr_queued_commands, bdr_queued_drops队列中的数据。
2. 在下游节点,注释shared_preload_library,重启数据库,去bdr_supervisordb 库删除下游节点的订阅库信息。
3. 在下游节点,改回shared_preload_library,重启数据库,删除原订阅库的bdr extension。
解决下游节点异常,例如无法添加订阅,可能由于之前没有正确的删除订阅。
注释shared_preload,重启数据库,去bdr_supervisordb 库修复。然后解除注释,重启数据库。
postgres=# \l
                                List of databases
       Name       |  Owner   | Encoding | Collate | Ctype |   Access privileges   
------------------+----------+----------+---------+-------+-----------------------
 bdr_supervisordb | postgres | UTF8     | C       | C     | 
 postgres         | postgres | UTF8     | C       | C     | 
 template0        | postgres | UTF8     | C       | C     | =c/postgres          +
                  |          |          |         |       | postgres=CTc/postgres
 template1        | postgres | UTF8     | C       | C     | =c/postgres          +
                  |          |          |         |       | postgres=CTc/postgres
 up               | postgres | UTF8     | C       | C     | 
(5 rows)
解决订阅异常,
2015-10-09 23:41:59.756 CST,,,7785,,5617e047.1e69,1,,2015-10-09 23:41:59 CST,3/0,0,ERROR,55000,"previous init failed, manual cleanup is required","Found bdr.bdr_nodes entry for bdr (6203671810517003626,1,16385,) with state=i in remote bdr.bdr_nodes","Remove all replication identifiers and slots corresponding to this node from the init target node then drop and recreate this database and try again",,,,,,"bdr_init_replica, bdr_init_replica.c:899","bdr (6203671810517003626,1,16385,): perdb"
链接到上游节点,删除slot
删除下游节点数据库,重新创建下游节点数据库,重新订阅。
小结
1. 如果上游节点产生XLOG非常频繁,下游节点初始化订阅的时间会非常漫长。
2. 如果上游节点产生XLOG非常频繁,下游节点的延迟可能会很大。
3. 由于udr和bdr是在一个插件中共用的,只是编译参数使用udr的编译选项,包含了BDR的功能后使用起来显得非常混乱。
4. 文档中存在一些BUG,例如删除UDR订阅的函数bdr.bdr_unsubscribe(local_node_name)不存在。
PostgreSQL的udr插件还有很多可以改进的地方,包括管理方面的,性能方面的。PGSQL又将多一个杀手锏。
如果担心目前还不够完善,在加入PG内核前,我们用它来做跨数据库大版本的增量迁移,跨硬件架构,或者不同数据块大小的增量数据迁移,是个不错的选择。
[参考]

1.  http://2ndquadrant.com/en/resources/bdr/

2. http://bdr-project.org/docs/0.9.0/index.html
3. https://github.com/2ndQuadrant/bdr/tree/bdr-plugin/REL0_9_STABLE
4. http://www.postgresql.org/docs/9.5/static/test-decoding.html
5. http://www.postgresql.org/docs/9.5/static/functions-admin.html#FUNCTIONS-REPLICATION
6. http://www.postgresql.org/docs/9.5/static/warm-standby.html#STREAMING-REPLICATION-SLOTS
7. http://www.postgresql.org/docs/9.5/static/protocol-replication.html
8. http://www.cybertec.at/en/products/walbouncer-enterprise-grade-partial-replication/
9. http://blog.163.com/digoal@126/blog/static/1638770402014101715715991

PostgreSQL 逻辑复制插件 UDR,可以愉快的玩类似MySQL的binlog复制了。相关推荐

  1. 参数详解 复制进程_如何优化PostgreSQL逻辑复制

    How to Optimize PostgreSQL Logical Replication 逻辑复制( Logical Replication )或 Pglogical 是表级别的复制.两者都是基于 ...

  2. 云和恩墨大讲堂丨PostgreSQL逻辑复制案例分享

    PostgreSQL逻辑复制案例分享--2月24日20:00 在PostgreSQL和基于PostgreSQL的国产数据库的使用中,逻辑复制作为一种区别于流复制的数据同步功能,常用于主业务库向分析库的 ...

  3. PostgreSQL 逻辑订阅 - 给业务架构带来了什么希望?

    标签 PostgreSQL , 逻辑订阅 , 10.0 , 数据汇聚 , 数据共享 , IDC多活 , 云端线下同步 背景 逻辑订阅是PostgreSQL 10.0的新特性. 具体的原理,使用方法可以 ...

  4. PostgreSQL逻辑订阅logical

    逻辑订阅是PostgreSQL10.0开始支持的新功能,PostgreSQL的物理流复制功能十分强大,在容灾.读写分离.HA等场景中都有广泛的使用,那么为什么还需要逻辑订阅呢? 我们先来看看物理流复制 ...

  5. 基于QGIS初探PostgreSQL的PostGIS插件,包括YUM和编译安装PostGIS

    写在前面:本文介绍 QGIS,只是为了展示怎么使用 PostGIS,因作者本人追求的是 PostgreSQL,所以本文的重点还是 PostGIS 这个 PostgreSQL 的插件,QGIS软件只做简 ...

  6. PostgreSQL全局临时表插件pgtt的使用

    墨墨导读:本文主要介绍PostgreSQL全局临时表插件pgtt的使用. https://github.com/darold/pgtt 前言 PostgreSQL目前到最新12版本只支持本地临时表不支 ...

  7. vscode中配置LeetCode插件的教程(愉快刷题)

    转载于脚本之家,原链接为https://www.jb51.net/article/183720.htm 大家好,今早在B站看到up主的vscode里藏了leetcode插件,这才知道原来还有这款神器. ...

  8. mysql 半同步 插件_编写半同步复制插件

    编写半同步复制插件 本节介绍如何使用plugin/semisyncMySQL源代码分发目录中的示例插件编写服务器端半同步复制插件.该目录包含名为rpl_semi_sync_master和的主插件和从插 ...

  9. 魔兽地图编辑器插件YDWE的使用与基本设置2之空格、复制、粘贴、撤销、重做键

    魔兽地图编辑器插件YDWE的使用与基本设置2之空格.复制.粘贴.撤销.重做键 1.space(空格):选择刷子 光标与上一选择状态(比如选中的建筑物.地形等)切换 2.撤销键Ctrl+Z 撤销上一步做 ...

  10. php 批量插件下载,网页链接批量复制插件下载 Bulk URL Opener (网页链接批量复制插件) v1.11.1 免费版 下载-脚本之家...

    Bulk URL Opener是一款简单好用的网页链接批量复制插件,该工具支持批量复制URL.批量打开等功能,极大提升了用户对于大量网址的处理效率,在短时间内完成多链接的操作,需要的人群千万别错过.感 ...

最新文章

  1. 在JavaScript中生成随机字符串/字符
  2. iphone开发如何测试?
  3. Android之RxJava(一)
  4. Unity WebGL 窗口自适应
  5. 语义分割概念及应用介绍
  6. 各种浏览器css不兼容的写法
  7. label mpchart 饼图_Android MPChart—饼图-Go语言中文社区
  8. oracle 12c容器数据库备份和恢复,oracle 12c数据库备份与恢复
  9. 【转】Perl、PHP、Python、Java和Ruby的比较
  10. 【实践1】Python调用搜狗语音,自制语音识别转文字生成字幕软件,并生成会议纪录。
  11. “滴血查癌”女主角被判入狱11年:公司估值曾达90亿美元 一朝覆灭
  12. 中国AI公司会议室取名简史
  13. 服务器端高性能的IO模型 转自酷勤网
  14. Embedded Browser WindowsPC内嵌网页
  15. 神雕侠侣手游服务器维护,《神雕侠侣》3月30日更新维护新服开启公告
  16. android 开发短信接收器
  17. applicationContext-job
  18. Go语言环境搭建详解(2020版)
  19. 惠普暗影精灵Plus 3代 (OMEN 17-an014TX)参数
  20. python 二进制流长度_python怎么处理二进制流

热门文章

  1. python入门学习——6种方法求n的阶乘(8种写法)
  2. OpenGL第三方库:GLAD入门篇
  3. 排队系统拥塞控制的位置
  4. 如何用自己的电脑开WiFi热点
  5. EBS 销售订单登记提示错误 ORA-00604 LPX-00225
  6. 播放器html模板,腾讯HTML播放器模板
  7. 关于交换的知识点(一)
  8. Java数独游戏破解工具源代码
  9. linux 查看硬盘序列号、设备序列号、系统安装时间
  10. 北京市朝阳区 办理 驾驶证期满换证 自助体检 的流程