пʼятниця, 24 липня 2015 р.

Механизмы обеспечения транзакций



Уровни изоляции, установленные стандартом, во-первых, определены недостаточно четко, во-вторых, не являются обязательными для начального уровня соответствия стандарту. Стандарт также не предусматривает того, как обеспечивается изоляция. Поэтому реальные СУБД достаточно по-разному трактуют изолированность транзакций и используют разные механизмы для обеспечения свойств ACID, в том числе и свойств параллельности.

5.4.1 Механизмы DB2

В DB2 для обеспечения атомарности транзакций применяется "упреждающее протоколирование", при котором изменения в данных записываются в журнальный файл прежде, чем транзакция фиксируется. Изменения в данных производятся в журнальных файлах и лишь при фиксации транзакции переносятся в основную область данных СУБД. Журнал используется для повторения или отката транзакций в случае сбоя.
В основе механизма обеспечения изоляции DB2 лежат блокировки. Суть блокировки состоит в том, что если для выполнения транзакции требуется гарантия того, что определенный объект не будет изменен параллельно выполняющейся транзакцией, объект блокируется, то есть, запрещается доступ к нему из других транзакций.
В самом простом случае СУБД обеспечивает блокировки двух типов:
  • S-блокировка - разделяемая блокировка или блокировка чтения, допускающая совместный доступ к строке таблицы;
  • X-блокировка - эксклюзивная блокировка или блокировка записи, не допускающая совместного доступа к строке.
Прежде чем считать (select) какую-либо строку из таблицы транзакция должна установить для строки S-блокировку. Прежде чем обновить (update, delete, insert) строку таблицы транзакция должна установить для строки X-блокировку. Если для строки уже установлена блокировка, несовместимая с той, которую пытается установить наша транзакция, то попытка нашей транзакции отвергается, транзакция переводится в ожидание до того момента, пока ранее установленная блокировка не будет снята. Совместимость блокировок показана в следующей таблице.
  S  X 
 S данет
 X нетнет
Тот или иной режим изоляции определяется той или иной комбинацией проверки и установки блокировок чтения.
Для уменьшения объема проверяемых при любом доступе блокировок вводятся дополнительные типы блокировок, называемые блокировками намерения. Блокировки намерения накладываются на всю таблицу перед наложением S- или X-блокировки на строку таблицы. Основной набор блокировок намерения следующий (хотя в действительности DB2 этот набор несколько шире):
  • IS - блокировка намерения чтения. Накладывается на некоторую таблицу T и означает намерение блокировать некоторую входящую в T строку в режиме S-блокировки.
  • IX - блокировка намерения записи. Накладывается на некоторую таблицу T и означает намерение блокировать некоторую входящую в T строку в режиме X-блокировки.
  • SIX - блокировка чтения с намерением записи. Накладывается на некоторую таблицу T и означает разделяемую блокировку всей этой таблицы с намерением впоследствии блокировать какие-либо входящие в нее строки в режиме X-блокировок.
Совместимость блокировок с учетом блокировок намерения показана в следующей таблице:
 ISSIXSIXX
ISдадададанет
Sдаданетнетнет
IXданетданетнет
SIXданетнетнетнет
Xнетнетнетнетнет

5.4.2 Механизмы Oracle

Механизмы обеспечения транзакций в Oracle называют оптимистическими. При этом имеется в виду то, что СУБД предполагает, что вероятность отката транзакции и конфликта транзакций по доступу к данным невелика. Oracle использует журнал, в котором хранит информацию для повторения транзакций в случае сбоя и "сегмент отката". Если транзакция изменяет базу данных, выполненные изменения сразу заносятся в область данных, а старая версия данных сохраняется в сегменте отката. Блокировки чтения, таким образом не накладываются. Подробный протокол чтения данных приводится ниже:
  • Для каждой транзакции (или запроса) запоминается текущий системный номер (SCN - System Current Number). Чем позже начата транзакция, тем больше ее SCN.
  • При записи страницы данных на диск фиксируется SCN транзакции, производящей эту запись; этот SCN становится текущим системным номером страницы данных.
  • Если транзакция читает страницу данных, то SCN транзакции сравнивается с SCN читаемой страницы данных.
  • Если SCN страницы данных меньше или равен SCN транзакции, то транзакция читает эту страницу.
  • Если SCN страницы данных больше SCN транзакции, то это означает, что некоторая другая транзакция, начавшаяся позже данной, успела изменить или сейчас изменяет данные страницы. В этом случае транзакция данная просматривает журнал транзакций назад в поиске первой записи об изменении нужной страницы данных с SCN меньшим, чем SCN данной транзакции. Найдя такую запись, транзакция использует вариант данных страницы из сегмента отката.
Для транзакции READ ONLY сегмент отката даже не создается.
При изменении данных Oracle накладывает на измененную строку эксклюзивную блокировку.
Таким образом, существенная разница в поведении двух рассматриваемых СУБД состоит в следующем: при угрозе возникновения чтения нецелостных данных DB2 блокирует транзакцию, пытающуюся выполнить такое чтение; Oracle же предоставляет транзакции то последнее целостное значение данных, которое существовало на момент начала транзакции. Подход Oracle в этой связи называют многоверсионным, так как одновременно разные транзакции могут видеть разные версии данных. Многоверсионный подход уменьшает количество блокировок, но подход, основанный на блокировках, обеспечивает более согласованное представление данных. Между разработчиками СУБД ведутся ожесточенные дискуссии о превосходстве того или другого подходов, но независимые эксперты, как правило, не могут отдать предпочтения ни тому, ни другому. Следует отметить, что существуют приемы и правила разработки приложений, которые позволяют обеспечить большую согласованность данных в Oracle и меньшее количество блокировок в DB2.

5.4.3 Реализация сценариев

5.4.3.1 Потерянные изменения
В обеих СУБД, независимо от установленного уровня изоляции, действия по сценарию п.5.3.1. будут следующими:
  • после ввода оператора UPDATE на шаге 2 транзакция Т1 "зависнет" (заблокируется):
  • оператор шага 2 завершится только, когда будет введен оператор COMMIT в транзакции T1.
  • на шаге 5 будет выбрано:
   id         dat 
---------- -----------
         1         101
         2         110
         3         120
         4         130
5.4.3.2 Грязное чтение
В DB2 при уровне изоляции CS и выше транзакция транзакция T1 на шаге 2 заблокируется. Оператор этого шага завершится только тогда, когда в транзакции T2 будет введен оператор ROLLBACK или COMMIT. Если транзакция T2 будет зафиксирована, то в транзакции T1 на шаге 3 будет выбрано:
        id         dat 
---------- -----------
         1         101
         2         101
         3         120
         4         130
Если транзакция T2 откатится, то в транзакции T1 на шаге 3 будет выбрано:
        id         dat 
---------- -----------
         1         100
         2         100
         3         120
         4         130
В Oracle блокировок не будет. На шаге 3 будет выбрано:
        id         dat 
---------- -----------
         1         100
         2         100
         3         120
         4         130
Поскольку транзакция T1 не завершается до шага 5, то на шаге 5 будет выбрано исходное состояние таблицы. (Если транзакция T2 зафиксируется, а не откатится, то на шаге 5 будут отображены только изменения, выполненные в T2).
5.4.3.3 Неповторяющееся чтение
В DB2 при уровне изоляции RS и выше транзакция T2 будет заблокирована на шаге 2, пока в транзакции T1 не будет выполнен оператор COMMIT или ROLLBACK. Если зафиксировать транзакцию T1 на шаге 2, то на шаге 4 будут выбраны уже обновленные значения, но это будет уже другая транзакция.
В Oracle при уровне изоляции SERIALIZABLE на шагах 1 и 4 будет выбрано одно и то же:
        id         dat 
---------- -----------
         1         100
Но если ту же выборку повторить после фиксации транзакции T1, то будет выбрано:
        id         dat 
---------- -----------
         1         101
5.4.3.4 Фантом
В DB2 при уровне изоляции RR транзакция T2 будет заблокирована на шаге 2, пока в транзакции T1 не будет выполнен оператор COMMIT или ROLLBACK. Если завершить транзакцию T1 на шаге 2, то на шаге 4 будут выбраны уже обновленные значения, но это будет уже другая транзакция.
В Oracle при уровне изоляции SERIALIZABLE на шагах 1 и 4 будет выбрано:
        id         dat 
---------- -----------
         3         120
         4         130
      
Если ту же выборку повторить после фиксации транзакции T1, будет выбрано:
        id         dat 
---------- -----------
         3         120
         4         130
         5         150

5.4.4 Тупики

