|
Программирование >> Oracle
10 commit; 11 end loop; 12 end; 13 / 11654 rows updated 21759 rows updated 309 rows updated 6 rows updated 270 rows updated 830 rows updated 412 rows updated 7 rows updated 378 rows updated 95 rows updated 203 rows updated 2482 rows updated 13 rows updated 318 rows updated 83 rows updated 14 rows updated 1408 rows updated 86 rows updated 2 rows updated 35 rows updated 2409 rows updated 57 rows updated 306 rows updated 379 rows updated 1 rows updated 1 rows updated PL/SQL procedure successfully completed. Теперь в случае сбоя мы сможем перезапустить процесс, поскольку не будут обраба-т1ваться имена объектов, уже успешно обработанных. Проблема этого подхода, однако, в том, что данные неравномерно распределены по обрабатываемым фрагментам. Второе изменение затронуло больше строк, чем все остальные вместе взятые. Кроме того, другие сеансы, обращающиеся к таблице и изменяющие данные, тоже могут изменить столбец objectname. Предположим, другой сеанс изменил имя объекта с Z на А после обработки фрагмента, содержащего объекты с именами на А - соответствующая запись будет пропущена. Более того, это - крайне неэффективный процесс по сравнению с использованием оператора update t set last ddl time = last ddl time+l. Для чтения каждой строки таблицы мы обычно используем индекс или многократно просматриваем всю таблицу, однако и то, и другое нежелательно. У этого подхода слишком много недостатков. Лучшим является подход, который я постоянно проповедую: делать проще. Если это можно сделать в SQL, делайте в SQL. To, что нельзя сделать одним SQL-оператором, делайте в PL/SQL. И используйте как можно меньше кода. Выделяйте достаточно ре- сурсов. Всегда думайте, что произойдет в случае ошибки. Часто разработчики пишут циклы для изменения данных, замечательно работающие в тестовой среде, но завершающиеся ошибкой после применения к реальным данным. И проблема здесь в том, что не известно, где остановилась обработка. Намного проще определить правильный размер сегмента отката, чем написать перезапускаемую транзакцию. Если должны изменяться большие таблицы, фрагментируйте их (подробнее об этом см. в главе 14), что позволит изменять каждый фрагмент отдельно. Для изменения можно даже использовать параллельные операторы ЯМД. И наконец, еще об одной плохой привычке при организации транзакций. Она приобретается при использовании популярных функциональных интерфейсов ODBC и JDBC. Эти функциональные интерфейсы по умолчанию выполняют автоматическую фиксацию . Рассмотрим следующие операторы, переводящие 1000 $ с текущего счета на накопительный: update accounts set balance = balance - 1000 where account id = 123; update accounts set balance = balance + 1000 where account id = 456; Если при эти операторы приложение отправляет через интерфейс JDBC, JDBC вставит оператор фиксации после каждого изменения. Подумайте о последствиях этого, если сбой произойдет после первого изменения, но перед вторым. Потеряны 1000 $! Я понимаю, почему интерфейс ODBC делает именно так. Интерфейс ODBC создавали разработчики SQL Server, а принятая в этой СУБД модель одновременного доступа (пишущие блокируют читающих, читающие - пишущих, и блокировки являются ограниченным ресурсом) требует использования очень коротких транзакций. Я не понимаю, как подобный подход был перенесен в JDBC - интерфейс, предназначавшийся для поддержки приложений масштаба предприятия . Практика показала, что сразу же после подключения по интерфейсу JDBC необходимо выполнять следующую строку кода: connection conn81 = DriverManager.getConnection ( jdbc:oracle:oci8:@ora8idev , scott , tiger ); conn81.setAutoCommIt (false); Это возвращает разработчику контроль над транзакциями. Затем можно вполне безопасно выполнять транзакцию по переводу денег и фиксировать ее после успешного выполнения обоих операторов. Незнание особенностей используемого функционального интерфейса в данном случае просто опасно. Я знал разработчиков, не подозревавших об этом свойстве автоматической фиксации и столкнувшихся с большими проблемами, когда в созданных ими приложениях произошла подобная ошибка. Распределенные транзакции Одной из действительно замечательных возможностей сервера Oracle является его способность прозрачно для пользователей выполнять распределенные транзакции. Я могу изменять данные в нескольких базах в пределах одной транзакции. При фиксации эти изменения либо фиксируются во всех экземплярах, либо ни в одном (они все откатываются). Для этого не нужно писать дополнительный код - достаточно просто выполнить оператор фиксации. Ключевым для распределенных транзакций в Oracle является понятие связи базы данн1х (database link). Связь базы данных - это объект базы данных, описывающий, как подключиться к другому экземпляру с текущего. Однако этот раздел посвящен не синтаксису оператора создания связи (он подробно описан в документации). После создания связей базы данных доступ к удаленным объектам выполняется очень просто: select * from T@another database; Этот оператор выберет данные из таблицы Т в экземпляре, определяемом связью базы данн1х ANOTHER DATABASE. Обычно факт удаленности таблицы Т скрывают , создавая для нее представление или синоним. Например, можно выполнить: create synonym T for T@another database; после чего обращаться к Т, как к локальной таблице. Теперь, настроив связи базы данных и проверив их с помощью чтения ряда таблиц, мы сможем также изменять их (при наличии соответствующих привилегий, конечно). Выполнение распределенной транзакции теперь не отличается от выполнения локальной. Достаточно сделать следующее: update local table set x= 5; update remote table@another database set у = 10; commit; И все. Сервер Oracle либо зафиксирует изменения в обеих базах данных, либо их не окажется ни в одной. Для этого используется протокол двухэтапной фиксации (two-phase commit - 2РС). 2РС - это распределенный протокол, позволяющий фиксировать неделимое изменение, затрагивающее несколько баз данных. Он пытается максимально ограничить возможность сбоя распределенной транзакции перед фиксацией. При использовании протокола 2РС одна из задействованных баз данных (обычно та, к которой первоначально подключен клиент) становится координаторомраспределенной транзакции. Сервер-координатор опрашивает соответствующие серверы, готовы ли они фиксировать изменения. Обращаясь к ним, он просит подготовиться к фиксации. Серверы сообщают о своем состоянии готовности в виде ДА или НЕТ. Если один из серверов сказал НЕТ, транзакция откатывается. Если все серверы сказали ДА, сервер-координатор расс1лает сообщение о необходимости зафиксировать изменения на каждом сервере. Этот протокол ограничивает время, когда может произойти серьезная ошибка. Перед голосованием по протоколу двухэтапной фиксации любая ошибка на задействованном сервере приведет к откату транзакции на всех серверах. Таким образом, не будет никаких сомнений как в отношении исхода транзакции, так и в том, что касается состояния распределенной транзакции. Сомнения в случае сбоя возможны только очень непродолжительное время - при сборе ответов. Предположим, например, что в транзакции участвует три сервера; сервер 1 - координатор. Сервер 1 попросил сервер 2 подготовиться к фиксации, и сервер 2 это сделал. Затем сервер 1 попросил сервер 3 подготовиться к фиксации, и тот подготовился. Сейчас только сервер 1 знает результат транзакции, и он обязан сообщить его двум другим серверам. Если в этот момент произойдет ошибка - сбой сети, сбой питания на сервере 1, любая ошибка, - серверы 2 и 3 останутся в подвешенном состоянии. У них возникнет так называемая сомнительная распределенная транзакция. Протокол двухэтапной фиксации пытается свести к ми-
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |