wtorek, 19 sierpnia 2014

Bucardo - replikacja master-master w PostgreSQLu

Bucardo jest aplikacją napisaną w perlu, służącą na przykład do replikacji danych w PostgreSQLu. Jest o tyle ciekawym rozwiązaniem, że pozwala na synchronizacje danych typu master-master.

Główne cechy Bucardo to:
  • Asynchroniczna kaskadowa replikacja master-slave i/lub master-master
  • Replikacja typy 'row based'
  • W swoim działania używa triggerów i asynchronicznych powiadomień LISTEN/NOTIFY/UNLISTEN (link do poprzedniego postu na ten temat tutaj)
  • Wymaga dedykowanej bazy danych. Działa jako demon programu napisanego w Perlu, który komunikuje się ze swoją bazą danych i wszystkimi innymi bazami związanymi z działającymi replikacjami (synchronizacjami)
  • Multimaster synchronizacja jest ograniczona tylko do dwóch baz danych (Od wersji 5.0 to ograniczenie już nie istnieje). Dla tego typu replikacji musi być zdefiniowany sposób rozwiązywania konfliktów. Typy definicji ich rozwiązywania:
    • source - wiersz na bazy 'source' zawsze wygrywa
    • target - wiersz na bazie 'target' zawsze wygrywa
    • skip - wiersze przy, których wynikł konflikt nie są zreplikowane
    • random - każdy z serwerów ma takie same szanse
    • latest - wiersz, który został niedawno zmieniony, wygrywa
    • abort - synchronizacja jest przerywana na konflikt
  • Replikacja master-slave umożliwia działanie jednego mastera z wieloma slavami
  • Jest możliwa kaskadowa synchronizacja
  • Nie ma możliwości replikacji zmian typu DDL ponieważ PostgreSQL nie ma triggerów na tabelach systemowych
  • Nie ma możliwości replikacji dużych obiektów
  • Możliwość dowolnego projektowania architektury naszej replikacji
  • Wymagania:
    • PostgreSQL:
      • Od wersji 8.X
      • zainstalowany język pl/perlu
      • zainstalowany język pl/pgsl
    • Perl + moduły (DBD::Pg, DBI, DBIx::Safe, ExtUtils::MakeMaker)
  • Bucardo w wersji 4 trzeba zdefiniować jeden z typów synchronizacji:
  • Demony działające w Bucardo:
    • MCP - Master Control Process - główny proces zarządzający synchronizującymi
    • CTL - Controller - śledzi i zabija procesy synchronizujące
    • KID - Proces synchronizujący
  • Bardzo prosta instalacja paczek perl-owych i Bucardo:

    perl Makefile.pl
    make
    make test
    sudo make install
    
    
    Instalacja Bucardo:

    [bucardo@bucardo ~]$ /usr/local/bin/bucardo install
    This will install the bucardo database into an existing Postgres cluster.
    Postgres must have been compiled with Perl support,
    and you must connect as a superuser
    
    Current connection settings:
    1. Host:           
    2. Port:           5432
    3. User:           postgres
    4. Database:       bucardo
    5. PID directory:  /var/run/bucardo
    Enter a number to change it, P to proceed, or Q to quit: 3
    
    Change the user to: bucardo
    
    Changed user to: bucardo
    Current connection settings:
    1. Host:           
    2. Port:           5432
    3. User:           bucardo
    4. Database:       bucardo
    5. PID directory:  /var/run/bucardo
    Enter a number to change it, P to proceed, or Q to quit: 4
    
    Change the database name to: test
    
    Changed database name to: test
    Current connection settings:
    1. Host:           
    2. Port:           5432
    3. User:           bucardo
    4. Database:       test
    5. PID directory:  /var/run/bucardo
    Enter a number to change it, P to proceed, or Q to quit: P
    
    Postgres version is: 9.3
    Attempting to create and populate the bucardo database and schema
    Database creation is complete
    
    Updated configuration setting "piddir"
    Installation is now complete.
    If you see errors or need help, please email bucardo-general@bucardo.org
    
    You may want to check over the configuration variables next, by running:
    /usr/local/bin/bucardo show all
    Change any setting by using: /usr/local/bin/bucardo set foo=bar
    
    
  • Konfiguracja Bucardo
    • Przykład dodania bazy danych

      [bucardo@bucardo ~]$ /usr/local/bin/bucardo add database amsterdam dbname=test host=188.226.xxx.xxx user=root pass=root
      Added database "amsterdam"
      
      [bucardo@bucardo ~]$ /usr/local/bin/bucardo add database niujork dbname=test host=107.170.xx.xx user=root pass=root
      Added database "niujork"
      
      

    • Przykład listy baz danych

      [bucardo@bucardo ~]$ /usr/local/bin/bucardo list dbs
      Database: amsterdam  Status: active  Conn: psql -U root -d test -h 188.226.xxx.xxx
      Database: niujork    Status: active  Conn: psql -U root -d test -h 107.170.xx.xx
      
      

    • Przykład dodania tabeli

      [bucardo@bucardo ~]$ /usr/local/bin/bucardo add table test1 db=amsterdam
      Added the following tables or sequences:
        public.test1
      [bucardo@bucardo ~]$ /usr/local/bin/bucardo add table test1 db=niujork
      Added the following tables or sequences:
        public.test1
      
      

    • Przykład usuwania tabel

      [bucardo@bucardo ~]$ /usr/local/bin/bucardo remove table test1
      Please use the full schema.table name
      [bucardo@bucardo ~]$ /usr/local/bin/bucardo remove table public.test1
      Removed the following tables:
        public.test1
      [bucardo@bucardo ~]$ /usr/local/bin/bucardo list tables
      No tables have been added yet
      
      

    • Przykład stworzenia synchronizacji

      [bucardo@bucardo ~]$ /usr/local/bin/bucardo add sync test1_sync dbs=niujork,amsterdam tables=test1 conflict_strategy=bucardo_source 
      WARNING:  Issuing rollback() due to DESTROY without explicit disconnect() of DBD::Pg::db handle dbname=test;host=107.170.39.26 at line 279.
      KONTEKST:  PL/Perl function "validate_sync"
      SQL statement "SELECT validate_sync('test1_sync')"
      PL/Perl function "validate_sync"
      Failed to add sync: DBD::Pg::st execute failed: ERROR:  DBD::Pg::db do failed: ERROR:  permission denied for database test at line 280. at line 30.
      KONTEKST:  PL/Perl function "validate_sync" at /usr/local/bin/bucardo line 4413.
      
      

      W bazach musi być zainstalowany język plperl:

      test=> CREATE EXTENSION plperl;
      CREATE EXTENSION
      test=> \dL
                               Lista języków
        Nazwa  | Właściciel | Zaufany |             Opis             
      ---------+------------+---------+------------------------------
       plperl  | root       | t       | PL/Perl procedural language
       plpgsql | postgres   | t       | PL/pgSQL procedural language
      (2 wiersze)
      
      test=> \q
      
      

      Stworzenie synchronizacji typu master-slave (pierwsza baza jest typu 'source', a druga (albo po prostu każde następne) 'target'):

      [bucardo@bucardo ~]$ /usr/local/bin/bucardo add sync test1_sync dbs=niujork,amsterdam tables=test1 conflict_strategy=bucardo_source 
      Added sync "test1_sync"
      Created a new relgroup named "test1_sync"
      Created a new dbgroup named "test1_sync"
      
      

      Stworzenie synchronizacji typu master-master (jawnie definiujemy typ bazy danych):

      [bucardo@bucardo ~]$ /usr/local/bin/bucardo add sync test1_sync dbs=niujork:source,amsterdam:source tables=test1 conflict_strategy=bucardo_source
      Added sync "test1_sync"
      Created a new relgroup named "test1_sync"
      Created a new dbgroup named "test1_sync_2"
      
      

    • Status synchronizacji

      [bucardo@bucardo ~]$ /usr/local/bin/bucardo status
      PID of Bucardo MCP: 22761
       Name         State    Last good    Time      Last I/D    Last bad    Time  
      ============+========+============+=========+===========+===========+=======
       test1_sync | Good   | 11:06:44   | 24m 32s | 0/373     | none      |       
      
      

      Szczegółowe informacje o synchronizacji:

      [bucardo@bucardo ~]$ /usr/local/bin/bucardo status test1_sync
      ======================================================================
      Last good                : Aug 12, 2014 11:06:43 (time to run: 2s)
      Rows deleted/inserted    : 0 / 373
      Sync name                : test1_sync
      Current state            : Good
      Source relgroup/database : test1_sync / amsterdam
      Tables in sync           : 1
      Status                   : Active
      Check time               : None
      Overdue time             : 00:00:00
      Expired time             : 00:00:00
      Stayalive/Kidsalive      : Yes / Yes
      Rebuild index            : No
      Autokick                 : Yes
      Onetimecopy              : No
      Post-copy analyze        : Yes
      Last error:              : 
      ======================================================================
      
      

