Настройка двухсторонней синхронизации БД Oracle (Oracle Streams)



Что делать, если нет дисковой полки под RAC, а отказоустойчивость БД Oracle в реальном времени обеспечить нужно? Настроить потоки данных Oracle (Oracle Streams)!

Про потоки данных написано много. Например, здесь, здесь и здесь, в том числе и в официальной документации. В данной статье описан вариант настроек без использования redo-логов, работающий для редакций Oracle Standard Edition One и Oracle Standard Edition 11gR2 (лицензия на Enterprise позволяет провести настройку проще, прямо в Enterprise Manager Console, см. ограничения).

Итак, стремимся к автоматическому перехвату DML операций во всех таблицах схемы HR и синхронизации данных в обе стороны:


  1. Настройка системных параметров БД:

    sqlplus / as sysdba
    ALTER SYSTEM SET global_names        = true scope= both;
    ALTER SYSTEM SET shared_pool_size    = 256M scope= both;
    ALTER SYSTEM SET streams_pool_size   = 256M scope= both;
    ALTER SYSTEM SET java_pool_size      = 100M scope= both;
    ALTER SYSTEM SET parallel_max_servers= 10   scope= both;
    
  2. Настройка Oracle Net Services
    tnsnames.ora:
    ORCLA =
      (DESCRIPTION =
        (ADDRESS = (PROTOCOL = TCP)(HOST = host_a)(PORT = 1521))
        (CONNECT_DATA =
          (SERVER = DEDICATED)
          (SERVICE_NAME = orcla.local)
        )
      )
    ORCLB =
      (DESCRIPTION =
        (ADDRESS = (PROTOCOL = TCP)(HOST = host_b)(PORT = 1521))
        (CONNECT_DATA =
          (SERVER = DEDICATED)
          (SERVICE_NAME = orclb.local)
        )
      )
    

  3. Создание табличного пространства администратора Streams:

    mkdir C:\Oracle\app\oradata\strems
    sqlplus / as sysdba
    -- Создать табличное пространство
    CREATE TABLESPACE streams_tbs DATAFILE 'C:\Oracle\app\oradata\strems\streams_tbs.dbf'
    	SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED;
    
  4. Cоздание администратора streams с привилегиями Oracle Streams:

    sqlplus / as sysdba
    -- создать администратора Streams
    CREATE USER strmadmin IDENTIFIED BY "<пароль>"
    	DEFAULT TABLESPACE streams_tbs
    	QUOTA UNLIMITED ON streams_tbs;
    -- права на подключение
    GRANT CONNECT TO strmadmin;
    GRANT DBA TO strmadmin;
    BEGIN
    -- привилегии администратора Streams
    	DBMS_STREAMS_AUTH.GRANT_ADMIN_PRIVILEGE(
    	grantee          => 'strmadmin',
    	grant_privileges => TRUE);
    END;
    /
    execute MGMT_USER.MAKE_EM_USER('STRMADMIN');
    
  5. Создание связей баз данных (database links):

    sqlplus strmadmin@orcla
    CREATE DATABASE LINK orclb CONNECT TO strmadmin
    	IDENTIFIED BY "<пароль>"
    	USING 'orclb';
    sqlplus strmadmin@orclb
    CREATE DATABASE LINK orcla CONNECT TO strmadmin
    	IDENTIFIED BY "<пароль>"
    	USING 'orcla';
    -- просмотреть связи базы данных
    SELECT * FROM ALL_DB_LINKS;
    -- протестировать связь базы данных
    SELECT * FROM DUAL@ORCLB;
    SELECT * FROM TAB@ORCLB;
    
  6. Создание очередей Streams, которые будут использоваться для хранения изменений в БД:

    sqlplus strmadmin@orcla
    begin
    -- Создание очередей (4 ANYDATA queues at each database)
    	dbms_streams_adm.set_up_queue@orcla(
    		queue_table=> 'strmadmin.apply_queue_table_a',
    		queue_name => 'strmadmin.apply_queue_a',
    		queue_user => 'strmadmin');
    	
    	dbms_streams_adm.set_up_queue@orcla(
    		queue_table=> 'strmadmin.capture_queue_table_a',
    		queue_name => 'strmadmin.capture_queue_a',
    		queue_user => 'strmadmin');
    	
    	dbms_streams_adm.set_up_queue@orclb(
    		queue_table=> 'strmadmin.apply_queue_table_b',
    		queue_name => 'strmadmin.apply_queue_b',
    		queue_user => 'strmadmin');
    	
    	dbms_streams_adm.set_up_queue@orclb(
    		queue_table=> 'strmadmin.capture_queue_table_b',
    		queue_name => 'strmadmin.capture_queue_b',
    		queue_user => 'strmadmin');
    end;
    /
    
  7. Создание процессов применения (Apply process):

    sqlplus strmadmin@orcla
    begin
    -- Создание процессов применения 
    	dbms_apply_adm.create_apply@orcla(
    		queue_name     => 'strmadmin.apply_queue_a',
    		apply_name     => 'apply_a',
    		source_database=> 'orclb',
    		apply_captured => false);
    
    	dbms_apply_adm.create_apply@orclb(
    		queue_name     => 'strmadmin.apply_queue_b',
    		apply_name     => 'apply_b',
    		source_database=> 'orcla',
    		apply_captured => false);
    end;
    /
    
  8. Настройка дополнительных параметров применения (подробнее):

    sqlplus strmadmin@orcla
    begin
    -- не выключать процесс при возникновении ошибки
    	dbms_apply_adm.set_parameter@orcla(
    		apply_name  => 'apply_a',
    		parameter   => 'disable_on_error',
    		value       => 'n');
    	dbms_apply_adm.set_parameter@orclb(
    		apply_name  => 'apply_b',
    		parameter   => 'disable_on_error',
    		value       => 'n');
    
    -- проводить сравнение только по первичному ключу
    	dbms_apply_adm.set_parameter@orcla(
    		apply_name  => 'apply_a',
    		parameter   => 'compare_key_only',
    		value       => 'y');
    	dbms_apply_adm.set_parameter@orclb(
    		apply_name  => 'apply_b',
    		parameter   => 'compare_key_only',
    		value       => 'y');
    end;
    /
    
  9. Создание процессов синхронного сбора изменений таблиц (Synchronous capture):

    sqlplus strmadmin@orcla
    SET SERVEROUTPUT ON
    DECLARE
    	V_SN VARCHAR2(255);
            -- получить список всех таблиц схемы
    	CURSOR GET_TABLES_CUR IS
    		SELECT table_name
    		FROM all_tables
    		WHERE owner = '<название схемы синхронизируемой БД>'
    		ORDER BY table_name;
    BEGIN
      DBMS_OUTPUT.ENABLE;
      -- название схемы
      V_SN := '<название схемы синхронизируемой БД>';
      FOR V_GT IN GET_TABLES_CUR LOOP
    -- Создание синхронного сбора изменений всех таблиц схемы (Synchronous capture)
    	dbms_streams_adm.add_table_rules@orcla(
    		table_name     => V_SN || '.' || V_GT.table_name,
    		streams_type   => 'sync_capture',
    		streams_name   => 'sca' || V_GT.table_name,
    		queue_name     => 'strmadmin.capture_queue_a',
    		include_dml    => true,
    		include_ddl    => false,
    		source_database=> 'orcla');
    	dbms_streams_adm.add_table_rules@orclb(
    		table_name     => V_SN || '.' || V_GT.table_name,
    		streams_type   => 'sync_capture',
    		streams_name   => 'scb' || V_GT.table_name,
    		queue_name     => 'strmadmin.capture_queue_b',
    		include_dml    => true,
    		include_ddl    => false,
    		source_database=> 'orclb');  
      END LOOP;
      COMMIT;
    END;
    /
    
  10. Процесс передачи изменений (Propagation process):

    sqlplus strmadmin@orcla
    SET SERVEROUTPUT ON
    DECLARE
    	V_SN VARCHAR2(255);
    	CURSOR GET_TABLES_CUR IS
    		SELECT table_name
    		FROM all_tables
    		WHERE owner = '<название схемы синхронизируемой БД>'
    		ORDER BY table_name;
    BEGIN
      DBMS_OUTPUT.ENABLE;
      -- название схемы
      V_SN := '<название схемы синхронизируемой БД>';
      FOR V_GT IN GET_TABLES_CUR LOOP
    	-- Настройка передачи изменений
    	  dbms_streams_adm.add_table_propagation_rules@orcla(
    		table_name              => V_SN || '.' || V_GT.table_name,
    		streams_name            => 'prop_a',
    		source_queue_name       => 'strmadmin.capture_queue_a',
    		destination_queue_name  => 'strmadmin.apply_queue_b@orclb',
    		source_database         => 'orcla',
    		queue_to_queue          => true);
    	  dbms_streams_adm.add_table_propagation_rules@orclb(
    		table_name              => V_SN || '.' || V_GT.table_name,
    		streams_name            => 'prop_b',
    		source_queue_name       => 'strmadmin.capture_queue_b',
    		destination_queue_name  => 'strmadmin.apply_queue_a@orcla',
    		source_database         => 'orclb',
    		queue_to_queue          => true);
      END LOOP;
      COMMIT;
    END;
    /
    
  11. Первоначальная синхронизация таблиц (если необходимо):

    -- удаление таблиц
    drop table "repl_user"."<имя удаляемой таблицы>"@orclb cascade constraints;
    -- создать ссылку на каталог на БД «b»
    create or replace directory b_dir as 'c:\oracle\app\oradata';
    -- получить SCN на БД «a»
    select owner, directory_name, directory_path from all_directories;
    -- синхронизация таблицы по сети
    select dbms_flashback.get_system_change_number@orcla() from dual;
    impdp strmadmin/<пароль>@orclb directory=b_dir network_link=orcla tables=<название схемы синхронизируемой БД>.<имя таблицы> flashback_scn=2025750
    
  12. Установка контрольных точек (необходимо для начала отчета перехвата изменений):

    sqlplus strmadmin@orcla
    SET SERVEROUTPUT ON
    DECLARE
    	V_SN VARCHAR2(255);
    	CURSOR GET_TABLES_CUR IS
    		SELECT table_name
    		FROM all_tables
    		WHERE owner = '<название схемы синхронизируемой БД>'
    		ORDER BY table_name;
    	iscn_a  number; 
    	iscn_b  number;    
    BEGIN
      DBMS_OUTPUT.ENABLE;
      -- название схемы
      V_SN := '<название схемы синхронизируемой БД>';
    	  iscn_a := dbms_flashback.get_system_change_number@orcla();
    	  iscn_b := dbms_flashback.get_system_change_number@orclb();
    	  FOR V_GT IN GET_TABLES_CUR LOOP
    	-- Установка SCN для таблиц
    	  dbms_apply_adm.set_table_instantiation_scn@orcla(
    		source_object_name    => V_SN || '.' || V_GT.table_name,
    		source_database_name  => 'orclb',
    		instantiation_scn     => iscn_b);
    	  dbms_apply_adm.set_table_instantiation_scn@orclb(
    		source_object_name    => V_SN || '.' || V_GT.table_name,
    		source_database_name  => 'orcla',
    		instantiation_scn     => iscn_a);
      END LOOP;
      COMMIT;
    END;
    /
    
  13. Запуск процессов применения и передачи:

    sqlplus strmadmin@orcla
    begin
    -- остановить все процессы
    	dbms_apply_adm.stop_apply@orcla(
    		apply_name      => 'apply_a');
    	dbms_propagation_adm.stop_propagation@orcla(
    		propagation_name=> 'prop_a',
    		force           => false);
    	dbms_apply_adm.stop_apply@orclb(
    		apply_name      => 'apply_b');
    	dbms_propagation_adm.stop_propagation@orclb(
    		propagation_name=> 'prop_b',
    		force           => false);
    -- запустить все процессы
    	dbms_apply_adm.start_apply@orcla(
    		apply_name      => 'apply_a');
    	dbms_propagation_adm.start_propagation@orcla(
    		propagation_name=> 'prop_a');
    	dbms_apply_adm.start_apply@orclb(
    		apply_name      => 'apply_b');
    	dbms_propagation_adm.start_propagation@orclb(
    		propagation_name=> 'prop_b');
    end;
    /
    
  14. Настройка завершена.
  15. Описание по отработке исключений приведено в «Troubleshooting Streams Apply Errors ORA-1403, ORA-26787 or ORA-26786,Conflict Resolution (Doc ID 265201.1)» и в «Streams Conflict Resolution (Doc ID 230049.1)»