Поскольку обе СУБД используют блокировки записи для предотвращения, например, такого нежелательного эффекта, как потерянные изменения, возможно возникновение тупиков. Тупик может возникнуть в том случае, если транзакция T1 требует каких-то ресурсов, которые эксклюзивно блокированы транзакцией T2, а транзакция T2 требует ресурсов, которые эксклюзивно блокированы транзакцией T1. Сценарий возникновения тупика, например, следующий:
Транзакция T1ШагТранзакция T2
UPDATE example SET dat=101 WHERE id=11 
 2UPDATE example SET dat=112 WHERE id=2
UPDATE example SET dat=111 WHERE id=23 
 4UPDATE example SET dat=102 WHERE id=1
Транзакция T1 должна заблокироваться на шаге 3, а транзакция T2 - на шаге 4. Единственным способом "развязки" тупика является принудительное освобождение одной из транзакций удерживаемого ею критического ресурса.
Обе СУБД обнаруживают и развязывают тупики. Дойдя до зависания обеих транзакций на шагах 3-4, обе СУБД после некоторой временной выдержки развязывают тупик, но делают это несколько по-разному. DB2 принудительно откатывает одну из транзакций с сообщением об ошибке, вторая транзакция при этом разблокируется и завершается. Oracle откатывает только оператор UPDATE одной из транзакций с сообщением об ошибке. Вторая транзакция при этом продолжает оставаться заблокированной. Транзакция, в которой был выполнен откат оператора, теперь должна явным образом завершиться (зафиксироваться или откатиться), только после этого будет разблокирована другая транзакция.

5.4.5 Эскалация блокировок

Поскольку каждый доступ к строке таблицы включает в себя проверку наложенных на строку блокировок, большое число блокировок может привести к замедлению доступа. В DB2 при достижении числом блокировок некоторого предустановленного порога (он задается в параметрах базы данных) происходит эскалация блокировок, наложение блокировок на более крупный объект. Так, если блокируется большое число строк в таблице, то эти блокировки переводятся в блокировку всей таблицы. Эскалация блокировок повышает эффективность выполнения приложения, но влечет за собой увеличение числа блокировок в параллельно выполняющихся транзакциях.
В Oracle нет концепции эскалации блокировок, так как Oracle, исходит из предположения, что большого числа блокировок быть не должно. Но нехватка памяти для размещения списка блокировок приводит к блокировке всего приложения.

Сценарии возникновения нежелательных эффектов (потерянные изменения, грязное чтение...)



Проиллюстрируем нежелательные эффекты, возникающие при параллельном выполнении транзакций на примере таблицы EXAMPLE. Мы предполагаем, что в начале выполнения каждого следующего пункта содержимое этой таблицы возвращается к исходному, а именно:
Таблица EXAMPLE
id INTEGERdat INTEGER
1100
2110
3120
4130

5.3.1 Потерянные изменения

Транзакция T1ШагТранзакция T2
UPDATE example SET dat=dat+1 WHERE id=11 
 2UPDATE example SET dat=dat+1 WHERE id=1
COMMIT3 
 4COMMIT
SELECT * FROM example5 
Если потерянные изменения допускаются, то сценарий выполнится без ошибок и блокировок. На шаге 5 будет выбрано:
        id         dat 
---------- -----------
         1         101
         2         110
         3         120
         4         130

5.3.2 Грязное чтение

Транзакция T1ШагТранзакция T2
 1UPDATE example SET dat=101 WHERE id=1
UPDATE example SET dat= (SELECT dat FROM example WHERE id=1) WHERE id=22 
SELECT * FROM example3 
 4ROLLBACK
 5SELECT * FROM example
Если грязное чтение допускается, то сценарий выполнится без ошибок и блокировок.
На шаге 3 будет выбрано:
        id         dat 
---------- -----------
         1         101
         2         101
         3         120
         4         130

На шаге 5 будет выбрано:
        id         dat 
---------- -----------
         1         100
         2         101
         3         120
         4         130

5.3.3 Неповторяющееся чтение

Транзакция T1ШагТранзакция T2
SELECT * FROM example WHERE id=11 
[COMMIT]2UPDATE example SET dat=101 WHERE id=1
 3COMMIT
SELECT * FROM example WHERE id=14 
COMMIT5 
Если неповторяющееся чтение допускается, то сценарий выполнится без ошибок и блокировок. Операцию COMMIT в транзакции T1 на шаге 2 выполнять не придется.
На шаге 1 будет выбрано:
        id         dat 
---------- -----------
         1         100
На шаге 4 будет выбрано:
        id         dat 
---------- -----------
         1         101
Если выполнить операцию COMMIT на шаге 2, то результаты будут те же, что и без нее, но здесь уже не будет эффекта неповторяющегося чтения, так как разные результаты будут прочитаны уже в разных транзакциях.

5.3.4 Фантом

Транзакция T1ШагТранзакция T2
SELECT * FROM example WHERE dat>1101 
[COMMIT]2INSERT INTO example VALUES(5,140)
 3COMMIT
SELECT * FROM example WHERE dat>1104 
COMMIT5 
Если допускаются фантомы, то сценарий выполнится без ошибок и блокировок. Операцию COMMIT в транзакции T1 на шаге 2 выполнять не придется.
На шаге 1 будет выбрано:
        id         dat 
---------- -----------
         3         120
         4         130

На шаге 4 будет выбрано:
        id         dat 
---------- -----------
         3         120
         4         130
         5         150

Понятие транзакции и операторы COMMIT и ROLLBACK



Транзакцией называется единица работы СУБД, то есть, такая последовательность операторов SQL, которая обрабатывается СУБД как единое целое. Транзакция характеризуется четырьмя основными свойствами, часто называемыми свойствами ACID:
  • атомарность (atomity) - транзакция является неделимой, она выполняется полностью или не выполняется вообще; если транзакция прерывается на середине, то база данных должна остаться в том состоянии, которое она имела до начала транзакции;
  • параллельность (concurrency) - эффект от параллельного выполнения нескольких транзакций должен быть таким же, как от их последовательного выполнения; выполняющиеся транзакции не должны накладываться друг на друга;
  • целостность (integrity) - транзакция переводит база данных из одного непротиворечивого (целостного) состояния в другое; в ходе выполнения транзакции база данных может временно пребывать в нецелостном состоянии;
  • долговременность (duration) - после того, как транзакция завершена и зафиксирована, результат ее выполнения гарантированно сохраняется в базе данных.
Объем транзакции может варьироваться от одного SQL-оператора до всех действий с базой данных, выполняемых приложением. В случае, если транзакции в приложении не определены явным образом, поведение приложения в этом отношении определяется состоянием режима AUTOCOMMIT. Когда этот режим выключен, все приложение составляет одну транзакцию (если в нем не задано явное управление транзакциями), которая завершается с завершением приложения. Когда же этот режим включен, каждый SQL-оператор в приложении выполняется как отдельная транзакция, даже если в приложении имеются операторы явного управления транзакциями. Оператор является минимальной единицей транзакции: некоторые операторы могут включать в себя сложные действия над множеством строк, но все эти операции СУБД выполняет как одну транзакцию.
При выключенном режиме AUTOCOMMIT приложение может само управлять разбиением выполняемых им действий на транзакции. Первый SQL-оператор, выполняемый в приложении, начинает новую транзакцию. Все последующие операторы продолжают эту транзакцию, пока не встретится оператор COMMIT или ROLLBACK.
Оператор фиксации - COMMIT - завершает текущую транзакцию и фиксирует ее результаты. После выполнения оператора COMMIT результаты транзакции гарантированно сохраняются в базе данных и начинается новая транзакция. Оператор отката - ROLLBACK - завершает текущую транзакцию "с откатом". После выполнения этого оператора восстанавливается то состояние базы данных, в котором она была перед началом транзакции, и начинается новая транзакция. При выполнении оператора COMMIT или ROLLBACK снимаются все наложенные в транзакции блокировки (о блокировках - см. ниже).
Стандартом SQL/92 не предусматриваются какие-либо дополнительные возможности операторов COMMIT и ROLLBACK, поэтому их стандартный синтаксис очень прост (см. рис.5.1).

Рисунок 5.1 - Операторы COMMIT и ROLLBACK
Обе наши СУБД предусматривают, так называемые, точки сохранения. Точка сохранения задается оператором SAVEPOINT, и в операторе ROLLBACK имеется возможность отката транзакции не к началу, а к указанной точке сохранения.
С учетом этой возможности синтаксис операторов SAVEPOINT и ROLLBACK в Oracle показан на рис. 5.2, а в DB2 (это новая возможность версии 7.1.) - на рис. 5.3.

Рисунок 5.2 - Операторы ROLLBACK и SAVEPOINT в Oracle