Każda tabela w synchronizacji ma nałożone na sobie triggery. Oto przykładowa tabela:

test=# \d test1
                                 Tabela "public.test1"
 Kolumna |          Typ          |                     Modyfikatory                     
---------+-----------------------+------------------------------------------------------
 c1      | integer               | niepusty domyślnie nextval('test1_c1_seq'::regclass)
 c2      | character varying(20) | 
 c3      | integer               | 
Indeksy:
    "test1_pkey" PRIMARY KEY, btree (c1)
Wyzwalacze:
    bucardo_delta AFTER INSERT OR DELETE OR UPDATE ON test1 FOR EACH ROW EXECUTE PROCEDURE bucardo.delta_public_test1()
    bucardo_kick_test1_sync AFTER INSERT OR DELETE OR UPDATE OR TRUNCATE ON test1 FOR EACH STATEMENT EXECUTE PROCEDURE bucardo.bucardo_kick_test1_sync()
    bucardo_note_trunc_test1_sync AFTER TRUNCATE ON test1 FOR EACH STATEMENT EXECUTE PROCEDURE bucardo.bucardo_note_truncation('test1_sync')


Triggery:
  • bucardo.delta_public_test1 - wywołuje inserty z kluczem głównym 'zmienionego' wiersza do tabeli trakującej delta_public_test1

    BEGIN 
     IF (TG_OP = 'INSERT') THEN 
       INSERT INTO bucardo.delta_public_test1 VALUES (NEW."c1");
     ELSIF (TG_OP = 'UPDATE') THEN
       INSERT INTO bucardo.delta_public_test1 VALUES (OLD."c1");
       IF (OLD."c1" <> NEW."c1") THEN 
         INSERT INTO bucardo.delta_public_test1 VALUES (NEW."c1");
       END IF; 
     ELSE 
       INSERT INTO bucardo.delta_public_test1 VALUES (OLD."c1"); 
     END IF; 
     RETURN NULL;
     END;
    
  • bucardo.bucardo_kick_test1_sync - wywołanie NOTIFY do bucardo informując, że doszło do zmian.

    BEGIN
       EXECUTE $nn$NOTIFY bucardo, 'kick_sync_test1_sync'$nn$;
     RETURN NEW; 
    END;
    
  • bucardo.bucardo_note_truncation - wywołuje insert do tabeli trakującej, informując o wykonaniu polecenia TRUCATE. Czyści także tabele trakujące zmiany

Od wersji 5.0 dane zanim będą przeniesione do docelowego serwera, są usuwane (przy conflict_stategy = 'bucardo_source') przy pomocy klucza głównego, aby za pobiedź konfliktom danych. W wcześniejszej wersji były najpierw sprawdzane pojedynczo, co powodowało obciążenie serwera i bardzo duże opóźnienia.

DELETE FROM public.test1 WHERE c1 = ANY('{{"5"}}')

Przenoszenie danych:

COPY (SELECT * FROM public.test1 WHERE c1 IN ('6')) TO STDOUT

COPY public.test1("c1","c2","c3") FROM STDIN

Zdecydowanie polecam obecną wersje tego programu do replikacji danych zwłaszcza z powodu lepszej wydajności. Jeśli jednak potrzebujemy tylko replikacji typu master-slave wybrała bym wbudowaną replikację PostgreSQLa bo działa na poziomie plików binarnych i nie obciąża serwera dodatkowymi zapytaniami.

Brak komentarzy:

Prześlij komentarz