在数据库postgres的一些应用中,经常有这样的需求:A系统数据库中的表test和B系统数据库中的表test_bak需要实现表级别数据同步。今天根据这个需求,我大致梳理了posrtgres_fdw + trigger的解决方案。这个解决方案基本上可以满足对数据表性能要求不苛刻的情况。之所以这样说,主要是因为:(1)利用触发器实现表同步意味着磁盘的写负载;(2)sql的二次执行会产生负载和延迟。使用这种方案实现表同步,最好需要斟酌一下这几个问题再确认下是否采用这个方案。
1. 需要复制多少张表;
2. 这些表主要是记录哪些信息的,这些表大概多少数据量;
3. 复制的表更新频率是否频繁,还是说主要是基础数据的同步?
4. 对于这些表数据的同步,实时性要求是怎样的,秒级同步能够满足要求?
如果针对以上的这些问题,你决定采用这个同步方案,那么OK,搭建流程我已经梳理了。
搭建流程:
一. 搭建postgres_fdw
把B服务器的同步表通过postgres_fdw扩展映射到A服务器做外部映射。
在A服务器上执行:
create extension postgres_fdw ;
create server basic_server foreign data wrapper postgres_fdwoptions(host'192.168.134.132',port'5432',dbname'flying');
create user mapping for flying SERVER basic_server options(user'flying',password'flying'); // 此处用户为应用用户,要求对表有读写权限。
grant USAGE on FOREIGN server basic_server to flying;
import foreign schema public limit to(test_bak) from server basic_serverinto public;
二. 在服务器A上创建trigger,使表test上的insert、update、delete操作都能在test_bak表进行。
(1) 创建触发器函数
CREATEOR REPLACE FUNCTION public.test_after_func()
RETURNS trigger AS
$BODY$
BEGIN
CASETG_OP
WHEN'INSERT' THEN
insertinto public.test_bak select new.*;
returnNULL;
WHEN'DELETE' THEN
deletefrom test_bak where id = OLD.id and name = OLD.name;
returnNULL;
WHEN'UPDATE' THEN
updatetest_bak set id = NEW.id ,name = NEW.name
whereid = OLD.id and name = OLD.name;
RETURNNULL;
ENDCASE;
END;
$BODY$
languageplpgsql;
(2)创建触发器
--droptrigger test_bak_insert ON test;
createtrigger test_bak_insert AFTER insert ON test FOR EACH ROW EXECUTE PROCEDUREtest_after_func();
--droptrigger test_bak_delete ON test;
createtrigger test_bak_delete AFTER delete ON test FOR EACH ROW EXECUTE PROCEDUREtest_after_func();
--droptrigger test_bak_update ON test;
createtrigger test_bak_update AFTER update ON test FOR EACH ROW EXECUTE PROCEDUREtest_after_func();
三. 测试
当前test表数据:
id | name
----+-------
3 | test3
2 | hello
当前test_bak表数据
id | name
----+-------
3 | test3
2 | hello
(1)test表插入数据 insert into test values(1,'test1');
当前test表数据:
id | name
----+-------
3 | test3
2 | hello
1 | test1
当前test_bak表数据
id | name
----+-------
3 | test3
2 | hello
1 | test1
(2)test表修改数据 update test set name='lili' where id =1;
当前test表数据:
id | name
----+-------
3 | test3
2 | hello
1 | lili
当前test_bak表数据
id | name
----+-------
3 | test3
2 | hello
1 | lili
(3)test表删除数据 deletefrom test where id =1;
当前test表数据:
id| name
----+-------
3 | test3
2 | hello
当前test_bak表数据
id | name
----+-------
3 | test3
2 | hello
四.需要注意问题
(1)当服务器A中的test表表结构发生更改时,需要手动对B服务器的test_bak表和test外部表进行相同的表结构的修改。
服务器A中修改表结构
altertable test add column text1 text;
服务器B中修改表结构
altertable test add column text1 text;
服务器A上修改外部表结构
ALTERFOREIGN TABLE test_bak ADD COLUMN text1 text;
ALTERFOREIGN TABLE test_bak ALTER COLUMN text1 OPTIONS (column_name 'text1');
(2)当表结构发生更改时,触发器函数需要进行进行修改。
whereid = OLD.id and name = OLD.name and text1 = OLD.text1;
(3)为了保证不同服务器上同步表的表名称相同,可以修改B服务器上的表名称为test。
altertable test_bak rename to pzy;
alterforeign table test_bak options(set schema_name 'public', set table_name'test');
(4)假如在B服务器上创建表test的外部表,会出现如下问题:
在A服务器上的操作不能触发B服务器上的trigger,B服务器上的操作不会触发A服务器上的操作。所以需要看那个服务器上去进行操作了。
OK,就是这样了。跨系统读表在业务系统中还是很常见的。数据库级别的表的同步也就更显得重要。当然还有其他的实现表同步的方式,如slony、ondiste3、pglogical等,有兴趣的可以自己看下了。




