Администратору Oracle: Раскрываем секреты - глубинный анализ журналов (Mining for Clues)Источник: Oracle Magazine
Использование утилиты Oracle LogMiner для восстановления транзакций и отмены изменений. Пол на другом конце провода был рассержен. В течение 20 лет он был персональным менеджером клиентки Эллен Смит. Но сегодня, когда Эллен пришла в банк, система сообщила ей, что Пол больше не является ее персональным менеджером! Сбитый с толку и обоснованно расстроенный, он требовал разъяснений. Джон, главный администратор базы данных банка, должен был действовать не теряя времени. Он быстро выполнил запрос к таблице ACCOUNTS (счета), который показал, что значение столбца ACC_MGR_ID (идентификатор персонального менеджера) для счета 4567 (счет Эллен) равно 1, тогда как идентификатор Пола - 5. Раньше это значение определенно было равно 5. Кто мог изменить его и почему? Кроме того, отдельный запрос показал, что Полу был назначен другой клиент (с номером счета 9876), которого Пол не признает. Почему это случилось, и кто действительный персональный менеджер счета 9876? "Рытье носом" в архивах Какие варианты имеет Джон для исправления ситуации? Сначала можно просмотреть на сервере базы данных Oracle журнальные данные. Заметим, сервер Oracle для каждого изменения, сделанного в базе данных, генерирует записи отката и повторного выполнения . Простыми словами, запись отката содержит значения данных, сохраненные перед изменением, а запись повторного выполнения содержит значения данных, сохраненные после изменения. Когда происходят изменения, записи повторного выполнения помещаются в журнальные буфера в системной глобальной области (SGA). Затем процесс записи в журнал LGWR записывает эти записи в оперативный журнал, и, если сервер базы данных работает в режиме архивирования (ARCHIVELOG), процесс архивирования ARC0 сохранит эти записи в архивном журнале. Назначение этих журнальных записей заключается в том, чтобы гарантировать (в случае повреждения базы данных) повторное выполнение сделанных ранее операций. В случае полной потери базы данных ее администратор может восстановить созданную ранее резервную копию базы данных и применить все архивные журнальные файлы, сгенерированные после создания этой копии, чтобы "накатить" эту копию на момент отказа. А как насчет записей отката? Согласно модели согласованности чтения в СУБД Oracle пользователь видит измененное значение только после фиксации этого изменения; в противном случае сервер базы данных возвращает предыдущие (неизменные) значения. Эти предыдущие значения извлекаются из сегментов отката. Кроме того, во время восстановления данных после их повреждения сервер базы данных Oracle для поддержки согласованности и целостности данных должен "откатить" все изменения, которые не были зафиксированы. Эти записи извлекаются из сегментов отката. Сегменты отката изменяются также как и другие сегменты базы данных, поэтому и для них генерируются журнальные записи, которые записываются в оперативный журнал, а затем и в архивный. В результате оперативный и архивный журналы содержат как информацию отката, так и повторного выполнения. Администраторы базы данных могут использовать их для извлечения как старых значений данных, так и новых. Однако формат оперативного и архивного журналов не опубликован и они не удобочитаемые. Для чтения и поиска в оперативном и архивном журналах администраторы баз данных используют утилиту Oracle LogMiner (глубинный анализ журнала). С этой утилитой можно работать с помощью поставляемого пакета DBMS_LOGMNR, появившегося в сервере Oracle8 i . В этой статье описано, как использовать этот мощный инструмент для решения простых и не так уж и простых проблем, возникающих при изменениях базы данных Oracle. Начинаем анализ Давайте вернемся к проблеме со счетом госпожи Смит. Джон может начать поиск, как и когда был изменен персональный менеджер ее счета, используя для этого ретроспективные запросы к таблице счетов, чтобы выбрать предшествующие значения. Технология выполнения ретроспективных операций ( Oracle Flashback ) - функциональные средства сервера Oracle, которые позволяют видеть значения столбцов в некоторый момент в прошлом, до их изменения и фиксации. Однако ретроспективные запросы работают только в интервале времени, задаваемом параметром UNDO_RETENTION (сохранение информации в пространстве отката), значение которого в OLTP-системах часто не превышает 30 минут. В таком случае ретроспективные запросы не найдут изменений. Джон может найти ответ на рассматриваемый вопрос с помощью утилиты LogMiner, просматривая непосредственно файлы оперативного журнала. Шаги для настройки и использования LogMiner:
GRANT EXECUTE_CATALOG_ROLE TO JOHN; CREATE PUBLIC SYNONYM DBMS_LOGMNR FOR SYS.DBMS_LOGMNR; Эти первые три шага нужно выполнить только один раз. На листинге 1 Джон видит, что пользователь по имени JOE (Джо) обновил эту таблицу в 14:16. Столбцы SID и SERIAL# идентифицируют сеанс, в котором работал этот пользователь.
Другой столбец этого представления, SESSION_INFO, содержит дополнительную информацию о сеансе пользователя Джо. Во всех выведенных строках значения столбца SESSION_INFO были следующими: login_username=JOE client_info= OS_username=jsmoe Machine_name=ACMENTNY\JSMOE OS_terminal= OS_process_id=4080:3096 OS_program name=sqlplus.exe По этой информации Джон узнает, что Джо обновил ряд записей таблицы ACCOUNTS с машины клиента по имени JSMOE, работавшей в сети ACMENTNY. Последняя порция информации, OS_program name=sqlplus.exe, также содержит важные сведения. Джо использовал утилиту SQL*Plus, поэтому он, вероятно, выполнил нерегламентированное обновление, но Джо - разработчик, ему запрещено обновлять промышленную базу данных, кроме случаев чрезвычайной ситуации. Почему он сделал эти изменения? В любом случае Джон должен знать, что обновил Джо, а также старые значения, хранимые перед обновлением. Для поиска этой информации Джон модифицирует запрос листинга 1, чтобы выбрать два очень важных столбца представления V$LOGMNR_CONTENTS: столбец SQL_REDO - показывает оператор, который может быть выполнен, чтобы вызвать то же самое изменение, сделанное пользователем, и столбец SQL_UNDO - оператор, который может быть выполнен для отмены изменения. На листинге 2 показаны результаты этого модифицированного запроса.
При личной встрече Джо признает, что он сделал эти изменения. В свою защиту, однако, Джо предоставляет весьма разумное объяснение. Очень важный клиент - Эллен Смит - имела неприятный опыт работы с ее назначенным персональным менеджером и потребовала сменить его. Такие изменения в базе данных обычно делаются главным менеджером, но он отсутствовал, и вновь назначенный персональный менеджер позвонил Джо и попросил "поколдовать" и помочь ему. Джо сделал это, выполнив следующий оператор: UPDATE ACCOUNTS SET acc_mgr_id = 6 WHERE first_name = 'Ellen' AND last_name = 'Smith'; Но Джо не заметил, что этот оператор обновил 340 записей , каждая из которых соответствовала клиентке с именем Эллен Смит. Джо хотел обновить запись только одной клиентки, но по неосмотрительности обновил 340 записей. Среди затронутых была и клиентка Пола, которая совсем не хотела замены своего персонального менеджера. Заметим, в вышеприведенном примере, Джон анализировал оперативные журнальные файлы. Однако если бы проблема возникла давно, содержимое оперативного журнала уже было бы перенесено в архивные журнальные файлы. В этом случае Джон мог бы также анализировать и архивный журнал. Для этого он просто бы указал имена архивных журнальных файлов в вызовах процедуры DBMS_LOGMNR.ADD_LOGFILE. Оставшаяся часть задач остается той же самой. Эти имена можно определить по представлению словаря данных V$ARCHIVED_LOG. Отмена изменений Следующая задача Джона состоит в том, чтобы немедленно обновить другие 339 записей, вернув их старые значения. Заметим, на листинге 2, текст под заголовком SQL_REDO показывает, что было изменено первоначально, а текст под заголовком SQL_UNDO показывает операторы, которые могут использоваться для отмены этих изменений. Хотя Джо первоначально использовал только один оператор, утилита LogMiner реконструирует операторы для модификации каждой записи по отдельности, как это и показано на листинге. Для восстановления старых идентификаторов персональных менеджеров Джон может выполнить каждый из операторов, перечисленных под заголовком SQL_UNDO. Это отменит изменения, которые сделал Джо. Удобный вывод Вывод значений столбцов SQL_UNDO и SQL_REDO на листинге 2 не очень легко читать - все они выдаются в одной строке. Для решения этой проблемы Джон может использовать опцию print_pretty_sql. Он также хочет выполнить SQL-операторы в цикле PL/SQL, используя динамический SQL, а этот подход требует, чтобы точка с запятой в конце каждого оператора была удалена. Эта задача решается с помощью опции no_sql_delimiter. Джон решает запустить сеанс утилиты LogMiner, используя следующие опции: begin dbms_logmnr.start_logmnr( options => dbms_logmnr. dict_from_online_catalog + dbms_logmnr.print_pretty_sql + dbms_logmnr.no_sql_delimiter ); end; После выполнения этого кода текст столбца SQL_UNDO в запросе листинга 2 будет выглядеть так: SQL_UNDO _____________________________ update "ARUP"."ACCOUNTS" set "ACC_MGR_ID" = 2 where "ACC_MGR_ID" = 6 and ROWID = 'AAOKdAALAAAGcEAB' ... Выборочная коррекция В предыдущих разделах мы обсуждали способ извлечения SQL-операторов для отмены всех операций обновления таблицы ACCOUNTS. Конечно, эту таблицу могли также обновлять и другие пользователи, Джон не хотел бы отменять их изменения. Чтобы выбирать только подходящие операторы, он может фильтровать содержимое представления V$LOGMNR_CONTENTS, как это показано на листинге 3.
Внимательно рассмотрите этот SQL-код. В нем используются дополнительные функции пакета DBMS_LOGMNR. Первая функция, MINE_VALUE, выбирает значение указанного столбца, записанного до или после изменения. Например, оператор DBMS_LOGMNR.MINE_VALUE (UNDO_VALUE, 'ARUP.ACCOUNTS.ACC_MGR_ID')показывает значение столбца ACC_MGR_ID перед изменением. Если параметр UNDO_VALUE изменить на REDO_VALUE, то этот оператор возвратит значение после изменения. Кроме того, функция COLUMN_PRESENT возвращает 1, если значение столбца существует и 0 в противном случае. Когда выполняется соединение с таблицей ACCOUNTS, она показывает другие подходящие детали счетов. Выходные данные на листинге 3 показывают старые и обновленные значения столбца ACC_MGR_ID, тип счета ACC_TYPE, а также имена и фамилии (столбцы FIRST_NAME и LAST_NAME). Эти данные помогают Джону мгновенно определить все поврежденные данные и способ их исправления. Используя SQL-оператор, показанный на листинге 3, Джон может написать простой PL/SQL-код для восстановления первоначальных значений столбца ACC_MGR_ID (выборочно, после проверки других столбцов, таких, как ACC_TYPE или FIRST_NAME, как это показано на листинге 4.
Удаленный анализ журнала Предшествующее обсуждение показало, как использовать утилиту LogMiner для анализа исходной базы данных в промышленной среде. Для повышения безопасности Джон может переслать архивные журнальные файлы в другую базу данных и там выполнить анализ. Сервер этой удаленной базы данных не доступен администраторам исходной базы данных, поэтому этот подход минимизирует возможности злонамеренного администратора по удалению следов своего вмешательства. Другое преимущество удаленного анализа - отсутствие влияния на производительность сервера исходной базы данных. На текущей установке, однако, есть одна небольшая проблема, которую Джон предварительно должен решить. В вышеописанном сценарии Джон использовал оперативный словарь данных в качестве словаря для преобразования шестнадцатеричных значений имен объектов в журнальных файла в удобочитаемые имена. Такой подход прекрасно работает, если для анализа используется исходная база данных, но в удаленной базе оперативный словарь исходной базы данных отсутствует. Если Джон занимается удаленным анализом журнала, он имеет два альтернативных варианта решения этой проблемы:
Оба этих варианта требуют выгрузки оперативного словаря данных либо в журнал, либо в обычный файл. Эта задача может быть выполнена с помощью поставляемого пакета DBMS_LOGMNR_D. Если этот пакет отсутствует, Джон может создать его, выполнив скрипты dbmslmd.sql и prvtlmd.sql, находящиеся в каталоге $ORACLE_HOME/rdbms/admin. Если Джон выберет первый вариант (помещение исходного словаря данных в журнальные файлы), то он выполнит следующий код: begin dbms_logmnr_d.build ( options => dbms_logmnr_d.store_in_redo_logs ); end; Заметим, этот вариант возможен, если в сервере базы данных включен режим архивирования (archivelog). В этом режиме словарная информация записывается в оперативные журнальные файлы, а затем, естественно, и в архивные журнальные файлы. Во время подготовки к анализу Джон пересылает эти архивные файлы (используя утилиту передачи файлов, такую, как FTP) и с помощью процедуры add_logfile пакета dbms_logmnr включает их для использования при анализе. После этого он запускает сеанс утилиты LogMiner, но с немного другим параметром: dbms_logmnr.start_logmnr( options => dbms_logmnr.dict_from_redo_logs ); Если Джон выберет второй вариант (хранение словаря данных в обычном файле), то он укажет файл, в который будет записан этот новый словарь. Для записи в файл используется пакет UTL_FILE, поэтому в параметре инициализации UTL_FILE_DIR должен быть задан путь доступа к этому файлу. Если это еще не сделано, Джон укажет, например, /tmp (в ОС UNIX): utl_file_dir = '/tmp' Чтобы это изменение начало действовать, Джон перезапустит экземпляр сервера исходной базы данных, а затем сгенерирует новый словарь, выполнив: begin dbms_logmnr_d.build ( dictionary_filename => 'ora_dict.txt', dictionary_location => '/tmp' ); end; Этот код генерирует в каталоге /tmp файл ora_dict.txt. Затем Джон пересылает файл /tmp/ora_dict.txt из сервера исходной базы данных в каталог /tmp сервера удаленной базы данных и запускает сеанс утилиты LogMiner, выполняя: dbms_logmnr.start_logmnr(dictfilename => '/tmp/ora_dict.txt'); Обратите внимание на новый параметр dictfilename, который указывает файл со словарем, созданный ранее. Нет никакой необходимости использования параметра OPTIONS, поскольку Джон не читает из оперативных словаря данных или журнальных файлов. Он может использовать параметр OPTIONS , например, для включения форматирования SQL-операторов, удаления точек с запятой в конце этих операторов и т.д. Какой подход лучше? В первым подходе журнальные файлы содержат словарь данных, поэтому во время анализа они должны быть доступными. Если вы анализируете небольшой набор журнальных файлов, то может оказаться неудобным включать в этот набор и файлы со словарем. Во втором подходе обычный файл доступен независимо от числа используемых журнальных файлов. Однако если не установлен параметр инициализации UTL_FILE_DIR, этот подход не будет доступным. Дополнительная журнализация Давайте рассмотрим немного другой сценарий истории "Персональный менеджер клиентки Эллен Смит". Предположим, таблица ACCOUNTS секционирована по диапазону значений столбца ACC_MGR_ID, где для каждого значения этого столбца создается отдельная секция. В этом случае, когда Джо обновил столбец ACC_MGR_ID, строки были перемещены в другую секцию. Например, строка ACC_MGR_ID = 6 находилась в шестой секции, но когда Джо установил в этом столбце значение 3, строка переместилась в третью секцию. Идентификатор строки ROWID основан на физическом местоположении строки, поэтому в результате этого физического перемещения был изменен идентификатор этой строки. Как вы должны действовать в этом случае? Если для отмены обновления вы выполните следующий оператор, извлеченный из сеанса утилиты LogMiner (см. листинг 2): update "ARUP"."ACCOUNTS" set "ACC_MGR_ID" = '6' where "ACC_MGR_ID" = '3' and ROWID = 'AAAOKeAAcAAAH8MAA7'; - он ничего не сможет обновить, потому что этот идентификатор ROWID, вероятно, не соответствует никакому текущему ROWID в данной таблице. Хуже, он может соответствовать ROWID фактической строки, но это будет неправильная строка для обновления, поскольку, по определению, ROWID анализируемой строки был изменен. Следовательно, для получения измененной строки вы не можете полагаться на ROWID.
Чтобы однозначно идентифицировать нужную строку, следует использовать первичный ключ таблицы. Но этот ключ по умолчанию не записывается в журнальные файлы. Для записи информации о первичных ключах в журнальные файлы нужно включить режим дополнительной журнализации (supplemental logging); в этом случае в журнальные файлы вместе с информацией об изменениях записываются значения дополнительных столбцов. Для включения этого режима следует выполнить оператор: alter database add supplemental log data (primary key) columns; Заметим, этот режим начинает действовать только после выполнения данного оператора; обратной силы он не имеет. Если бы вы выполнили этот оператор до внесения исходных изменений, вы смогли бы анализировать журнальные файлы и видеть на листинге 2 следующее содержимое столбца SQL_UNDO: update "ARUP"."ACCOUNTS" set "ACC_MGR_ID" = '6' where "ACC_NO" = '5720' and "ACC_MGR_ID" = '3' and ROWID = 'AAAOKeAAcAAAH8MAA7'; Обратите внимание, в содержимом столбца SQL_UNDO в дополнение к идентфикатору ROWID появился первичный ключ, ACC_NO, который позволяет идентифицировать правильные строки. В режиме дополнительной журнализации значения первичного ключа помогают однозначно идентифицировать строки. Если таблица не имеет первичного ключа, Джон может использовать режим дополнительной журнализации всех столбцов, которая делает строки уникальными. С другой стороны, запись этой добавочной информации приводит к дополнительной нагрузке на сервер базы данных, влияя на производительность, поэтому ее важность нужно внимательно оценивать. Заключение Журнальные файлы сервера базы данных Oracle представляют собой, без преувеличения, протокол всего происходящего внутри сервера базы данных и содержат полезную для анализа информацию. Утилита LogMiner - инструмент для извлечения этой информации из журнальных файлов и ее применения для идентификации пользовательских транзакций и даже для их отмены. Арап Нанда (Arup Nanda) ( arup@proligence.com ) - директор подразделения проектирования и эксплуатации баз данных компании Starwood Hotels and Resorts (White Plains, New York). В 2003 г. он был удостоен награды " Oracle's DBA of the Year "( администратор года баз данных Oracle). Арап - соавтор книги Oracle Privacy Security Auditing (издательство Rampant TechPress, 2003) - "Средства аудита в СУБД Oracle, обеспечивающие информационную безопасность". |