Рисунок 5.3 - Операторы ROLLBACK и SAVEPOINT в DB2
В Oracle откат транзакции к указанной точке сохранения безусловно снимает все блокировки, наложенные после точки сохранения. В DB2 эта возможность является выборочной, задаваемой при создании точки сохранения. Имена точек сохранения могут повторяться в транзакции (в DB2 может быть указано требование уникальности имени) если имена повторяются, то выполнение следующего оператора SAVEPOINT с тем же именем точки отменяет предыдущую точку сохранения.
Выше мы отметили, что внутри транзакции состояние базы данных в принципе может быть нецелостным. В Oracle в описании ограничений целостности может быть задано ключевое слово DEFERRED (отсроченный) - для ограничения, проверка которого откладывается до окончания транзакции. Отключение ограничений может выполняться также оператором SET CONSTRAINT, что соответствует стандарту SQL/92. В DB2 отключения ограничений целостности могут быть сделаны только явным образом - оператором SET INTEGRITY. Подробное описание этих возможностей содержится в [7, 9].

Уровни изоляции


Свойство параллельности является одним из наиболее важных свойств транзакций, которые обеспечивают промышленные базы данных. Современные промышленные СУБД обеспечивают параллельную работу с одними и теми же данными огромного количества пользователей. Так, СУБД DB2 была протестирована при одновременной работе 60 тыс. пользователей; точных данных по Oracle у нас нет, но и здесь речь идет о десятках тысяч пользователей. В таких условиях важно, чтобы параллельно выполняемые транзакции не накладывались, а были изолированы друг от друга.

Стандарт SQL/92 определяет уровни изоляции транзакций в многопользовательской системе через отсутствие таких аномалий доступа к базе данных, которые могут в конечном итоге угрожать целостности данных. В стандарте различаются следующие аномалии:
  • Потерянные изменения. Транзакция Т1 читает данные. Транзакция Т2 читает те же данные. Транзакция T1 на основании прочитанного значения вычисляет новое значение данных, записывает его в базу данных и завершается. Транзакция T2 на основании прочитанного значения вычисляет новое значение данных, записывает его в базу данных и завершается. В результате значение, записанное транзакцией Т2, "затрет" значение, записанное транзакцией Т1.
  • Грязное чтение. Транзакция Т1 изменяет некоторые данные, но еще не завершается. Транзакция Т2 читает эти же данные (с изменениями, внесенными транзакцией Т1) и принимает на их основе какие-то решения. Транзакция Т1 выполняет откат. В результате решение, принятое транзакцией Т2, основано на неверных данных.
  • Неповторяющееся чтение. Транзакция Т1 в ходе своего выполнения несколько раз читает одни и те же данные. Транзакция Т2 в интервалах между чтениями данных в транзакции Т1 изменяет эти данные и фиксируется. В результате оказывается, что чтения одних и тех же данных в транзакции Т1 дают разные результаты.
  • Фантом. Транзакция Т1 в ходе своего выполнения несколько раз выбирает множество строк по одним и тем же критериям. Транзакция Т2 в интервалах между выборками транзакции Т1 добавляет или удаляет строки или изменяет столбцы некоторых строк, используемых в критерии выборки, и фиксируется. В результате оказывается, что одинаковые запросы в транзакции Т1 выбирают разные множество строк.
Промышленные СУБД в том или ином объеме выполняют требования стандарта по дифференциации уровней изоляции, но при формально одном и том же уровне изоляции поведение транзакций может существенно различаться в разных СУБД.
Определение уровней изоляции в стандарте и в рассматриваемых нами СУБД сведено в таблицу:
Уровни изоляции SQL/92АномалииDB2Oracle
А1А2А3А4
READ UNCOMMITTEDнетдададаUNCOMMITTED READ (UR)-
READ COMMITTEDнетнетдадаCURSOR STABILITY (CS)READ COMMITTED
REPEATABLE READнетнетнетдаREAD STABILITY (RS)-
SERIALIZABLEнетнетнетнетREPEATABLE READ (RR)SERIALIZABLE
Аномалии:
    А1 - Потерянные изменения
    А3 - Неповторяющееся чтение
    А2 - Грязное чтение
    А4 - Фантом
Кроме названных, в Oracle имеется еще уровень изоляции READ ONLY - для только-читающих транзакций.
В Oracle требуемый уровень изоляции может быть установлен для сеанса соединения с базой данных - как одна из возможностей оператора ALTER SESSION или для отдельной транзакции оператором SET TRANSACTION. Синтаксис этих операторов показан на рис. 5.4.

Рисунок 5.4 - Операторы ALTER SESSION и SET TRANSACTION (Oracle)
В DB2 уровень изоляции устанавливается для приложения, и способы его установки различны для разных способов разработки приложений. При работе в среде DB2 Command Line Processor или DB2 Command Center уровень изоляции устанавливается перед соединением с базой данных оператором CHANGE ISOLATION:

Рисунок 5.5 - Операторы CHANGE ISOLATION (DB2)

пʼятниця, 15 травня 2015 р.

Alexander Ryndin: Использование GoldenGate Director для управления интеграцией


Использование GoldenGate Director для управления интеграцией

АРХИТЕКТУРА GOLDENGATE DIRECTOR

GoldenGate Director (сейчас это называется GoldenGate Management Pack) — это многозвенное клиент-серверное приложение, обеспечивающее возможность конфигурирования и управления экземплярами(instances) GoldenGate с удаленного рабочего места. GoldenGate Director состоит из следующих компонент:
image
Экземпляры GoldenGate
Каждый экземпляр процесса GoldenGate Manager — индентифицируется полным именем сервера, портом, на котором слушает Manager и пользовательским именем источника данных. Поскольку процесс GoldenGate Manager связан с базой данных, эта комбинация определяется как источник данных в клиенте GoldenGate Director.
GoldenGate Director Server
GoldenGate Director Server координирует управление экземплярами GoldenGate. GoldenGate Director Server инсталлируется как домен в Oracle Weblogic Server и состоит из следующих приложений:
  • GoldenGate Director Server  — набор сервисов, управляющих безопасностью, информацией о сервисах, объектной моделью, консолидированным журналированием событий и слежбами уведомления;
  • Monitor Agent — клиент для серверов GoldenGate, который устанавливает выделенное соединение с помощью GGSCI. Соединение используется, чтобы получить информацию о статусе процессов и событиях.
Director Database
GoldenGate Director Server использует базу данных как центральный репозиторий для хранения информации о пользователях и группах, графических диаграмм, созданных пользователями, консолидированных событий и другой информации. Пользователь может использовать клиента, проинсталлированного на любом компьютере, но видеть одну и ту же информацию.
Клиенты GoldenGate
  • GoldenGate Director Client — это клиентское приложение для GoldenGate Director Server, обеспечивающее GUI интерфейс для управления экземплярами GoldenGate. Клиент может быть запущен на любой платформе, которая поддерживает Java.
  • GoldenGate Director Web — это тонкий клиент. Обеспечивает средства контроля за экземплярами GoldenGate и простейшего управления.
  • GoldenGate Director Administrator — средство управления метаданными GoldenGate Director Server. Этот инструмент не управляет процессами GoldenGate, но позволяет настроить параметры для подключения к экземплярам, а также управлять пользователями пользователей.

ИНСТАЛЛЯЦИЯ GOLDENGATE АГЕНТОВ

См.  статью  Использование GoldenGate для live reporting. Читать то заголовка «Настраиваем процесс сбора изменений»

ИНСТАЛЛЯЦИЯ GOLDENGATE DIRECTOR

Перед инсталляцией необходимо иметь проинсталлированным следующее ПО:
  • JRE 6 (1.6.x)
  • Oracle Weblogic Server 11g (10.3.1) Standard Edition
  • База данных (MySQL 5.x EE, SQL Server 2000 или 2005, Oracle 9i
Дальше я останавливаюсь на инсталляции GoldenGate Director на Oracle Database.
Создание пользователя
CREATE USER ggDirector IDENTIFIED BY passw0rd DEFAULT TABLESPACE users;
ALTER USER ggDirector QUOTA UNLIMITED ON users;
GRANT connect,resource TO ggDirector;
Инсталляция
  1. Скачиваем дистрибутив с http://edelivery.oracle.com из раздела Fusion Middleware
  2. Запускаем инсталляцию ggdirector-serversetup_<version>
  3. Welcome screen: Нажимаем Next.
  4. Choose Installation Location: Вводим каталог, в который инсталлируем Director
  5. Weblogic Location: Вводим путь к каталогу, который на один уровень выше wlserver_10.3.1 (по-умолчанию это каталог Middleware). Этот каталог используется для поиска пути к каталогу с доменами.
  6. HTTP port: вводим порт, который будет использоваться. По умолчанию используется порт 7001.
  7. Database: Указывается тип базы данных.
  8. Database driver configuration: Прописываем информацию необходимую для подключения к базе данных.
  9. Database User: Указываем имя пользователя и пароль для создания репозитория.
  10. Pre-installation summary: Жмем Next.
  11. Затем жмем Finish.
Запуск и останов GoldenGate Director.
ДействиеWindowsUnix и Linux
Запускdomain\startWebLogic.cmddomain/startWebLogic.sh
Остановdomain\bin\stopWebLogic.cmddomain/bin/stopWebLogic.sh
Подключение к GoldenGate Director

НАСТРОЙКА GOLDENGATE DIRECTOR

Для того, чтобы управлять инфраструктурой с помощью GoldenGate Director необходимо настроить подключения к каждому установленному GoldenGate Manager. Кроме того, необходимо настроить пользователей, которые будут использоваться при управлении GoldenGate Director.
Для этих целей используется инструмент GoldenGate Director Administrator. Проинсталлировать его можно перейдя по ссылке :/download»>http://<servername>:<port>/download. После запуска мы получаем окно входа в систему:
image
Имя и пароль по умолчанию — admin. После первого запуска рекомендуется сменить этот пароль. Имя сервера необходимо вводить вместе с  номером порта, на котором слушает weblogic (по-умолчанию 7001).
Учетные записи мы сейчас трогать не будет, поэтому сразу перейдем на вторую закладку, на которой регистрируются источники данных:
image
На этой закладке для каждой базы данных, для которой будет производиться  репликация. На этой закладке более менее все понятно
image
После того, как все настроено можно перейти на закладку Monitor Agent и попробовать перезапустить агентов.

НАСТРОЙКА РЕПЛИКАЦИИ

После настройки источников данных мы запускаем толстый клиент Oracle GoldenGate-Director. Жмем кнопку Login и вводим того же пользователя admin, что и ранее.
image
Создаем новую диаграмму, на которую перетаскиваем нужные источники данных:
image
Далее в простейшем случае мы можем перетащить с закладки Add new действие Capture and Delivery на источник. Необходимые действия на настройки репликации.
image

МОНИТОРИНГ РАБОТЫ ПРОЦЕССОВ GOLDENGATE

Для мониторинга сервисов можно использовать как толстый клиент, так и веб-клиент, расположенный по адресу :/acon»>http://<servername>:<port>/acon
image

ЗАКЛЮЧЕНИЕ

Инструмент GoldenGate Director — это мощное, но достаточно простое в использовании средство настройки репликации данных, а также готовое средство мониторинга процессов, участвующих в передаче данных.
Вследствие своей архитектуры GoldenGate Director обеспечивает единый взгляд на процессы репликации для всех пользователей вне зависимости от того, с какого сервера произведен вход в систему.
Для одних задач удобно применять веб-клиент, а для других — удобнее толстый клиент. Кроме того, GoldenGate обеспечивает инфраструктуру для настройки уведомления администратора при возникновении заданных событий.

Installing GoldenGate Director Server and Client


Oracle GoldenGate – Installing GoldenGate Director Server and Client

GoldenGate Director is a multi tier client server application that enables the configuration and management of the GoldenGate environment from a remote client which includes a web browser based client.
There are a number of different components which go to make up the GoldenGate Director product. Let us briefly describe each one of them.
GoldenGate Director Server – It is installed in a Weblogic server domain and enables the management of the different instances of GoldenGate which run in our environment.
GoldenGate Director Database – it is the central repository which is housed in a database (SQL Server/MySQL/Oracle) which contains information about the users, graphical diagrams which are created and other information related to user preferences.
GoldenGate Director Client – it is a GUI tool for managing the GoldenGate instances and runs on any platform which supports Java providing a menu driven interface with standard drag and drop functionality.
GoldenGate Director Web – web application that is hosted in the Director Server which provided browser based access to the GoldenGate environment.
GoldenGate Director Administrator – another client of the Director Server which enables us to carry out admin tasks like creating and modifying the admin user accounts, creating and modifying data sources which can be then used in the Director Client.
Before installing the Director Server we need to ensure that the JRE version 1.6 is already installed on the platform where we are going to install Director Server and also that Oracle 11g Weblogic Server (10.3.1) is available and running.
The following files were used for installation on Red Hat Linux RHEL 5 ….
oepe11_wls1031_linux32.bin – Oracle 11g Weblogic Server 10.3.1.
V19134-01.zip – GoldenGate Director Server
V19136-01.zip – GoldenGate Director Client
Let us look at the screen shots of an Oracle Weblogic Server installation.
Director Server Installation
In addition to the JRE 1.6.x requirement and the providing the location of the Weblogic Server software installation, we need to ensure that a database user has been created in the database which is going to serve as a repository for the Director Server. This database user needs standard privileges to create, alter and drop tables and indexes in it’s own schema. We will provide details of this user account in the course of the GoldenGate Director Server installation.
[oracle@redhat346 bin]$ export PATH=/u01/oracle/jre1.6.0_18/bin:$PATH
[oracle@redhat346 bin]$ java -version
java version “1.6.0_18
Java(TM) SE Runtime Environment (build 1.6.0_18-b07)
Java HotSpot(TM) 64-Bit Server VM (build 16.0-b13, mixed mode)
[oracle@redhat346 ~]$ ./gg-director-serversetup_unix_v2_0_0_3_007.sh
Starting Installer …
Director Client Installation
[oracle@redhat346 ~]$ ./gg-director-clientsetup_unix_v2_0_0_3_007.sh
Starting Installer …

четвер, 16 квітня 2015 р.

Табличные функции в ORACLE



Табличные функции
(Table Functions)

Табличные функции используются для возврата PL/SQL-коллекций, которые имитируют таблицы. Они могут быть запрошены как обычные таблицы с помощью функцию TABLE во фразе FROM. Обычные табличные функции требуют, чтобы коллекции перед возвращением были полностью наполнены (населены). Так как коллекции хранятся в памяти, это может стать проблемой, поскольку на большие коллекции впустую тратится много памяти и времени в ожидании возвращения первой строки. Эти узкие возможности делают обычные табличные функции непригодными в случаях масштабных ETL-операций (ETL — Extraction Transformation Load — Извлечение-Преобразование-Загрузка). Обычные табличные функции требуют создания именованной строки и табличных типов как объектов базы данных.
-- Создание типов для функции.
DROP TYPE t_tf_tab;
DROP TYPE t_tf_row;

CREATE TYPE t_tf_row AS OBJECT (
  id           NUMBER,
  description  VARCHAR2(50)
);
/

CREATE TYPE t_tf_tab IS TABLE OF t_tf_row;
/

-- Build the table function itself.
CREATE OR REPLACE FUNCTION get_tab_tf (p_rows IN NUMBER) RETURN t_tf_tab AS
  l_tab  t_tf_tab := t_tf_tab();
BEGIN
  FOR i IN 1 .. p_rows LOOP
    l_tab.extend;
    l_tab(l_tab.last) := t_tf_row(i, 'Description for ' || i);
  END LOOP;

  RETURN l_tab;
END;
/

-- Тестирование функции.
SELECT *
FROM   TABLE(get_tab_tf(10))
ORDER BY id DESC;

        ID DESCRIPTION
---------- --------------------------------------------------
        10 Description for 10
         9 Description for 9
         8 Description for 8
         7 Description for 7
         6 Description for 6
         5 Description for 5
         4 Description for 4
         3 Description for 3
         2 Description for 2
         1 Description for 1

10 rows selected.

SQL>
Заметим, что в этом листинге строки перечислены в обратном порядке, поскольку запрос содержит фразу упорядоченности по убыванию.

Конвейерные табличные функции)
(Pipelined Table Functions

Конвейерная обработка отменяет надобность в создании огромных наборов, передавая строки по каналу из функции по мере их создания, сохраняя память и позволяя запустить последующую обработку еще до окончания генерации всех строк.
Конвейерные табличные функции включают фразу PIPELINED и используют вызов PIPE ROW, чтобы вытолкнуть строки из функции, как только они создадутся, вместо построения табличной коллекции. Заметим, что вызов RETURN пустой, поскольку нет никакой коллекции, возвращаемой из функции.
-- Построение конвейерной табличной функции.
CREATE OR REPLACE FUNCTION get_tab_ptf (p_rows IN NUMBER) RETURN t_tf_tab PIPELINED AS
BEGIN
  FOR i IN 1 .. p_rows LOOP
    PIPE ROW(t_tf_row(i, 'Description for ' || i));   
  END LOOP;

  RETURN;
END;
/

-- Тестирование 
SELECT *
FROM   TABLE(get_tab_ptf(10))
ORDER BY id DESC;

        ID DESCRIPTION
---------- --------------------------------------------------
        10 Description for 10
         9 Description for 9
         8 Description for 8
         7 Description for 7
         6 Description for 6
         5 Description for 5
         4 Description for 4
         3 Description for 3
         2 Description for 2
         1 Description for 1

10 rows selected.

SQL>
Когда ETL-операции проводятся на большом хранилище данных, наблюдается существенное повышение производительности, поскольку загрузка данных из внешних таблиц производится табличными функциями непосредственно в таблицы хранилища, избегая промежуточного размещения данных.

Исключение NO_DATA_NEEDED
(NO_DATA_NEEDED Exception)

Конвейерная табличная функция может создать больше данных, чем необходимо запросившему её процессу. Когда такое происходит, конвейерная табличная функция останавливает выполнение, порождая исключение NO_DATA_NEEDED. Оно не должно явно обрабатываться, если только в процедуру не включен обработчик исключений OTHERS.
Приведенная ниже функция возвращает 10 строк, но запрос потребовал от нее только первые 5 строк. В этом случае функция прекращает выполнение, вызывая исключение NO_DATA_NEEDED.
-- Построение конвейерной табличной функции.
CREATE OR REPLACE FUNCTION get_tab_ptf (p_rows IN NUMBER) RETURN t_tf_tab PIPELINED AS
BEGIN
  FOR i IN 1 .. p_rows LOOP
    DBMS_OUTPUT.put_line('Row: ' || i);
    PIPE ROW(t_tf_row(i, 'Description for ' || i));
  END LOOP;

  RETURN;
END;
/

-- Тестирование 
SET SERVEROUTPUT ON

SELECT *
FROM   TABLE(get_tab_ptf(10))
WHERE  rownum <= 5;

        ID DESCRIPTION
---------- --------------------------------------------------
         1 Description for 1
         2 Description for 2
         3 Description for 3
         4 Description for 4
         5 Description for 5

5 rows selected.

Row: 1
Row: 2
Row: 3
Row: 4
Row: 5
SQL>
Если имеется обработчик исключений OTHERS, то он захватит исключение NO_DATA_NEEDED и выполнит некоторый код обработки ошибок, что не нужно.
-- Построение конвейерной табличной функции.
CREATE OR REPLACE FUNCTION get_tab_ptf (p_rows IN NUMBER) RETURN t_tf_tab PIPELINED AS
BEGIN
  FOR i IN 1 .. p_rows LOOP
    DBMS_OUTPUT.put_line('Row: ' || i);
    PIPE ROW(t_tf_row(i, 'Description for ' || i));
  END LOOP;

  RETURN;
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.put_line('OTHERS Handler');
    RAISE;
END;
/

-- Тестирование
SET SERVEROUTPUT ON

SELECT *
FROM   TABLE(get_tab_ptf(10))
WHERE  rownum <= 5;

        ID DESCRIPTION
---------- --------------------------------------------------
         1 Description for 1
         2 Description for 2
         3 Description for 3
         4 Description for 4
         5 Description for 5

5 rows selected.

Row: 1
Row: 2
Row: 3
Row: 4
Row: 5
OTHERS Handler
SQL>
Если вы планируете использовать обработчик исключений OTHERS, то для исключения NO_DATA_NEEDED необходимо задействовать специальное прерывание.
-- Построение конвейерной табличной функции.
CREATE OR REPLACE FUNCTION get_tab_ptf (p_rows IN NUMBER) RETURN t_tf_tab PIPELINED AS
BEGIN
  FOR i IN 1 .. p_rows LOOP
    DBMS_OUTPUT.put_line('Row: ' || i);
    PIPE ROW(t_tf_row(i, 'Description for ' || i));
  END LOOP;

  RETURN;
EXCEPTION
  WHEN NO_DATA_NEEDED THEN
    RAISE;
  WHEN OTHERS THEN
    DBMS_OUTPUT.put_line('OTHERS Handler');
    RAISE;
END;
/

-- Тестирование
SET SERVEROUTPUT ON

SELECT *
FROM   TABLE(get_tab_ptf(10))
WHERE  rownum <= 5;

        ID DESCRIPTION
---------- --------------------------------------------------
         1 Description for 1
         2 Description for 2
         3 Description for 3
         4 Description for 4
         5 Description for 5

5 rows selected.

Row: 1
Row: 2
Row: 3
Row: 4
Row: 5
SQL>
Исключение NO_DATA_NEEDED может быть также использовано для выполнения операций очистки (cleanup).
CREATE OR REPLACE FUNCTION get_tab_ptf (p_rows IN NUMBER) RETURN t_tf_tab PIPELINED AS
BEGIN
  my_package.initialize;

  FOR i IN 1 .. p_rows LOOP
    PIPE ROW(t_tf_row(i, 'Description for ' || i));
  END LOOP;

  RETURN;
EXCEPTION
  WHEN NO_DATA_NEEDED THEN
    my_package.cleanup;
    RAISE;
  WHEN OTHERS THEN
    DBMS_OUTPUT.put_line('OTHERS Handler');
    RAISE;
END;
/

Сравнение использования памяти
(Memory Usage Comparison)

Следующая функция возвращает текущее значение определенной статистики. Она позволит нам сравнивать память, используемую обычными и конвейерными табличными функциями.
CREATE OR REPLACE FUNCTION get_stat (p_stat IN VARCHAR2) RETURN NUMBER AS
  l_return  NUMBER;
BEGIN
  SELECT ms.value
  INTO   l_return
  FROM   v$mystat ms,
         v$statname sn
  WHERE  ms.statistic# = sn.statistic#
  AND    sn.name = p_stat;
  RETURN l_return;
END get_stat;
/
Сначала мы протестируем обычную табличную функцию, создав новое соединение и запросив большую коллекцию. Проверяя выделение памяти PGA как до, так и после, тест позволит нам увидеть, сколько памяти было выделено в результате проведения теста.
-- Создание новой сессии.
CONN test/test

-- Тестирование табличной функции.
SET SERVEROUTPUT ON
DECLARE
  l_start  NUMBER;
BEGIN
  l_start := get_stat('session pga memory');

  FOR cur_rec IN (SELECT *
                  FROM   TABLE(get_tab_tf(100000)))
  LOOP
    NULL;
  END LOOP;

  DBMS_OUTPUT.put_line('Regular table function : ' ||
                        (get_stat('session pga memory') - l_start));
END;
/
Regular table function : 22872064
[Обычная табличная функция: 22872064 ]

PL/SQL procedure successfully completed.

SQL>
Затем мы повторим тест для конвейерной табличной функции.
-- Создание новой сессии.
CONN test/test

-- Test pipelined table function.
SET SERVEROUTPUT ON
DECLARE
  l_start  NUMBER;
BEGIN
  l_start := get_stat('session pga memory');

  FOR cur_rec IN (SELECT *
                  FROM   TABLE(get_tab_ptf(100000)))
  LOOP
    NULL;
  END LOOP;

  DBMS_OUTPUT.put_line('Pipelined table function : ' ||
                        (get_stat('session pga memory') - l_start));
END;
/
Pipelined table function : 65536
[Конвейерная табличная функция: 65536 ]

PL/SQL procedure successfully completed.

SQL>
Сокращение памяти, используемой конвейерной табличной функцией, обусловлено тем, что она не требует разместить целую коллекцию в памяти.

Кардинальность
(Cardinality)

Oracle оценивает кардинальность (мощность, количество элементов) конвейерной табличной функции, базируясь на размере блока базы данных. Когда используется размер блока по умолчанию, оптимизатор всегда предполагает, что кардинальность - 8168 строк.
SET AUTOTRACE TRACE EXPLAIN

-- Возвращение 10 строк.
SELECT *
FROM   TABLE(get_tab_ptf(10));

Execution Plan
----------------------------------------------------------
Plan hash value: 822655197

-------------------------------------------------------------------------------------------------
| Id  | Operation                         | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |             |  8168 | 16336 |     8   (0)| 00:02:19 |
|   1 |  COLLECTION ITERATOR PICKLER FETCH| GET_TAB_PTF |  8168 | 16336 |     8   (0)| 00:02:19 |
-------------------------------------------------------------------------------------------------

SET AUTOTRACE OFF
Это прекрасно, если вы запрашиваете только конвейерную табличную функцию, но если планируется использовать ее в соединении, это может оказать негативное влияние на план выполнения.
Есть 4 способа, чтобы исправить оценку кардинальности конвейерной табличной функции:
  • хинт CARDINALITY (9i+): Недокументирован (Undocumented)
  • хинт OPT_ESTIMATE (10g+): Недокументирован (Undocumented)
  • хинт DYNAMIC_SAMPLING (11gR1+): Вызывает полное сканирование конвейерной табличной функции для оценки количества элементов в самом запросе перед его запуском. Это очень расточительно.
  • Расширенный Оптимизатор (Extensible Optimizer) (9i+): возможность расширения оптимизатора позволяет нам сообщить ему, что нужно поддерживать кардинальность.
  • Обратная связь по количеству элементов (Cardinality Feedback): в Oracle Database 11gR2 оптимизатор замечает, отличается ли фактическое количество элементов от ожидаемой кардинальности. Последующие запросы будут нести свою кардинальность, скорректированную на основе этой обратной связи. Если предложение выбирается из разделяемого пула или экземпляр стартует заново, эта обратная связь по количеству элементов теряется. В Oracle Database 12c обратная кардинальность по количеству элементов сохраняется в табличной области SYSAUX.
Чтобы использовать расширяемый оптимизатор, в конвейерные табличные функции нужно вручную добавить параметр, который укажет оптимизатору использовать кардинальность.
CREATE OR REPLACE FUNCTION get_tab_ptf (p_cardinality IN INTEGER DEFAULT 1)
  RETURN t_tf_tab PIPELINED AS
BEGIN
  FOR i IN 1 .. 10 LOOP
    PIPE ROW (t_tf_row(i, 'Description for ' || i));
  END LOOP;

  RETURN;
END;
/
Заметим, что параметр p_cardinality нигде не используется непосредственно в функции.
Затем мы строим тип и тело типа, чтобы установить кардинальность (количество элементов) вручную. Обратите внимание на ссылку на параметр p_cardinality в типе.
CREATE OR REPLACE TYPE t_ptf_stats AS OBJECT (
  dummy INTEGER,
  
  STATIC FUNCTION ODCIGetInterfaces (
    p_interfaces OUT SYS.ODCIObjectList
  ) RETURN NUMBER,

  STATIC FUNCTION ODCIStatsTableFunction (
    p_function    IN  SYS.ODCIFuncInfo,
    p_stats       OUT SYS.ODCITabFuncStats,
    p_args        IN  SYS.ODCIArgDescList,
    p_cardinality IN INTEGER
  ) RETURN NUMBER
);
/

CREATE OR REPLACE TYPE BODY t_ptf_stats AS
  STATIC FUNCTION ODCIGetInterfaces (
    p_interfaces OUT SYS.ODCIObjectList
  ) RETURN NUMBER IS
  BEGIN
    p_interfaces := SYS.ODCIObjectList(
                      SYS.ODCIObject ('SYS', 'ODCISTATS2')
                    );
    RETURN ODCIConst.success;
  END ODCIGetInterfaces;

  STATIC FUNCTION ODCIStatsTableFunction (
                    p_function    IN  SYS.ODCIFuncInfo,
                    p_stats       OUT SYS.ODCITabFuncStats,
                    p_args        IN  SYS.ODCIArgDescList,
                    p_cardinality IN INTEGER
                  ) RETURN NUMBER IS
  BEGIN
    p_stats := SYS.ODCITabFuncStats(NULL);
    p_stats.num_rows := p_cardinality;
    RETURN ODCIConst.success;
  END ODCIStatsTableFunction;
END;
/
Этот тип может быть связан с любой конвейерной табличной функцией, используя следующую команду.
ASSOCIATE STATISTICS WITH FUNCTIONS get_tab_ptf USING t_ptf_stats;
Мы знаем, что функция возвращает 10 строк, но оптимизатор этого не знает. Независимо от числа строк, возвращенных функцией, оптимизатор использует значение параметра p_cardinality как оценку количества элементов (кардинальности).
SET AUTOTRACE TRACE EXPLAIN

SELECT *
FROM   TABLE(get_tab_ptf(p_cardinality => 10));

Execution Plan
----------------------------------------------------------
Plan hash value: 822655197

-------------------------------------------------------------------------------------------------
| Id  | Operation                         | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |             |    10 |    20 |     8   (0)| 00:02:19 |
|   1 |  COLLECTION ITERATOR PICKLER FETCH| GET_TAB_PTF |    10 |    20 |     8   (0)| 00:02:19 |
-------------------------------------------------------------------------------------------------

SELECT *
FROM   TABLE(get_tab_ptf(p_cardinality => 10000));

Execution Plan
----------------------------------------------------------
Plan hash value: 822655197

-------------------------------------------------------------------------------------------------
| Id  | Operation                         | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |             | 10000 | 20000 |     8   (0)| 00:02:19 |
|   1 |  COLLECTION ITERATOR PICKLER FETCH| GET_TAB_PTF | 10000 | 20000 |     8   (0)| 00:02:19 |
-------------------------------------------------------------------------------------------------

SET AUTOTRACE OFF

Неявные (теневые) типы
(Implicit (Shadow) Types)

В отличие от обычных табличных функций, конвейерные табличные функции могут быть определены с использованием типов "table" и "record", определенных в спецификации пакета.
-- Удаление ранее созданных объектов.
DROP FUNCTION get_tab_tf;
DROP FUNCTION get_tab_ptf;
DROP TYPE t_tf_tab;
DROP TYPE t_tf_row;

-- Построение пакета, содержащего внутренние типы "record" и "table".
CREATE OR REPLACE PACKAGE ptf_api AS
  TYPE t_ptf_row IS RECORD (
    id           NUMBER,
    description  VARCHAR2(50)
  );

  TYPE t_ptf_tab IS TABLE OF t_ptf_row;

  FUNCTION get_tab_ptf (p_rows IN NUMBER) RETURN t_ptf_tab PIPELINED;
END;
/

CREATE OR REPLACE PACKAGE BODY ptf_api AS

  FUNCTION get_tab_ptf (p_rows IN NUMBER) RETURN t_ptf_tab PIPELINED IS
    l_row  t_ptf_row;
  BEGIN
    FOR i IN 1 .. p_rows LOOP
      l_row.id := i;
      l_row.description := 'Description for ' || i;
      PIPE ROW (l_row);
    END LOOP;
  
    RETURN;
  END;
END;
/

SELECT *
FROM   TABLE(ptf_api.get_tab_ptf(10))
ORDER BY id DESC;

        ID DESCRIPTION
---------- --------------------------------------------------
        10 Description for 10
         9 Description for 9
         8 Description for 8
         7 Description for 7
         6 Description for 6
         5 Description for 5
         4 Description for 4
         3 Description for 3
         2 Description for 2
         1 Description for 1

10 rows selected.

SQL>
Это представляется более правильным решением, чем построение всех типов базы данных вручную, а Oracle по умолчанию строит теневые объектные типы неявно.
COLUMN object_name FORMAT A30

SELECT object_name, object_type
FROM   user_objects;

OBJECT_NAME                    OBJECT_TYPE
------------------------------ -------------------
PTF_API                        PACKAGE BODY
SYS_PLSQL_82554_9_1            TYPE
SYS_PLSQL_82554_DUMMY_1        TYPE
SYS_PLSQL_82554_24_1           TYPE
PTF_API                        PACKAGE

5 rows selected.

SQL>
Как можно видеть, Oracle фактически создал три теневых объектных типа с системно сгенерированными именами для поддержки типов, требуемых конвейерной табличной функцией. По этой причине я всегда строю именованные объектные типы базы данных, вместо того, чтобы полагаться на неявные типы.

Конвейерные табличные функции, запускаемые параллельно
(Parallel Enabled Pipelined Table Functions)

Чтобы включить параллельные конвейерные табличные функции, должны быть выполнены следующие условия.
  • Должна быть включена фраза PARALLEL_ENABLE.
  • Должен присутствовать один или более входных параметров REF CURSOR.
  • Должна иметь место фраза PARTITION BY, чтобы указать метод секционирования рабочей нагрузки. Слабо связанные ref-курсоры (weakly typed ref cursors) могут использовать только фразу PARTITION BY ANY, которая определяет секционирование рабочей нагрузки случайный образом.
Основной синтаксис показан ниже.
CREATE FUNCTION function-name(parameter-name ref-cursor-type)
  RETURN rec_tab_type PIPELINED
  PARALLEL_ENABLE(PARTITION parameter-name BY [{HASH | RANGE} (column-list) | ANY ]) IS
BEGIN
  ...
END;
Чтобы увидеть это в действии, сначала надо создать и населить тестовую таблицу.
CREATE TABLE parallel_test (
  id           NUMBER(10),
  country_code VARCHAR2(5),
  description  VARCHAR2(50)
);

INSERT /*+ APPEND */ INTO parallel_test
SELECT level AS id,
       (CASE TRUNC(MOD(level, 4))
         WHEN 1 THEN 'IN'
         WHEN 2 THEN 'UK'
         ELSE 'US'
        END) AS country_code,
       'Description or ' || level AS description
FROM   dual
CONNECT BY level <= 100000;
COMMIT;

-- Проверка данных.
SELECT country_code, count(*) FROM parallel_test GROUP BY country_code;

COUNT   COUNT(*)
----- ----------
US         50000
IN         25000
UK         25000

3 rows selected.

SQL>
Следующий пакет определяет включенные параллельные конвейерные табличные функции, которые принимают ref-курсоры по запросу из тестовой таблицы и возвращают те же самые строки вместе с SID (системный идентификатор) сессии, которая их обработала. Можно было бы использовать слабо связанный ref-курсор, подобный SYS_REFCURSOR, но тогда мы были бы ограничены только методом секционированияSYS_REFCURSOR. Следующие три функции представляют три метода секционирования.
CREATE OR REPLACE PACKAGE parallel_ptf_api AS

  TYPE t_parallel_test_row IS RECORD (
    id             NUMBER(10),
    country_code   VARCHAR2(5),
    description    VARCHAR2(50),
    sid            NUMBER
  );

  TYPE t_parallel_test_tab IS TABLE OF t_parallel_test_row;

  TYPE t_parallel_test_ref_cursor IS REF CURSOR RETURN parallel_test%ROWTYPE;
  
  FUNCTION test_ptf_any (p_cursor  IN  t_parallel_test_ref_cursor)
    RETURN t_parallel_test_tab PIPELINED
    PARALLEL_ENABLE(PARTITION p_cursor BY ANY);
    
  FUNCTION test_ptf_hash (p_cursor  IN  t_parallel_test_ref_cursor)
    RETURN t_parallel_test_tab PIPELINED
    PARALLEL_ENABLE(PARTITION p_cursor BY HASH (country_code));
    
  FUNCTION test_ptf_range (p_cursor  IN  t_parallel_test_ref_cursor)
    RETURN t_parallel_test_tab PIPELINED
    PARALLEL_ENABLE(PARTITION p_cursor BY RANGE (country_code));
    
END parallel_ptf_api;
/

CREATE OR REPLACE PACKAGE BODY parallel_ptf_api AS

  FUNCTION test_ptf_any (p_cursor  IN  t_parallel_test_ref_cursor)
    RETURN t_parallel_test_tab PIPELINED
    PARALLEL_ENABLE(PARTITION p_cursor BY ANY)
  IS
    l_row  t_parallel_test_row;
  BEGIN
    LOOP
      FETCH p_cursor
      INTO  l_row.id,
            l_row.country_code,
            l_row.description;
      EXIT WHEN p_cursor%NOTFOUND;
      
      SELECT sid
      INTO   l_row.sid
      FROM   v$mystat
      WHERE  rownum = 1;
      
      PIPE ROW (l_row);
    END LOOP;
    RETURN;
  END test_ptf_any;

  FUNCTION test_ptf_hash (p_cursor  IN  t_parallel_test_ref_cursor)
    RETURN t_parallel_test_tab PIPELINED
    PARALLEL_ENABLE(PARTITION p_cursor BY HASH (country_code))
  IS
    l_row  t_parallel_test_row;
  BEGIN
    LOOP
      FETCH p_cursor
      INTO  l_row.id,
            l_row.country_code,
            l_row.description;
      EXIT WHEN p_cursor%NOTFOUND;
      
      SELECT sid
      INTO   l_row.sid
      FROM   v$mystat
      WHERE  rownum = 1;
      
      PIPE ROW (l_row);
    END LOOP;
    RETURN;
  END test_ptf_hash;

  FUNCTION test_ptf_range (p_cursor  IN  t_parallel_test_ref_cursor)
    RETURN t_parallel_test_tab PIPELINED
    PARALLEL_ENABLE(PARTITION p_cursor BY RANGE (country_code))
  IS
    l_row  t_parallel_test_row;
  BEGIN
    LOOP
      FETCH p_cursor
      INTO  l_row.id,
            l_row.country_code,
            l_row.description;
      EXIT WHEN p_cursor%NOTFOUND;
      
      SELECT sid
      INTO   l_row.sid
      FROM   v$mystat
      WHERE  rownum = 1;
      
      PIPE ROW (l_row);
    END LOOP;
    RETURN;
  END test_ptf_range;
      
END parallel_ptf_api;
/
Следующий запрос использует функцию CURSOR, чтобы преобразовать запрос к тестовой таблице в ref-курсор, который передан табличной функции в качестве параметра. Результаты группируются по SID сессий, которые обрабатывают строки. Отметим, что все строки обработаны одной и той же сессией. Почему? Потому что, хотя эта функция включена как параллельная, мы не указали ей работать параллельно.
SELECT sid, count(*)
FROM   TABLE(parallel_ptf_api.test_ptf_any(CURSOR(SELECT * FROM parallel_test t1))) t2
GROUP BY sid;

       SID   COUNT(*)
---------- ----------
        31     100000

1 row selected.

SQL>
Следующие запросы включают хинт параллельности и вызывают все эти функции.
SELECT country_code, sid, count(*)
FROM   TABLE(parallel_ptf_api.test_ptf_any(CURSOR
        (SELECT /*+ parallel(t1, 5) */  * FROM   parallel_test t1))) t2
GROUP BY country_code,sid
ORDER BY country_code,sid;

COUNT        SID   COUNT(*)
----- ---------- ----------
IN            23       4906
IN            26       5219
IN            41       4847
IN            42       4827
IN            43       5201
UK            23       4906
UK            26       5218
UK            41       4848
UK            42       4826
UK            43       5202
US            23       9811
US            26      10437
US            41       9695
US            42       9655
US            43      10402

15 rows selected.

SQL>

SELECT country_code, sid, count(*)
FROM   TABLE(parallel_ptf_api.test_ptf_hash(CURSOR
        (SELECT /*+ parallel(t1, 5) */  * FROM   parallel_test t1))) t2
GROUP BY country_code,sid
ORDER BY country_code,sid;

COUNT        SID   COUNT(*)
----- ---------- ----------
IN            29      25000
UK            38      25000
US            40      50000

3 rows selected.

SQL>

SELECT country_code, sid, count(*)
FROM   TABLE(parallel_ptf_api.test_ptf_range(CURSOR
        (SELECT /*+ parallel(t1, 5) */ * FROM   parallel_test t1))) t2
GROUP BY country_code,sid
ORDER BY country_code,sid;

COUNT        SID   COUNT(*)
----- ---------- ----------
IN            40      25000
UK            23      25000
US            41      50000

3 rows selected.

SQL>
Уровень параллелизма (DOP — degree of parallelism) может быть ниже чем тот, который указан в хинте.
Дополнительная фраза управления выходным потоком быть использована для упорядочения или кластеризации (объединение в группы) —order or cluster — данных, основанной на списке столбцов, в процессе серверной обработки. Это может быть необходимым, если существуют зависимости в данных. Например, нужно секционировать по определенному столбцу, но также и требовать, чтобы строки были обработаны в определенном порядке в рамках этого же секционирования. Расширенный синтаксис такого случая показан ниже.
CREATE FUNCTION function-name(parameter-name ref-cursor-type)
  RETURN rec_tab_type PIPELINED
  PARALLEL_ENABLE(PARTITION parameter-name BY [{HASH | RANGE} (column-list) | ANY ]) 
  [ORDER | CLUSTER] parameter-name BY (column-list) IS
BEGIN
  ...
END;
Можно сделать нечто подобное:
FUNCTION test_ptf_hash (p_cursor  IN  t_parallel_test_ref_cursor)
  RETURN t_parallel_test_tab PIPELINED
  PARALLEL_ENABLE(PARTITION p_cursor BY HASH (country_code))
  ORDER p_cursor BY (country_code, created_date);

FUNCTION test_ptf_hash (p_cursor  IN  t_parallel_test_ref_cursor)
  RETURN t_parallel_test_tab PIPELINED
  PARALLEL_ENABLE(PARTITION p_cursor BY HASH (country_code))
  CLUSTER p_cursor BY (country_code, created_date);

Трансформация конвейеров
(Transformation Pipelines)

В традиционных ETL-процессах необходимо сначала загрузить данные в промежуточную область, затем сделать по ней несколько проходов, чтобы преобразовать и переместить данные в область, откуда они будут загружены в схему назначения. Прохождение данных через промежуточные таблицы может потребовать значительного количества операций дискового ввода/вывода, как для загружаемых данных, так и для данных redo-журнала. Альтернативой должно стать выполнение преобразования конвейерными табличными функциями, поскольку данные читаются из внешней таблицы и вставляются непосредственно в таблицу назначения, сокращая большую часть операций дискового ввода/вывода.
В этой секции мы увидим и проэкзаменуем с использованием обсуждавшихся ранее методов трансформацию конвейера.
Сначала в виде плоского файла нужно выкачать из файловой системы сервера базы данных какие-либо тестовые данные.
SET PAGESIZE 0
SET FEEDBACK OFF
SET LINESIZE 1000
SET TRIMSPOOL ON
SPOOL /tmp/tp_test.txt
SELECT owner || ',' || object_name || ',' || object_type || ',' || status
FROM   all_objects;
SPOOL OFF
SET FEEDBACK ON
SET PAGESIZE 24
Создаем объект "directory", где указывается местоположение этого файла, создаем внешнюю таблицу, чтобы прочитать файл, и создаем таблицу назначения.
-- Создание директории, указывающей на плоский файл. 
CONN / AS SYSDBA
CREATE OR REPLACE DIRECTORY data_load_dir AS '/tmp/';
GRANT READ, WRITE ON DIRECTORY data_load_dir TO test;

CONN test/test
-- Создание внешней таблицы.
DROP TABLE tp_test_ext;
CREATE TABLE tp_test_ext (
  owner                    VARCHAR2(30),
  object_name              VARCHAR2(30),
  object_type              VARCHAR2(19),
  status                   VARCHAR2(7)
)
ORGANIZATION EXTERNAL
(
  TYPE ORACLE_LOADER
  DEFAULT DIRECTORY data_load_dir
  ACCESS PARAMETERS
  (
    RECORDS DELIMITED BY NEWLINE
    BADFILE data_load_dir:'tp_test_%a_%p.bad'
    LOGFILE data_load_dir:'tp_test_%a_%p.log'
    FIELDS TERMINATED BY ','
    MISSING FIELD VALUES ARE NULL
    (
      owner                    CHAR(30),
      object_name              CHAR(30),
      object_type              CHAR(19),
      status                   CHAR(7)
    )
  )
  LOCATION ('tp_test.txt')
)
PARALLEL 10
REJECT LIMIT UNLIMITED
/

-- Создание таблицы как заключительного назначения для данных. 
CREATE TABLE tp_test (
  owner                    VARCHAR2(30),
  object_name              VARCHAR2(30),
  object_type              VARCHAR2(19),
  status                   VARCHAR2(7),
  extra_1                  NUMBER,
  extra_2                  NUMBER
);
Заметим, что в таблице назначения по сравнению с внешней таблицей есть два дополнительных столбца. Каждый из этих столбцов представляет шаг преобразования. Фактические преобразования в этом примере тривиальны, но следует представить, что они могут быть сложными и невыполнимыми одним SQL-предложением. Следовательно, имеет место потребность в табличных функциях.
Пакет ниже определяет два шага процесса преобразования и процедуры для его запуска.
CREATE OR REPLACE PACKAGE tp_api AS

  TYPE t_step_1_in_rc IS REF CURSOR RETURN tp_test_ext%ROWTYPE;
  
  TYPE t_step_1_out_row IS RECORD (
    owner                    VARCHAR2(30),
    object_name              VARCHAR2(30),
    object_type              VARCHAR2(19),
    status                   VARCHAR2(7),
    extra_1                  NUMBER
  );
  
  TYPE t_step_1_out_tab IS TABLE OF t_step_1_out_row;

  TYPE t_step_2_in_rc IS REF CURSOR RETURN t_step_1_out_row;

  TYPE t_step_2_out_tab IS TABLE OF tp_test%ROWTYPE;

  FUNCTION step_1 (p_cursor  IN  t_step_1_in_rc)
    RETURN t_step_1_out_tab PIPELINED
    PARALLEL_ENABLE(PARTITION p_cursor BY ANY);

  FUNCTION step_2 (p_cursor  IN  t_step_2_in_rc)
    RETURN t_step_2_out_tab PIPELINED
    PARALLEL_ENABLE(PARTITION p_cursor BY ANY);

  PROCEDURE load_data;

END tp_api;
/


CREATE OR REPLACE PACKAGE BODY tp_api AS

  FUNCTION step_1 (p_cursor  IN  t_step_1_in_rc)
    RETURN t_step_1_out_tab PIPELINED
    PARALLEL_ENABLE(PARTITION p_cursor BY ANY)
  IS
    l_row  t_step_1_out_row;
  BEGIN
    LOOP
      FETCH p_cursor
      INTO  l_row.owner,
            l_row.object_name,
            l_row.object_type,
            l_row.status;
      EXIT WHEN p_cursor%NOTFOUND;
      
      -- Do some work here.
      l_row.extra_1 := p_cursor%ROWCOUNT;
      PIPE ROW (l_row);
    END LOOP;
    RETURN;
  END step_1;


  FUNCTION step_2 (p_cursor  IN  t_step_2_in_rc)
    RETURN t_step_2_out_tab PIPELINED
    PARALLEL_ENABLE(PARTITION p_cursor BY ANY)
  IS
    l_row  tp_test%ROWTYPE;
  BEGIN
    LOOP
      FETCH p_cursor
      INTO  l_row.owner,
            l_row.object_name,
            l_row.object_type,
            l_row.status,
            l_row.extra_1;
      EXIT WHEN p_cursor%NOTFOUND;
      
      -- Do some work here.
      l_row.extra_2 := p_cursor%ROWCOUNT;
      PIPE ROW (l_row);
    END LOOP;
    RETURN;
  END step_2;


  PROCEDURE load_data IS
  BEGIN
    EXECUTE IMMEDIATE 'TRUNCATE TABLE tp_test';
    
    INSERT /*+ APPEND */ INTO tp_test
    SELECT *
    FROM   TABLE(step_2(CURSOR(SELECT *
                               FROM   TABLE(step_1(CURSOR(SELECT *
                                                          FROM   tp_test_ext t1
                                                          )
                                                   )
                                            ) t2
                               )
                        )
                 ) t3;
    COMMIT;
  END load_data;

END tp_api;
/
Вставка внутри процедуры LOAD_DATA полностью выполняет загрузку данных, включая преобразования. Предложение выглядит довольно сложно, но оно состоит из следующих простых шагов.
  • Строки запрашиваются из внешней таблицы.
  • Строки конвертируются в ref-курсор с помощью функции CURSOR.
  • Этот ref-курсор передается на первом этапе преобразования (STEP_1).
  • Возвращаемая на шаге STEP_1 коллекция запрашивается, когда используется функция TABLE.
  • Вывод этого запроса преобразуется в ref-курсор с помощью функции CURSOR.
  • Этот ref-курсор передается на второй этап преобразования (STEP_2).
  • Возвращаемая на шаге STEP_2 коллекция запрашивается, когда используется функция TABLE.
  • Этот запрос используется для выполнения вставки в результирующую таблицу.
Применяя процедуру LOAD_DATA, можно как преобразовывать, так и загружать данные.
EXEC tp_api.load_data;

PL/SQL procedure successfully completed.

SQL>

-- Проверка числа строк во внешней таблице.
SELECT COUNT(*) FROM tp_test_ext;

  COUNT(*)
----------
     56059

1 row selected.

SQL> 

-- Compare to the destination table.Сравнение с таблицей назначения.
SELECT COUNT(*) FROM tp_test;

  COUNT(*)
----------
     56059

1 row selected.

SQL>
Заметим, что этот пример не содержит процедуры обработки ошибок и что в нем нет хинтов параллельности, чтобы упростить запрос в процедуре LOAD_DATA.




==========================================================================================================================================

создавать типы лучше через SQL+ иначе создадутся в базе с уродливым названием


create table ane_test as select type_name from user_types where type_name like 'SYS_PLSQL%'
/
Table created

create or replace package abm_t1 as
type rc is record (
  n number
);
type tb is table of rc;
function f return tb pipelined;
end abm_t1;
/
Package created

create package body abm_t1 as
function f return tb pipelined as
  rec abm_t1.rc;
begin
    for i in 1..9 loop
        rec.n := i;
        pipe row(rec);
    end loop;
    return;
end f;
end abm_t1;
/
Package body created

select * from table(abm_t1.f);

         N
----------
         1
         2
         3
         4
         5
         6
         7
         8
         9

9 rows selected

select * from user_types
where type_name like 'SYS_PLSQL%'
  and type_name not in (select type_name from ane_test);

TYPE_NAME                      TYPE_OID                         TYPECODE                       ATTRIBUTES    METHODS PREDEFINED INCOMPLETE FINAL INSTANTIABLE SUPERTYPE_OWNER                SUPERTYPE_NAME                 LOCAL_ATTRIBUTES LOCAL_METHODS TYPEID
------------------------------ -------------------------------- ------------------------------ ---------- ---------- ---------- ---------- ----- ------------ ------------------------------ ------------------------------ ---------------- ------------- --------------------------------
SYS_PLSQL_2906526_17_1         123DB7CD8BB64B34E0440003BA0CA326 COLLECTION                              0          0 NO         NO         YES   YES                                                                                                       
SYS_PLSQL_2906526_9_1          123DB7CD8BAE4B34E0440003BA0CA326 OBJECT                                  1          0 NO         NO         YES   YES                                                                                                       
SYS_PLSQL_2906526_DUMMY_1      123DB7CD8BB24B34E0440003BA0CA326 COLLECTION                              0          0 NO         NO         YES   YES                                                                                                       

drop package abm_t1
/
Package dropped

select * from user_types
where type_name like 'SYS_PLSQL%'
  and type_name not in (select type_name from ane_test);

TYPE_NAME                      TYPE_OID                         TYPECODE                       ATTRIBUTES    METHODS PREDEFINED INCOMPLETE FINAL INSTANTIABLE SUPERTYPE_OWNER                SUPERTYPE_NAME                 LOCAL_ATTRIBUTES LOCAL_METHODS TYPEID
------------------------------ -------------------------------- ------------------------------ ---------- ---------- ---------- ---------- ----- ------------ ------------------------------ ------------------------------ ---------------- ------------- --------------------------------

drop table ane_test
/
Table dropped