Поделиться публикацией
Комментарии 6
    0
    В статье ничего лишнего, нет даже описания проблемы.
    [ режим зануды ON]
    Stream как и любая репликация больше не для отказоустойчивости, а для передачи данных на «большие» расстояния. Если вас интересует только отказоустойчивость, то тогда не понятно зачем репликация двухсторонняя, судя по схеме. Скрипты не читал, так как пока нет времени. Это же лишние проблемы со встречными транзакциями и конфликтами.
    Для отказоустойчивости на SE, где нет STAND BY, я бы делал примерно так:
    Физическая копия + скрипт в кроне:
    rsync директории арх логов, recover database.
    Статья хорошая, даёт повод новичкам почитать про Stream, так как кроме скриптов в статье почти ничего не написано.
    [ режим зануды OFF]
      0
      Минусом реализации отказоустойчивости через арх логи является необходимость нахождения (выезда) человека на объекте, способного, в случае отказа основного узла, в короткие сроки вывести резервный узел из READ ONLY в PRIMARY, и после починки основного узла вернуть как было. Здесь можно просто переключиться на второй узел и продолжить работу. Проблемы с конфликтами транзакций, опять же возникают не во всех схемах работы, в данной конфигурации за верное значение берется последнее введенное (compare_key_only).
      Польза статьи как раз и задумывалась, как польза в скриптах, т.к. теория то понятна и не сильно забывается, но когда доходит дело до конкретной реализации, которая не находится быстро в поисковике, то уходит много времени, тем более, если делать это приходится раз в пару лет.
      +1
      Все замечательно, но вы не описали самое интересное — сценарий действий в случае отказа основного узла, ради которого Streams и затевался. Где выгода в сравнении с «ручным» стендбаем?
        0
        По моему опыту, сама по себе технология stream не самая надежная, в ней множетсво подводных камней и неожиданных сюрпризов.
        Как я понял, в вашей конфигурации есть основной сервер и резервный.
        Зачем репликация данных в обе стороны, достаточно с основного на резервный?
          0
          Более того, она устаревшая, и более не продвигается.
          0
          Да, есть новая технология — GoldenGate, которая будет уметь/умеет делать то же самое, но за отдельные деньги.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое