Как выгрузить логически согласованый набор данных из нескольких таблиц в БД под OLTP нагрузкой?

Для этих целей в ORACLE можно использовать замечательный инструмент- FLASHBACK.
FB позиционируется ORACLом как инструмент dba обеспечивающий ретроспективные запросы к таблицам
для восстановления утерянной по каким-либо причинам информации либо отката всех изменений к моменту времени в прошлом.
Один из режимов чтения работает по опции SCN(System Change Number).
Зафиксировав значение SCN можно читать произвольный обьем логически согласованой информации примерно DB_FLASHBACK_RETENTION_TARGET минут.
Пост не претендует на исчерпывающее описание функционала FB поэтому приведенный ниже пример cхематичен и достаточен для демонстрации его нетрадиционного применения.

Проверяем доступность опции FLASHBACK.
SELECT FLASHBACK_ON FROM V$DATABASE;


Выдаем учетке под которой работает выгрузка необходимые права.
grant execute on SYS.DBMS_FLASHBACK to LOADER;


Значение SCN можно получить несколькими способами.
   select sys.DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER from dual;
   select TIME_DP, SCN from sys.smon_scn_time order by scn desc;
   SELECT TIMESTAMP_TO_SCN(sysdate-1/(24*60)) from dual;


Проверяем велосипед на ходу.

create table FB_TABLE( p1 VARCHAR2(64));
select sys.DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER from dual;
GET_SYSTEM_CHANGE_NUMBER
8842201836421

insert into FB_TABLE (p1) values ('string_1');
commit;
select sys.DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER from dual;
GET_SYSTEM_CHANGE_NUMBER
8842201836472

update FB_TABLE set p1='string_2';
commit;
select sys.DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER from dual;
GET_SYSTEM_CHANGE_NUMBER
8842201836530

select t.* from FB_TABLE AS OF SCN 8842201836421 t;
P1

select t.* from FB_TABLE AS OF SCN 8842201836472 t;
P1
string_1

select t.* from FB_TABLE AS OF SCN 8842201836530 t;
P1
string_2

По данной технологии несколько лет online собирается информация c нескольких десятков OLTP-серверов на сервер аналитики.
Поделиться с друзьями
-->

Комментарии (9)


  1. hellamps
    01.06.2016 13:18
    +2

    не очень хорошая идея:

    1) создается нагрузка на продакшн
    2) flashback — часть функционала Enterprise Edition — другие деньги за лицензирование
    3) DB_FLASHBACK_RETENTION_TARGET не дает 100% гарантии получения снэпшота за этот ретеншн.

    Если стоит цель просто получить текущий консистентный набор данных, то всё гораздо проще, достаточно поставить SERIALIZABLE транзакционный уровень изоляции и просто выбрать данные.

    А если стоит задача сделать постоянный процесс — почему не взять и сделать master-slave репликацию на сервер аналитики или на промежуточный сервер для ETL? Есть куча средств, начиная от ораклового GoldenGate до всяких SymmetricDS. В простейшем случае это вообще standby юнит, останавливаете применение логов = неизменяемый снепшот, выгрузили данные, поехали дальше апплаить… Этим может заниматься пассивный юнит, чтобы не простаивал, например.


    1. ComodoHacker
      01.06.2016 13:48

      достаточно поставить SERIALIZABLE транзакционный уровень изоляции и просто выбрать данные
      Вы упустили «под OLTP нагрузкой» в заголовке поста.


      1. hellamps
        01.06.2016 16:14

        Если мы берем flashback query — то и там и там механизм один и тот же — UNDO, и там и там будет snapshot too old если данные из undo ушли. Расскажете, чем будет отличаться в этой самой OLTP нагрузке сессия которая по прочитанному SCN будет делать flashback query от serializable транзакции? Или, таки, ничем?


        1. ComodoHacker
          01.06.2016 16:21

          Она не будет мешать другим сессиям.


          1. hellamps
            01.06.2016 16:55
            +1

            Никаких блокировок сессия с serializable не создает, мы же не в MS SQL, прости господи:

            SQL> select lo.session_id
            2, lo.oracle_username locker
            3, lo.os_user_name,o.owner||'.'||o.object_name object
            4, o.object_type
            5, decode(lo.locked_mode,
            6 1, 'No Lock',
            7 2, 'Row Share',
            8 3, 'Row Exclusive',
            9 4, 'Shared Table',
            10 5, 'Shared Row Exclusive',
            11 6, 'Exclusive') locked_mode
            12 from v$locked_object lo, dba_objects o
            13 where lo.object_id=o.object_id;

            SESSION_ID LOCKER OS_USER_NAME OBJECT OBJECT_TYPE LOCKED_MODE
            — — — — — —

            SQL> alter session set isolation_level=serializable;

            Session altered

            SQL>
            SQL> select lo.session_id
            2, lo.oracle_username locker
            3, lo.os_user_name,o.owner||'.'||o.object_name object
            4, o.object_type
            5, decode(lo.locked_mode,
            6 1, 'No Lock',
            7 2, 'Row Share',
            8 3, 'Row Exclusive',
            9 4, 'Shared Table',
            10 5, 'Shared Row Exclusive',
            11 6, 'Exclusive') locked_mode
            12 from v$locked_object lo, dba_objects o
            13 where lo.object_id=o.object_id;

            SESSION_ID LOCKER OS_USER_NAME OBJECT OBJECT_TYPE LOCKED_MODE
            — — — — — —
            SQL>

            Oracle использует оптимистический подход к блокировкам, и ничего заранее не блокирует и никого не приостанавливает. Предположим такой вариант: сессия ставит себе isolation_level=serializable, а другая сессия после этого апдейтит какую-то строчку. Затем, наша serializable сессия пробует обновить эту строчку. Где-то в другой вселенной первый апдейт должен был быть заблокирован нашей сессией, но не в оракле, в оракле мы получим во втором апдейте ORA-08177: can't serialize access for this transaction. Таким образом наша транзакция никого не будет держать(ну кроме конкуренции за shared pool, т.к. нужно snapshot востанавливать для сессии и память на это тратить…


    1. hellamps
      01.06.2016 17:10

      еще пять копеек от коллег: flashback query никогда не учавствует в shared cursors, т.е. каждый селект с флэшбеком это и parse и flood в library cache. Т.е. для продакшена это вообще, никак-никак не рекомендуется в каком-то повторяющемся коде.


  1. ComodoHacker
    01.06.2016 13:46
    +1

    FLASHBACK — это общее маркетинговое название, часто вызывающее путаницу (как и многие подобные у Оракла). Следует различать входящие в него технологии.

    1. Flashback Query — это возможность получать данные отдельных таблиц БД на определенный момент времени в прошлом (SELECT… FROM… AS OF ...)
    2. Flashback Database — возможность восстановить согласованное состояние всей БД на момент времени в прошлом.


    Так вот, для первого не требуется включения Flashback на уровне базы (FLASHBACK_ON в V$DATABASE). И параметр DB_FLASHBACK_RETENTION_TARGET к первому не имеет отношения. Данные берутся из UNDO и их наличие там регулируется параметром UNDO_RETENTION.

    Для выполнения flashback query, помимо обычного права SELECT на таблицы, пользователю нужно дать право FLASHBACK на те же таблицы, либо системное право FLASHBACK ANY TABLE.


    1. zip_zero
      01.06.2016 14:01
      +2

      С вашего позволения дополню.

      Чтобы обеспечить такую технологию работы, нужно убедиться, что у нас есть достаточное количество UNDO.
      Рассчитывается как undo_size = undo_retention * db_block_size * undo_block_per_sec

      А примерный порядок OLTP нагрузки — undo_block_per_sec — можно прикинуть так:
      SELECT MAX(undoblks/((end_time-begin_time)*3600*24)) undo_block_per_sec FROM v$undostat


  1. Cubus
    03.06.2016 23:08

    А чем DataPump не устраивает?

    Делаем просто
    $ expdp DUMPFILE=file.dmp TABLES=(T1,T2) FLASHBACK_TIME=(sysdate-24)

    Или с помощью impdp через dblink.

    Следующая ступень Кунг-фу — использование пакета dbms_datapump для таких вещей.

    Том Кайт справедливо говорил, что если в Oracle есть некий функционал, то дублировать его не стоит.