Oracle для професіоналів. Глава 1. Частина 1

Розробка успішних програм для Oracle


Значну частину часу я проводжу, працюючи з програмним забезпеченням СУБД Oracle або, точніше, з людьми, які це програмне забезпечення використовують. Протягом останніх дванадцяти років я працював над багатьма проектами, як успішними, так і закінчилися невдало, і якщо б треба було узагальнити набутий при цьому досвід декількома фразами, я б сказав наступне:


Ці твердження можуть здатися очевидними, але досвід показує, що дуже багато хто використовує СУБД як "чорний ящик", про деталі пристрою якого знати необов'язково. Вони можуть використовувати генератор SQL, що дозволяє не утруднювати себе вивченням мови SQL. Можливо, вони вирішать використовувати її як звичайний файл з можливістю читання записів по ключу. Як би там не було, я можу вам сказати, що подібного роду міркування майже напевно призводять до неправильних висновків – працювати, не розуміючи пристрої СУБД, просто не можна. У цьому розділі описано, чому необхідно знати пристрій СУБД, зокрема, чому необхідно розуміти:


Цей список тем для початкового вивчення може здатися занадто довгим, але давайте розглянемо наступну аналогію: якби ви розробляли масштабується, високопродуктивне додаток для абсолютно нової операційної системи (ОС), з чого б ви почали? Сподіваюся, ваша відповідь: "З вивчення особливостей функціонування цієї нової ОС, роботи додатків в ній і т.п.". Якщо відповідь принципово інший, ваша розробка приречена на невдачу.

Розглянемо, наприклад, одну з ранніх версій Windows (скажімо, Windows 3.x). Вона, як і ОС UNIX, була "багатозадачного" операційною системою. Однак ця багатозадачність була не такою, як в ОС UNIX, – використовувалася модель невитискаючої багатозадачності (тобто, якщо працює програма не повертає управління, ніщо інше працювати не може, включаючи операційну систему). Фактично, в порівнянні з UNIX, Windows 3.x взагалі не була багатозадачного ОС. Для створення ефективних додатків розробники повинні були точно знати, як реалізована можливість "багатозадачності" Windows. Якщо необхідно розробляти програму, працює безпосередньо у середовищі певної ОС, розуміння особливостей цієї ОС дуже важливо.

Те, що вірно на програми, які безпосередньо працюють в середовищі операційної системи, вірно і для додатків, що працюють у середовищі СУБД: розуміння особливостей СУБД є визначальним чинником успіху. Якщо ви не розумієте, що робить використовувана СУБД або як вона це робить, створюване додаток не буде працювати успішно. Припущення про те, що успішно працює в середовищі SQL Server додаток так само успішно працюватиме і в середовищі Oracle, скоріше за все не виправдається.

Мій підхід


Перш ніж почати, хотілося б пояснити вам мій підхід до розробки. Я віддаю перевагу вирішувати більшість проблем на рівні СУБД. Якщо щось можна зробити в СУБД, я так і зроблю. Для цього є дві причини. Перша і головна полягає в тому, що якщо вмонтувати функціональність в СУБД, то її можна буде застосовувати де завгодно. Я не знаю серверної операційної системи, для якої немає реалізації СУБД Oracle. Одна і та ж СУБД Oracle з усіма опціями працює скрізь – від Windows до десятків версій ОС UNIX і великих ЕОМ типу OS/390. Я часто розробляю і тестую програми на моєму портативному комп'ютері, де працює СУБД Oracle8i для Windows NT. А застосовуються ці програми на різних серверах з ОС UNIX, на яких працює та ж версія СУБД. Якщо доводиться реалізувати функціональність за межами СУБД, її дуже складно переносити на будь-яку іншу платформу. Одна з основних особливостей, які роблять мову Java привабливим для багатьох розробників, полягає в тому, що програми завжди компілюються в одній і тій ж віртуальному середовищі, віртуальній машині Java Virtual Machine (JVM), і тому максимально стерпні. Саме ця особливість приваблює мене в СУБД. СУБД Oracle – це моя віртуальна машина, моя "віртуальна операційна система ".

Мій підхід полягає в тому, щоб робити в СУБД все, що можливо. Якщо вимоги виходять за межі можливостей СУБД, я реалізую відповідні функції на мові Java поза СУБД. У цьому випадку особливості практично будь-якої операційної системи ховаються. Мені все одно треба розуміти, як працюють мої "віртуальні машини" (Oracle або JVM) – треба знати використовувані інструментальні засоби, – але найбільш ефективна реалізація відповідних функцій в конкретній ОС залишається прерогативою творців цих віртуальних машин.

Таким чином, знаючи лише особливості роботи однієї "віртуальної ОС", можна створювати додатки, що демонструють відмінну продуктивність і масштабованість в багатьох операційних системах. Я не стверджую, що можна повністю ігнорувати базову ОС, – просто розробник додатків баз даних досить добре від неї ізольований, і йому не доведеться враховувати багато її нюанси. Ваш АБД, що відповідає за підтримку СУБД Oracle, повинен знати набагато більше про особливості базової ОС (якщо не знає – знайдіть нового АБД!). При розробці клієнт-серверного програмного забезпечення, якщо основна частина коду винесено з СУБД і віртуальної машини (найбільш популярною віртуальною машиною, ймовірно, є Java Virtual Machine), розробнику доведеться враховувати особливості ОС сервера.

При розробці додатків баз даних я використовую дуже просту мантру:


У книзі ви побачите застосування цього підходу. Ми будемо використовувати мову PL / SQL і його об'єктні типи для реалізації того, що не можна зробити в SQL. Мова PL / SQL існує давно, за ним стоїть понад тринадцять років налаштування, і немає іншої мови, настільки тісно інтегрованого з мовою SQL і настільки оптимізованого для взаємодії з SQL. Коли можливостей PL / SQL виявляється недостатньо, наприклад, при доступі до мережі, відправлення повідомлень електронної пошти і т.п., ми будемо використовувати мову Java. Іноді ми будемо вирішувати певні задачі за допомогою мови C, але зазвичай лише в тих випадках, коли програмування на C – єдино можливий варіант або коли забезпечується компілятором C швидкість роботи програми дійсно необхідна. У багатьох випадках зараз остання причина відпадає при використанні компіляції в машинні коди програм на мові Java (можливості перетворити байт-код Java в специфічний об'єктний код операційної системи для даної платформи). Це забезпечує програмами на Java таку ж швидкість роботи, як і в програм на мові C.

Підхід з використанням принципу чорного ящика


У мене є припущення, засноване на особистому досвіді, чому так часто розробка додатків баз даних закінчується невдало. Дозвольте уточнити, що до розряду невдалих розробок я відношу також проекти, визнані офіційно не невдалими, але зажадали на розробку і впровадження значно більше часу, чим планувалося спочатку, оскільки довелось їх істотно "переписувати", "перепроектувати" або "налаштовувати". Особисто я такі не завершені у строк проекти вважаю невдалими: дуже часто їх цілком можна було завершити вчасно (і навіть достроково).

Найбільш типовою причиною невдачі є брак практичних знань з використовуваної СУБД – елементарне нерозуміння основ роботи використовуваного інструментального засобу. Підхід за принципом "чорного скриньки "вимагає усвідомленого рішення: захистити розробників від СУБД. Їх змушують не вникати в жодні особливості її функціонування. Причини використання цього підходу пов'язані з побоюваннями, незнанням і невпевненістю. Розробники чули, що СУБД – це "складно", мова SQL, транзакції і цілісність даних – не менш "складно". Рішення: не змушувати нікого робити що-небудь "складне". Будемо ставитися до СУБД, як до чорного ящика, і знайдемо інструментальне засіб, який згенерує необхідний код. Ізолюємо себе кількома проміжними рівнями, щоб не довелося стикатися безпосередньо з цією "складною" СУБД.

Такий підхід до розробки додатків баз даних я не міг зрозуміти ніколи. Одна з причин, чому мені важко це зрозуміти, полягає в тому, що для мене вивчення мов Java і C виявилося набагато складніше, ніж вивчення основ роботи СУБД. Я зараз дуже добре знаю мови Java і C, але для їх освоєння мені знадобилося набагато більше практичного досвіду, ніж для досягнення відповідного рівня компетентності при використанні СУБД. У разі СУБД необхідно знати, як вона працює, але деталі знати не обов'язково. При програмуванні на мові C або Java, необхідно, наприклад, знати всі особливості використовуваних компонентів, крім того, це дуже великі за обсягом мови.

Ще одна причина – те, що при створенні додатків бази даних найважливішим компонентом програмного забезпечення є СУБД. Для успішної розробки необхідно враховувати це і доводити до відома розробників, постійно звертаючи на це їхню увагу. Багато разів я стикався з проектами, де дотримувалися прямо протилежних поглядів.

Ось типовий сценарій такого роду розробки.


Не зумівши забезпечити потрібну продуктивність, розробники зверталися по допомогу до мене. Особливо показовий один випадок. Я не міг згадати точний синтаксис нової команди, яку треба було використовувати, і попросив керівництво SQL Reference. Мені принесли екземпляр з документації по СУБД Oracle версії 6.0, хоча розробка велася на версії 7.3, через п'ять років після виходу версії 6.0! Нічого іншого для роботи у них не було, але це взагалі нікого не турбувало. Хоча необхідне їм для трасування і налаштування інструментальне засіб у той час взагалі не існувало. Хоча за п'ять років, що минули після написання була у них документації, були додані такі засоби, як тригери, збережені процедури, і багато сотень інших. Нескладно зрозуміти, чому їм потрібна допомога, набагато важче було вирішити їхні проблеми.

Дивна ідея про те, що розробник програми баз даних повинен бути захищений від СУБД, надзвичайно живуча. Багато хто чомусь вважають, що розробникам не слід витрачати час на вивчення СУБД. Неодноразово доводилося чути: "СУБД Oracle – сама Швидка, у світі, моїм співробітникам не потрібно її вивчати, бо СУБД з усіма проблемами впорається сама". Дійсно, СУБД Oracle – сама розширювана. Проте написати поганий код, який масштабуватися не буде, в Oracle набагато простіше, ніж написати хороший, масштабований код. Можна замінити СУБД Oracle будь-який інший СУБД – це твердження залишиться вірним. Це факт: простіше писати програми з низькою продуктивністю, ніж високопродуктивні додатки. Іноді дуже легко створити однопользовательськую систему на базі самої масштабованої СУБД в світі, якщо не знати, що робиш. СУБД – це інструмент, а неправильне застосування будь-якого інструмента може призвести до катастрофи. Ви будете щипцями колоти горіхи так само, як молотком? Можна, звичайно, і так, але це неправильне використання інструменту, і результат вас не порадує. Аналогічні результати будуть і при ігноруванні особливостей використовуваної СУБД.

Я нещодавно працював над проектом, в якому проектувальники придумали дуже елегантну архітектуру. Клієнт за допомогою Web-браузера взаємодіяв з протоколу HTTP з сервером додатків, що забезпечує підтримку Java Server Pages (JSP). Алгоритми роботи програми цілком генерувалися інструментальними засобами і реалізовувалися у вигляді компонентів EJB (з використанням постійного зберігання на базі контейнерів), причому фізично вони виконувалися іншим сервером додатків. У базі даних зберігалися лише таблиці та індекси.

Отже, ми почали з технічно складної архітектури. Для вирішення завдання повинні взаємодіяти один з одним чотири компоненти. Web-браузер отримує сторінки JSP від сервера додатків, який звертається до компонентів EJB, а ті, у свою чергу, – до СУБД. Для розробки, тестування, налаштування та впровадження цієї програми необхідні були технічно компетентні фахівці. Після завершення розробки мене попросили оцінити продуктивність програми. Перш за все я хотів дізнатися підхід розробників до СУБД:


Вони не мали ні найменшого уявлення про це. На питання про те, хто допоможе мені переписати код компонента EJB для налаштування згенерованого запиту, відповідь була така: "О, цей код не можна змінювати, все треба робити в базі даних ". Тобто, додаток повинен залишатися незмінним. У цей момент я був готовий відмовитися від роботи над проектом – ясно, що змусити це додаток нормально працювати неможливо:


Все це стало зрозуміло вже через годину тестування. Як виявилося, у додатку спочатку виконувався оператор:

select * from t for update;

Це призводило до строго послідовній роботі всіх клієнтів. У базі даних була реалізована така модель, що перед виконанням будь-яких істотних дій доводилося блокувати дуже великий ресурс. Це вмить перетворювало додаток в дуже велику однопользовательськую систему. Розробники не вірили мені (в інший СУБД, що використовує поділювану блокування читання, спостерігалася інша ситуація). Після десяти хвилин роботи з інструментальним засобом TKPROF (про нього докладно написано в розділі 10) я зміг продемонструвати, що саме цей оператор SQL виконувався додатком (вони про це не знали – просто ніколи не бачили генеруються операторів SQL). Я не просто показав, які оператори SQL виконуються додатком, але і за допомогою пари сеансів SQL * Plus продемонстрував, що другий сеанс не починається до повного завершення роботи першим сеансом.

