暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

跨库实现表数据同步——posrtgres_fdw + trigger

漠南的工作笔记 2017-03-30
1459

在数据库postgres的一些应用中,经常有这样的需求:A系统数据库中的表testB系统数据库中的表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上的insertupdatedelete操作都能在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服务器上的triggerB服务器上的操作不会触发A服务器上的操作。所以需要看那个服务器上去进行操作了。

 

OK,就是这样了。跨系统读表在业务系统中还是很常见的。数据库级别的表的同步也就更显得重要。当然还有其他的实现表同步的方式,如slonyondiste3pglogical等,有兴趣的可以自己看下了。


文章转载自漠南的工作笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论