Отже, замість того, щоб тиждень тестувати продуктивність програми, я вжив цей час на навчання розробників налаштуванні, особливостям блокування в базах даних, механізмам управління одночасним доступом, порівняння їх реалізацій в СУБД Oracle, Informix, SQL Server, DB2 і так далі (у всіх цих СУБД вони різні). Але спочатку мені довелося зрозуміти, однак, чому використовувався оператор SELECT FOR UPDATE. Виявилося, що розробники хотіли домогтися повторюваності при читанні.

Повторюваність при читанні – це такий режим роботи СУБД, коли повторне читання в транзакції рядка, який вже один раз прочитана в цій транзакції, дає той же результат.

Навіщо їм це було потрібно? Вони чули, що "це добре". Гаразд, припустимо, повторюваність при читанні дійсно необхідна. У СУБД Oracle це робиться шляхом встановлення рівня ізольованості транзакції SERIALIZABLE (Що дає не тільки повторюваність при читанні рядка, але і повторюваність при виконанні будь-якого запиту – якщо два рази виконати один і той самий запит в межах транзакції з таким рівнем ізольованості, будуть отримані однакові результати). Для забезпечення повторюваності при читанні в Oracle не потрібно використовувати SELECT FOR UPDATE – Це робиться тільки для забезпечення послідовного доступу до даних. На жаль, використане розробниками інструментальне засіб це не враховувало – він був створений для використання з іншою СУБД, де саме так повторюваність при читанні і досягалася.

Отже, в даному випадку для встановлення рівня ізольованості транзакцій SERIALIZABLE довелося створити тригер на реєстрацію в базі даних, що змінює параметри сеансу (рівень ізольованості транзакцій) для цього додатка. Потім ми відключили всі установки повторюваності при читанні в використовувався інструментальному засобі і повторно запустили програму. Тепер, без конструкції FOR UPDATE, В базі даних певні дії стали виконуватися одночасно.

Це була далеко не остання проблема даного проекту. Нам довелося розібратися:


У кінці тижня розробники, ніколи раніше не працювали з СУБД, були здивовані тим, що насправді вона дає можливість зробити, як легко отримати зазначену вище інформацію і, що найбільш важливо, як істотно все це може позначитися на продуктивності програми. Тестуванням продуктивності протягом цього тижня ми не займалися (їм дещо довелося переробляти!), Але в кінцевому підсумку проект завершився успішно – просто на пару тижнів пізніше запланованого терміну.

Це не критика інструментальних засобів або сучасних технологій, таких як компоненти EJB і підтримка постійного існування на базі контейнерів. Це – критика навмисного ігнорування особливостей СУБД, принципів її роботи і використання. Технології, обрані в цьому проекті, працювали відмінно, але лише після того, як розробники трохи розібралися в самій СУБД.

Підводячи підсумки: СУБД – це наріжний камінь програми. Якщо вона не працює як слід, все інше не має значення. Якщо погано працює чорний ящик, що з ним робити? Його не можна виправити, не можна налаштувати (оскільки незрозуміло, як він улаштований), і таку позицію ви вибрали самі. Але є й інший підхід, який я відстоюю: розберіться в використовуваної СУБД та принципи її роботи, зрозумійте, що вона може робити, і використовуйте весь її потенціал.

Як треба (і як не треба) розробляти додатки баз даних


Проте чи вистачить теоретизувати, принаймні поки що. У частині розділу я використовую більш емпіричний підхід, описуючи, чому знання СУБД і особливостей її роботи суттєво підвищує шанси успішного реалізації програми (без необхідності переписувати його двічі!). Деякі проблеми вирішити легко, якщо відомо, як їх знайти. Для вирішення інших будуть потрібні істотні переробки. Одна з цілей цієї книги – допомогти вам взагалі уникнути проблем.

У наступних розділах я опишу ряд найважливіших особливостей СУБД Oracle, не вдаючись у подробиці їх реалізації та використання. Наприклад, я опишу лише один з наслідків використання архітектури багатопотокового сервера (Multi-Threaded Server – MTS) – режиму, в якому можна (а іноді і потрібно) конфігурувати сервер Oracle для підтримки безлічі користувальницьких сеансів. Я, однак, не буду детально описувати архітектуру MTS, особливості її роботи і т.п. Усе це докладно описано в керівництві Oracle Server Concepts Manual (додаткову інформацію можна знайти також у керівництві Net8 Administrators Guide).

Розуміння архітектури СУБД Oracle


Нещодавно я брав участь у проекті, творці якого вирішили використовувати тільки новітні, найдосконаліші технології: все програмне забезпечення було написано на Java у вигляді компонентів EJB. Клієнтське додаток взаємодіяло з СУБД через ці компоненти – ніякого протоколу Net8. Між клієнтом і сервером не віддавалися оператори SQL – тільки звернення до компонентів EJB за допомогою викликів віддалених методів (Remote Method Invocation – RMI) за протоколом Internet Inter-Orb Protocol (IIOP).

Про організацію RMI по протоколу IIOP можна дізнатися на сайті http://java.sun.com/products/rmi-iiop/.

Це цілком допустимий підхід. Такий спосіб взаємодії працює і може бути вельми масштабуванні. Ті, хто розробляв архітектуру, добре розуміли мову Java, технологію компонентів EJB, знали використовувані протоколи, у загальному – всю "кухню". Їм здавалося, що є всі підстави для успішної реалізації подібного проекту. Коли ж з'ясувалося, що додаток може підтримувати лише декількох користувачів, вони вирішили, що проблема – в СУБД, і засумнівалися в декларованої корпорацією Oracle "рекордної масштабованості СУБД".

Проблема, однак, була не в СУБД, а в незнанні особливостей її роботи, і деякі рішення, прийняті на етапі проектування, призвели до краху програми в цілому. Щоб використовувати компоненти EJB в базі даних, сервер Oracle повинен бути налаштований для роботи в режимі багатопотокового (MTS), а не виділеного сервера. Чого не розуміла команда розробників в принципі, так це наслідків використання режиму MTS в поєднанні з компонентами EJB для їх застосування. При відсутності цього розуміння, як і знання основ роботи СУБД Oracle взагалі, були прийняті два ключових рішення.


Ці, здавалося б, непринципові рішення зумовили неминучий провал проекту. Все було зроблено так, що гранично Швидка, СУБД не справлялася з навантаженням навіть при невеликій кількості користувачів. Брак знань про особливості роботи СУБД звела нанівець усі глибокі знання розробників зі створення компонентів на Java і розподіленій обробці. Якщо б вони знайшли час на мінімальну вивчення особливостей роботи СУБД Oracle і потім застосували два представлених далі простих принципу, шанси на успіх проекту вже в першій версії істотно б підвищилися.

Уникайте тривалих трансакцій в середовищі MTS


Рішення використовувати транзакції тривалістю більше 45 секунд в середовищі MTS видало недостатнє розуміння призначення режиму MTS та особливостей його роботи в Oracle. Якщо коротко, в режимі MTS використовується загальний пул серверних процесів, що обслуговує набагато більший пул кінцевих користувачів. Це схоже на пул підключень. Оскільки створення і керування процесом – найбільш дорогі операції, що виконуються операційною системою, режим MTS дає великі переваги для великомасштабної системи. Можна обслуговувати 100 користувачів всього п'ятьма або десятьма розділяються серверними процесами.

Коли розділяється серверний процес отримує запит на зміну даних або виконання збереженої процедури, він прив'язується до цього завдання до її завершення. Жодна інша задача не буде використовувати розділяється серверний процес, поки не буде закінчено зміна або не завершиться виконання збереженої процедури. Тому при використанні режиму MTS треба використовувати якомога швидше виконуються оператори. Режим MTS створений для забезпечення масштабованості систем оперативної обробки транзакцій (OLTP), для яких характерні оператори, що виконуються за частки секунди. Мова йде про зміни окремих рядків, вставці кількох рядків і запитах записів по первинному ключу. Не варто в цьому режимі виконувати пакетні процеси, для завершення яких потрібні десятки секунд або хвилини.

Якщо всі оператори виконуються швидко, архітектура MTS працює відмінно. Можна ефективно обслуговувати невеликою кількістю процесів велика спільнота користувачів. Якщо ж є сеанси, що монополізують розділяється сервер надовго, то здається, що СУБД "зависає". Нехай конфігуроване десять поділюваних серверів для підтримки 100 користувачів. Якщо в деякий момент часу десять користувачів одночасно введуть оператор, що виконується більше 45 секунд, то всім іншим транзакціям (і новим підключень) доведеться чекати. Якщо деяким з чекають у черзі сеансів необхідно виконувати оператор такий же тривалості, виникає велика проблема – "зависання" триватиме не 45 секунд, а набагато довше. Навіть якщо бажаючих виконати подібний оператор одночасно буде не десять, а лише кілька, все одно буде спостерігатися суттєве падіння продуктивності сервера. Ми відберемо на тривалий час спільно використовуваний ресурс, і це погано. Замість десяти серверних процесів, що виконують швидкі запити в черзі, залишається п'ять або шість (або ще менше). З часом система стане працювати з продуктивністю, помітно менше передбачуваної, виключно через брак цього ресурсу.

Просте рішення "в лоб" складається у запуску більшої кількості поділюваних серверів, але в кінцевому підсумку доведеться запускати розділяється сервер для кожного користувача, а це неприйнятно для системи з тисячами користувачів (як та, що створювалася в даному проекті). Це не тільки створює вузькі місця в самій системі (чим більшою кількістю процесів доводиться управляти, тим більше процесорного часу на це йде), але і просто не відповідає цілям створення режиму MTS.

Реальне вирішення цієї проблеми виявилося простим: не виконувати тривалі транзакції на сервері, що працює в режимі MTS. А от реалізація цього рішення виявилася складніше. Це можна було зробити декількома способами, але всі вони вимагали суттєвих змін архітектури. Найбільш підходящим способом, що вимагає мінімальних змін, виявилося використання коштів розширеної підтримки черг (Advanced Queues – AQ).

AQ – це проміжне програмне забезпечення для обміну повідомленнями, реалізоване на базі СУБД Oracle і дозволяє клієнтського сеансу додавати повідомлення в таблицю черги бази даних. Це повідомлення надалі (зазвичай відразу після фіксації транзакції) вибирається з черги іншим сеансом, перевіряючим зміст повідомлення. Повідомлення містить інформацію для обробки іншим сеансом. Воно може використовуватися для емуляції миттєвого виконання за рахунок винесення тривалого процесу за межі інтерактивного клієнта.

Отже, замість виконання 45-секундного процесу, компонент повинен поміщати запит з усіма необхідними вхідними даними в чергу і виконувати його асинхронно, а не синхронно. У цьому випадку користувачеві не доведеться чекати відповіді 45 секунд, тобто система стає більш динамічною.

Хоча, судячи з опису, цей підхід простий (підключення механізму AQ повністю вирішує проблему), потрібно було зробити набагато більше. Цей 45-секундний процес генерував ідентифікатор транзакції, необхідний на наступному кроці в інтерфейсі для з'єднання таблиць – за проектом інтерфейс без цього не працював. Використовуючи механізм AQ, ми не чекаємо генерації ідентифікатора транзакції, – ми звертаємося до системи з проханням зробити це коли-небудь. Тому додаток знову опинилося в глухому куті. З одного боку, ми не можемо чекати завершення процесу 45 секунд, але, з іншого боку, для переходу до наступного екрану необхідний згенерований ідентифікатор, а отримати його можна тільки через 45 секунд. Для того щоб вирішити цю проблему, довелося синтезувати власний підроблений ідентифікатор транзакції, змінити тривалий процес так, щоб він приймав цей згенерований підроблений ідентифікатор і оновлював таблицю, записуючи його по завершенні роботи, завдяки чому реальний ідентифікатор транзакції зв'язувався з підробленим. Тобто, замість отримання реального ідентифікатора в результаті тривалого процесу, цей ідентифікатор стає для процесу вхідними даними. У всіх "підлеглих" таблицях використовувався цей підроблений ідентифікатор транзакції, а не реальний (оскільки генерації реального треба чекати певний час). Нам також довелося переглянути використання цього ідентифікатора транзакції, щоб зрозуміти, як це зміна вплине на інші модулі, і так далі.

Ще одна проблема полягала в тому, що при синхронній роботі, якщо 45-секундний процес завершувався невдало, користувач дізнавався про це відразу. Він міг усунути причину помилки (зазвичай шляхом зміни вхідних даних) і повторно виконати запит. Тепер, коли транзакції виконуються асинхронно за допомогою механізму AQ, зробити це неможливо. Для підтримки відстроченого повідомлення про помилку довелося додати нові кошти. Зокрема, знадобилося реалізувати механізм потоків завдань для відправки інформації про невдалі транзакціях відповідній особі.

У результаті довелося суттєво переглянути структуру бази даних. Довелося додати нове програмне забезпечення (AQ). Довелося також створити нові процеси (управління потоками завдань і інші службові процеси). До позитивних наслідків цих змін можна віднести не тільки рішення проблеми з архітектурою MTS, а й зручність для користувача (створювалася видимість більш швидкої реакції системи). З іншого боку, всі ці зміни суттєво затримали завершення проекту, оскільки проблеми були виявлені лише безпосередньо перед впровадженням, на етапі тестування масштабованості. Дуже шкода, що додаток відразу не був правильно спроектовано. Якби розробники знали, як фізично реалізований механізм MTS, було б ясно, що вихідний проект не забезпечує необхідної масштабованості.

Використовуйте зв'язуються змінні


Якби мені довелося писати книгу про те, як створювати немасштабіруемие програми Oracle, перша і єдина її голова називалася б "Не використовуйте зв'язуються змінні". Це – основна причина проблем, пов'язаних з продуктивністю, і основна перешкода масштабованості. Особливості використання розділяється пулу Oracle (дуже важливої структури даних пам'яті, що розділяється) вимагають від розробників використовувати Спільні змінні. Якщо треба сповільнити роботу програми Oracle, аж до повного зупинення, – відмовтеся від їхнього використання.

Пов'язують змінна – це підставляється параметр запиту. Наприклад, для отримання запису доячи співробітника з номером 123, можна виконати запит:

select * from emp where empno = 123;

Але можна задати і інший запит:

select * from emp where empno = :empno;

У звичайній системі інформацію про співробітника з номером 123 можуть запитувати всього один раз. Надалі будуть запитувати інформацію про співробітників з номерами 456, 789 і т.д. При використанні в запиті літералів (Констант) кожен запит є для СУБД абсолютно новим, ніколи раніше не виконувалися. Його треба розбирати, уточнювати (визначати об'єкти, відповідні іменам), перевіряти права доступу, оптимізувати і т.д. – Коротше, кожен виконуваний унікальний оператор доведеться компілювати при кожному виконанні.

У другому запиті використовується пов'язують мінлива,: empno, значення якої підставляється в запит при виконанні. Цей запит компілюється один раз, а потім план його виконання запам'ятовується в поділюваному пулі (в бібліотечному кеші), з якого його можна вибрати для повторного виконання. Різниця між цими двома варіантами в плані продуктивності і масштабованості – величезна, навіть принципове.

З представленого вище опису цілком зрозуміло, що розбір оператора з явними, жорстко заданими константами (так званий повний розбір) виконується довше і вимагає набагато більше ресурсів, ніж повторне використання вже згенерованого плану запиту (його називають частковим розбором). Менш очевидним може виявитися, наскільки постійний повний розбір скорочує кількість користувачів, підтримуваних системою. Почасти це пов'язано з підвищеним споживанням ресурсів, але в набагато більшому ступені – з механізм засувок, використовуваних в бібліотечному кеші. При повному розборі запиту СУБД буде довше утримувати певні низькорівневі засоби забезпечення послідовного доступу, які називаються засувками (докладніше про них див у розділі 3). Засувки захищають структури даних у пам'яті, що розділяється сервера Oracle від одночасного зміни двома сесіями (інакше ці структури даних Oracle в кінцевому підсумку були б пошкоджені) і від читання цієї структури даних по ходу зміни іншим сеансом. Чим частіше і на більш тривалий час на ці структури даних встановлюються засувки, тим довшою стає чергу для установки цих засувок. Точно так само відбувається при використанні довгих транзакцій в середовищі MTS, – монополізуються критичні ресурси. Часом машина може здаватися мінімально завантаженою, а СУБД працює дуже повільно. Цілком ймовірно, що один з сеансів утримує клямку і формується чергу в очікуванні її звільнення. У результаті робота з максимальною швидкістю неможлива. Досить одного невірно працюючого програми для істотного зниження продуктивності всіх інших додатків. Одне невелике додаток, що не використовує зв'язуються змінні, призводить з часом до видалення з розділяється пулу необхідних SQL-операторів інших добре налаштованих додатків. Досить ложки дьогтю, щоб зіпсувати бочку меду.

При використанні пов'язують змінних будь-який сеанс, що видає той же самий запит, буде використовувати вже скомпільований план виконання з бібліотечного пулу. Підпрограма компілюється один раз, а використовується багаторазово. Це дуже ефективно, і саме таку роботу користувачів припускає СУБД. При цьому не тільки використовується менше ресурсів (частковий розбір вимагає набагато менше ресурсів), але і заскочки утримуються значно менше часу, і потрібні набагато рідше. Це підвищує продуктивність і масштабованість.

Щоб хоч приблизно зрозуміти, наскільки це суттєво може позначитися на продуктивності, досить виконати дуже простий тест:

tkyte@TKYTE816> alter system flush shared_pool;

System altered.


Тут я починаю з "порожнього" розділяється пулу. Якщо буде потрібно виконувати цей тест багаторазово, доведеться очищати пул, що розділяється кожен раз, інакше представлений нижче оператор SQL, в якому не використовуються зв'язуються змінні, виявиться в кеші і буде виконуватися дуже швидко.

tkyte@TKYTE816> set timing on
tkyte@TKYTE816> declare
2 type rc is ref cursor;
3 l_rc rc;
4 l_dummy all_objects.object_name%type;
5 l_start number default dbms_utility.get_time;
6 begin
7 for i in 1 .. 1000
8 loop
9 open l_rc for
10 “select object_name
11 from all_objects
12 where object_id = ” // i;
13 fetch l_rc into l_dummy;
14 close l_rc;
15 end loop;
16 dbms_output.put_line
17 ( round( (dbms_utility.get_time-l_start)/100, 2 ) //
18 ” seconds…” );
19 end;
20 /
14.86 seconds…

PL/SQL procedure successfully completed.


У цьому коді використовується динамічний SQL для запиту одного рядка з подання ALL_OBJECTS. Він генерує 1000 унікальних запитів із значеннями 1, 2, 3, … і так далі, жорстко заданими в конструкції WHERE. На моєму ноутбуці з процесором Pentium 300 Мгц для його виконання було потрібно близько 15 секунд (швидкість виконання на різних машинах може бути різною).

Тепер зробимо те ж саме з використанням пов'язують змінних:

tkyte@TKYTE816> declare
2 type rc is ref cursor;
3 l_rc rc;
4 l_dummy all_objects.object_name%type;
5 l_start number default dbms_utility.get_time;
6 begin
7 for i in 1 .. 1000
8 loop
9 open l_rc for
10 “select object_name
11 from all_objects
12 where object_id = :x”
13 using i;
14 fetch l_rc into l_dummy;
15 close l_rc;
16 end loop;
17 dbms_output.put_line
18 (round( (dbms_utility.get_time-l_start)/100, 2 ) //
19 ” seconds…”);
20 end;
21 /
1.27 seconds…

PL/SQL procedure successfully completed.


У цьому коді використаний такий самий алгоритм. Єдина зміна – замість жорстко заданих значень 1, 2, 3 … і так далі в запиті використовується пов'язують змінна. Результати дуже вражаючі. Код не тільки виконується набагато швидше (розбір запитів вимагав більше часу, ніж їх реальне виконання!), Але і дозволяє більшій кількості користувачів одночасно працювати з системою.

Виконання операторів SQL без пов'язують змінних багато в чому подібно перекомпіляції підпрограми перед кожним викликом. Уявіть собі передачу клієнтам такого вихідного коду мовою Java, що перед будь-яким викликом методу класу їм необхідно викликати компілятор Java, компілювати клас і викидати згенерований байт-код відразу після виконання методу. При необхідності виклику того ж методу в подальшому їм довелося б робити те ж саме – компілювати, запускати і викидати байт-код. У своїх програмах ніхто так не надходить – не робіть цього і в СУБД.

У розділі 10 ми розглянемо способи визначити, чи використовуються зв'язуються змінні, різні варіанти їх застосування, підтримувану СУБД можливість автоматичної підстановки пов'язують змінних і т.д. Ми також розглянемо особливий випадок, коли використання пов'язують змінних небажано.

Часто, як і в даному проекті, – переписати існуючий код так, щоб використовувалися зв'язуються змінні, є єдино можливим виходом. Отримується в результаті код працює на кілька порядків швидше і у багато разів збільшується кількість підтримуваних системою одночасно працюючих користувачів. Для цього, однак, потрібно багато часу і зусиль. Справа не в тому, що використовувати зв'язуються змінні складно або при цьому часто роблять помилки, – проблема в тому, що з самого початку цього не робили, і тому довелося переглянути і змінити практично весь код. Розробникам не довелося б платити таку ціну, якщо б вони з першого дня розуміли принципову важливість використання в додатку пов'язують змінних.

Особливості управління одночасним доступом


Управління одночасним доступом – це те, чим відрізняються різні СУБД. Саме це відрізняє СУБД від файлової системи і одну СУБД від іншої. Для програміста важливо, щоб його додаток бази даних коректно працювало в умовах одночасного доступу, і саме це постійно забувають перевіряти. Прийоми, чудово працюють в умовах послідовного доступу, працюють набагато гірше при одночасному їх застосуванні сесіями. Якщо не знати досконало, як в конкретній СУБД реалізовані механізми управління одночасним доступом, то:


Зверніть увагу: я не пишу "можливо, буде …" або "ви ризикуєте …" – Всі ці проблеми точно будуть. Все це ви отримаєте, навіть не уявляючи, що саме відбувається. При неправильному управлінні одночасним доступом буде порушена цілісність даних, оскільки те, що працює окремо, буде працювати не так, як передбачалося, в багатокористувацької середовищі. Додаток буде працювати повільніше, оскільки доведеться чекати доступу до даних. Можливість масштабування буде втрачена через проблеми з блокуванням і конфліктів блокувань. В міру ускладнення запитів до ресурсу чекати доведеться все довше і довше. Можна провести аналогію з митним переходом на кордоні. Якщо машини приїжджають по одній, рівномірно, з передбачуваною частотою, ніяких черг немає. Якщо ж одночасно приїде багато машин, починають формуватися черги. Причому час очікування зростає нелінійно по відношенню до довжини черги. З певного моменту більше часу співробітників йде на "наведення порядку" в черзі, ніж на митний огляд машин (у разі СУБД ми говоримо про планування процесів і перемиканні контексту).

Проблеми одночасного доступу виявляти найскладніше – труднощі порівнянні з налагодженням многопотоковой програми. Програма може відмінно працювати в керованою, штучному середовищі отладчика, але постійно "Злітати" в "реальному світі". Наприклад, в умовах інтенсивних звернень може виявитися, що два потоки одночасно змінюють одну і ту ж структуру даних. Такого роду помилки дуже складно виявляти і виправляти. Якщо додаток тестувалося тільки в одного користувача режимі, а потім впроваджується в середовищі з десятками одночасно звертаються користувачів, цілком ймовірно прояв болючих проблем з одночасним доступом.

У наступних двох розділах буде представлено два невеликих прикладу того, як нерозуміння особливостей управління одночасним доступом може зруйнувати дані або знизити продуктивність і масштабованість додатки.

Реалізація блокування


СУБД використовує блокування, щоб в кожний момент часу ті чи інші дані могли змінюватися тільки однієї транзакцією. Говорячи простіше, блокування – це механізм забезпечення одночасного доступу. За відсутності певної моделі блокування, що запобігає одночасна зміна, наприклад, одного рядка, багатокористувацький доступ до бази даних просто неможливий. Однак при надмірному або неправильному блокуванні одночасний доступ теж може виявитися неможливим. Якщо користувач або сама СУБД блокує дані без необхідності, то працювати одночасно зможе менша кількість користувачів. Тому розуміння призначення блокування і способів його реалізації в використовуваної СУБД принципово важливо для створення коректних і масштабованих додатків.

Принципово важливо також розуміти, що в різних СУБД блокування реалізовано по-своєму. В одних – використовується блокування на рівні сторінок, в інших – на рівні рядків; в деяких реалізаціях виконується ескалація блокувань з рядкового на сторінковий рівень, в інших – не виконується, а в деяких СУБД використовуються блокування читання, в інших – ні, в одних СУБД реалізується рівень ізольованості транзакцій SERIALIZABLE за допомогою блокування, а в інших – через узгоджені з читання представлення даних (без встановлення блокувань). Ці невеликі відмінності можуть перерости у величезні проблеми, пов'язані з продуктивністю, або навіть призвести до виникнення помилок в додатках, якщо не розуміти їх особливостей.

Нижче наведені принципи блокування в СУБД Oracle.


Ці факти необхідно враховувати при розробці додатків, однак слід пам'ятати, що ці принципи використовуються тільки в Oracle. Розробник, не розуміє, як використовується СУБД забезпечує одночасний доступ, неминуче зіткнеться з проблемами цілісності даних (особливо часто це відбувається, коли розробник переходить з іншого СУБД на Oracle, або навпаки, і не враховує у додатку відмінності механізмів забезпечення одночасного доступу).

Один з побічних ефектів прийнятого в СУБД Oracle "неблокуючим" підходу полягає в тому, що якщо дійсно необхідно забезпечити доступ до рядка не більше ніж одного користувача в кожен момент часу, то саме розробнику необхідно вжити для цього певні зусилля. Розглянемо наступний приклад. Один розробник показував мені щойно завершену ним програму планування ресурсів (навчальних класів, проекторів і т.д.), що знаходиться в стадії впровадження. Ця програма реалізувало бізнес-правило, що запобігає виділення ресурсу більш ніж одній особі на будь-який період часу. Тобто, додаток містило спеціальний код, який перевіряв, що ніхто з користувачів не зажадав ресурс на той же період часу (принаймні розробник думав, що його код це перевіряє). Код звертався до таблиці планів і, якщо в ній не було рядків з перекриваються тимчасовим інтервалом, вставляв у неї новий рядок. Отже, розробник просто працював з парою таблиць:

create table resources
( resource_name varchar2(25) primary key, … );
create table schedules
( resource_name varchar2(25) references resources,
start_time date,
end_time date );

І перш ніж зарезервувати на певний період, скажімо, навчальний клас, додаток виконувало запит вигляду:

select count(*)
from schedules
where resource_name = :room_name
and (start_time between :new_start_time and :new_end_time
or
end_time between :new_start_time and :new_end_time)

Він здавався розробнику простим і надійним: якщо повернуто значення 0, навчальний клас можна займати; якщо повернуто ненульове значення, значить, навчальний клас на цей період вже кимось зайнятий. Ознайомившись з використовуваним алгоритмом, я підготував простий тест, що показує, яка помилка буде виникати при реальній експлуатації програми. Цю помилку буде вкрай складно виявити і тим більше встановити її причину, – кому-то може навіть здатися, що це помилка СУБД.

Я запропонував його колезі сісти за сусідній комп'ютер, перейти на той самий екран і попросив на рахунок три обох натиснути на кнопку Go і спробувати зарезервувати клас на одне й те саме час. Обидва змогли це зробити – Те, що прекрасно працювало в ізольованому середовищі, не спрацювало в середовищі на багато користувачів. Проблема в цьому випадку була викликана неблокуючим читанням в Oracle. Жоден з сеансів не блокував інший. Обидва сеансу просто виконували представлений вище запит і застосовували алгоритм резервування ресурсу. Вони обидва могли виконувати запит, перевіряючий зайнятість ресурсу, навіть якщо інший сеанс уже почав змінювати таблицю планів (це зміна невидимо для інших сеансів до фіксації, тобто до тих пір, коли вже надто пізно). Оскільки сеанси ніколи не намагалися змінити одну і ту ж рядок у таблиці планів, вони ніколи і не блокували один одного, внаслідок чого бізнес-правило не спрацьовувало так, як очікувалося.

Розробнику необхідний метод реалізації даного бізнес-правила в багатокористувацької середовищі, спосіб, який гарантує що в кожний момент часу лише один сеанс резервує даний ресурс. У даному випадку рішення полягало в програмному впорядкування доступу – крім представленого вище запиту count (*), необхідно було спочатку виконати:

 select * from resources where resource_name =: room_name FOR UPDATE;

Раніше в цій главі розглядався приклад, коли використання конструкції FOR UPDATE призводило до проблем, але в цьому випадку вона забезпечує коректну роботу бізнес-правила. Ми просто блокуємо ресурс (навчальний клас), використання якого планується, безпосередньо перед його резервуванням, до виконання запиту до таблиці планів, що вибирає рядки для даного ресурсу. Блокуючи ресурс, який ми намагаємося зарезервувати, ми гарантуємо, що жоден інший сеанс у цей же час не змінює план використання ресурсу. Йому доведеться чекати, поки наша транзакція не буде зафіксована – після цього він зможе побачити зроблене в ній резервування. Можливість перекриття планів, таким чином, усунена. Розробник повинен розуміти, що в багатокористувацької середовищі іноді необхідно використовувати ті ж прийоми, що і при багатопотоковому програмуванні. У даному випадку конструкція FOR UPDATE працює як семафор. Вона забезпечує послідовний доступ до конкретної рядку в таблиці ресурсів, гарантуючи, що два сеанси одночасно не резервують ресурс.

Цей підхід забезпечує високу ступінь паралелізму, оскільки резервуються ресурсів можуть бути тисячі, а ми всього лише гарантуємо, що сеанси змінюють конкретний ресурс по черзі. Це один з небагатьох випадків, коли необхідно блокування вручну даних, які не повинні змінюватися. Потрібно вміти розпізнавати ситуації, коли це необхідно, і, що не менш важливо, коли цього робити не потрібно (приклад, коли не потрібно, наведено далі). Крім того, такий прийом не блокує читання ресурсу іншими сеансами, як це могло б відбутися в інших СУБД, завдяки чому забезпечується висока масштабованість.

Подібні проблеми призводять до серйозних наслідків при перенесенні додатки з однієї СУБД на іншу (до цієї теми я повернуся трохи пізніше), яких розробникам складно уникнути. Наприклад, при наявності досвіду розробки для іншої СУБД, в якій друкарські сеанси блокують читають, і навпаки, розробник може покладатися на це блокування як захищає від подібного роду проблем – саме так все і працює в багатьох СУБД, відмінних від Oracle. У Oracle пріоритет відданий одночасності доступу, і необхідно враховувати, що в результаті все може працювати по-іншому.

У 99 відсотках випадків блокування виконується непомітно, і про нього можна не піклуватися. Але залишився 1 відсоток треба навчитися розпізнавати. Для вирішення цієї проблеми немає простого переліку критеріїв типу "У такому-то випадку треба зробити те-то". Потрібно розуміти, як додаток буде працювати в багатокористувацької середовищі і що він буде робити в базі даних.

Багатоверсійність


Ця тема дуже тісно пов'язана з управлінням одночасним доступом, оскільки створює основу для механізмів управління одночасним доступом в СУБД Oracle – Oracle використовує модель багатоваріантної узгодженості з читання при одночасному доступі. У розділі 3 ми більш детально розглянемо технічні аспекти багатоверсійності, але по суті це механізм, за допомогою якого СУБД Oracle забезпечує:


Це дві дуже важливі концепції СУБД Oracle. Термін багатоверсійність стався від того, що фактично СУБД Oracle може одночасно підтримувати безліч версій даних в базі даних. Розуміючи сутність багатоверсійності, завжди можна зрозуміти результати, отримані з бази даних. Найбільш простий з відомих мені способів продемонструвати багатоверсійність в Oracle:

tkyte@TKYTE816> create table t
2 as
3 select * from all_users;

Table created.

tkyte@TKYTE816> variable x refcursor

tkyte@TKYTE816> begin
2 open 😡 for select * from t;
3 end;
4 /

PL/SQL procedure successfully completed.

tkyte@TKYTE816> delete from t;

18 rows deleted.

tkyte@TKYTE816> commit;

Commit complete.

tkyte@TKYTE816> print x

USERNAME USER_ID CREATED
—————————— ———- ———
SYS 0 04-NOV-00
SYSTEM 5 04-NOV-00
DBSNMP 16 04-NOV-00
AURORA$ORB$UNAUTHENTICATED 24 04-NOV-00
ORDSYS 25 04-NOV-00
ORDPLUGINS 26 04-NOV-00
MDSYS 27 04-NOV-00
CTXSYS 30 04-NOV-00

DEMO 57 07-FEB-01

18 rows selected.


У цьому прикладі ми створили тестову таблицю T і заповнили її даними з уявлення ALL_USERS. Ми відкрили курсор для цієї таблиці. Ми не вибирали дані за допомогою цього курсору, просто відкрили його.

Пам'ятайте, що при відкритті курсору сервер Oracle не "відповідає" на запит, він нікуди не копіює дані при відкритті курсору (уявіть, скільки часу зажадало б відкриття курсору для таблиці з мільярдом рядків у протилежному випадку). Курсор просто відкривається і дає результати запиту по ходу звертання до даних. Іншими словами, він буде читати дані з таблиці під час вилучення їх через курсор.

У тому ж (або в іншому) сеансі ми потім видаляємо всі дані з таблиці. Більше того, ми навіть фіксуємо (COMMIT) це видалення. Рядків більше немає – чи не так? Насправді їх можна витягти за допомогою курсору. Фактично, результуюче безліч, повертається командою OPEN, було зумовлено в момент відкриття курсору. Ми не прочитали при відкритті курсору жодного блоку даних таблиці, але результат виявився жорстко зафіксованим. Ми не зможемо дізнатися цей результат, поки не дістанемо дані, але з точки зору нашого курсору результат цей незмінний. Справа не в тому, що СУБД Oracle скопіювала всі ці дані в інше місце при відкритті курсору; дані зберіг оператор delete, помістивши їх в область даних під назвою сегмент відкоту.

Саме в цьому і полягає узгодженість з читання, і якщо не розуміти, як працює схема багатоверсійності в Oracle і які її наслідки, ви не тільки не зможете скористатися всіма перевагами СУБД Oracle, але й не створите коректних програм для Oracle, що гарантують цілісність даних.

Давайте розглянемо наслідки використання багатоверсійності узгодженості запитів з читання та неблокіруемой читань. Для тих, хто не знайомий з багатоверсійності, представлені нижче результати можуть здатися дивними. Для простоти припустимо, що в кожному блоці даних (це мінімальна одиниця зберігання в СУБД) зчитується таблиці зберігається всього один рядок і що виконується повний перегляд таблиці.

У запитуваної таблиці зберігаються баланси банківських рахунків. Вона має дуже просту структуру:

create table accounts
( account_number number primary key,
account_balance number
);

У реальному таблиці рахунків будуть сотні тисяч рядків, але для простоти ми будемо розглядати таблицю за все з чотирма рядками (більш детально ми розглянемо цей приклад у розділі 3):
























Рядок


Номер рахунку


Баланс рахунку

1 123 500,00 $
2 234 250,00 $
3 345 400,00 $
4 456 100,00 $

Потрібно створити звіт, який наприкінці банківського дня дозволяє визначити кількість грошей в банку. Це робиться за допомогою гранично простого запиту:

select sum(account_balance) from accounts;

Звичайно, в даному прикладі відповідь очевидна – 1250 $. Однак що станеться, якщо ми прочитаємо рядок 1, а при зчитуванні рядків 2 і 3 з одного з банкоматів буде виконана транзакція, переводить 400 $ зі рахунку 123 на рахунок 456? Наш запит прочитає 500 $ у рядку 4 і видасть результат 1650 $, чи не так? Звичайно, цього треба уникнути, тому що подібний результат помилковий – ніколи такого балансу по рахунках в базі даних не було. Потрібно зрозуміти, як СУБД Oracle уникає подібних ситуацій і чим відрізняються використовувані при цьому методи від використовуваних у всіх інших СУБД.

Практично в будь-який інший СУБД для отримання "узгодженого" і "коректного" відповіді на цей запит необхідно блокувати або всю таблицю, за якою йде підсумовування, або рядка по мірі їх читання. Це запобіжить зміна результату іншими сеансами в ході його отримання. Якщо заблокувати всю таблицю, буде отриманий результат, відповідає стану бази даних в момент початку виконання запиту. Якщо блокувати дані в міру читання (така колективна блокування читання запобігає зміни, але не читання даних іншими сеансами), буде отриманий результат, відповідає стану бази даних в момент завершення виконання запиту. Обидва ці методу істотно знижують можливості одночасного доступу. Блокування таблиці запобігає будь-які зміни таблиці під час виконання запиту (для таблиці з чотирьох рядків цей період дуже короткий, але для таблиць з сотнями тисяч рядків запит може виконуватися кілька хвилин). Метод "блокування по ходу читання" запобігає зміна вже прочитаних і оброблених даних і потенційно може призводити до взаємних блокувань виконання вашого запиту та інших змін.

Як вже було сказано, ви не зможете в повному обсязі використовувати переваги СУБД Oracle, якщо не розумієте концепцію багатоверсійності. У СУБД Oracle багатоверсійність використовується для отримання результатів, відповідних моменту початку виконання запиту, при цьому не блокується жодного рядка (поки транзакція з переказу грошей змінює рядки 1 і 4, вони будуть заблоковані від інших змін, але не від читання, виконуваного, наприклад, нашим запитом SELECT SUM…). Фактично в СУБД Oracle немає "поділюваних блокувань читання", типових для інших СУБД, – вони в неї просто не потрібні. Всі переборні перешкоди для одночасного доступу були усунені.

Отже, як же СУБД Oracle отримує коректний, узгоджений результат (1250 $) при читанні, не блокуючи даних, іншими словами, не заважаючи одночасного доступу? Секрет – в механізмі виконання транзакцій, використовуваному в СУБД Oracle. При зміні даних Oracle створює записи в двох різних місцях. Один запис потрапляє в журнал повторного виконання, де Oracle зберігає інформацію, достатню для повторного виконання, або "накату", транзакції. Для оператора вставки це буде вставляється рядок. Для оператора видалення це буде запит на видалення рядка в слоті X блоку Y файлу Z. І так далі. Інший запис – Це запис скасування, що поміщається в сегмент відкоту. Якщо транзакція завершується невдало і повинна бути скасована, СУБД Oracle буде читати "попередній" образ із сегмента відкоту, відновлюючи необхідні дані. Крім скасування транзакцій, СУБД Oracle використовує сегменти відкоту для скасування змін до блоках при їх читанні, тобто для відновлення даних блоку на момент початку виконання запиту. Це дозволяє читати дані незважаючи на блокування та отримувати коректні, узгоджені результати, не блокуючи дані.

Частина 2

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*