ПЕРЕЛІК ДИСЦИПЛІН:
  • Адміністративне право
  • Арбітражний процес
  • Архітектура
  • Астрологія
  • Астрономія
  • Банківська справа
  • Безпека життєдіяльності
  • Біографії
  • Біологія
  • Біологія і хімія
  • Ботаніка та сільське гос-во
  • Бухгалтерський облік і аудит
  • Валютні відносини
  • Ветеринарія
  • Військова кафедра
  • Географія
  • Геодезія
  • Геологія
  • Етика
  • Держава і право
  • Цивільне право і процес
  • Діловодство
  • Гроші та кредит
  • Природничі науки
  • Журналістика
  • Екологія
  • Видавнича справа та поліграфія
  • Інвестиції
  • Іноземна мова
  • Інформатика
  • Інформатика, програмування
  • Юрист по наследству
  • Історичні особистості
  • Історія
  • Історія техніки
  • Кибернетика
  • Комунікації і зв'язок
  • Комп'ютерні науки
  • Косметологія
  • Короткий зміст творів
  • Криміналістика
  • Кримінологія
  • Криптология
  • Кулінарія
  • Культура і мистецтво
  • Культурологія
  • Російська література
  • Література і російська мова
  • Логіка
  • Логістика
  • Маркетинг
  • Математика
  • Медицина, здоров'я
  • Медичні науки
  • Міжнародне публічне право
  • Міжнародне приватне право
  • Міжнародні відносини
  • Менеджмент
  • Металургія
  • Москвоведение
  • Мовознавство
  • Музика
  • Муніципальне право
  • Податки, оподаткування
  •  
    Бесплатные рефераты
     

     

     

     

     

     

         
     
    MSSQL 2005 (Yukon) - робота з чергами і асинхронна обробка даних
         

     

    Інформатика, програмування

    MSSQL 2005 (Yukon) - робота з чергами і асинхронна обробка даних

    Ivan Bodyagin

    Декілька загальних слів

    Цей нарис присвячено труднощів, з якими стикається розробник при спробі побудувати повноцінне асинхронне додаток, а також тієї посильну допомогу, яку може надати компанія Microsoft у цій нелегкій підприємстві завдяки наступній версії SQL Server з кодовим ім'ям Yukon і супутніх бібліотек.

    Безумовно, тема асинхронності дуже велика, і її неможливо охопити в одній статті, навіть якщо обмежуватися виключно рамками SQL Server-а, але я й не ставив перед собою завдання охопити все. Тут буде дано короткий огляд нової функціональності, яка з'явиться в MS SQL Server з виходом нової версії, найбільш важливою на мій погляд, і кілька прикладів використання цієї функціональності ...

    асинхронність

    Я кілька разів підкрадався до своїх знайомих і намагався несподівано запитати, що ж вони розуміють під асинхронність -- з'ясувалося, що всі розуміють, що це таке, але ніхто не може дати чіткої визначення.

    Так що ж таке асинхронність? Формальне визначення говорить, що це така характеристика процесів, що не збігаються в часі. Коротко, ємко, але незрозуміло ... Якщо ж спростити, то це можливість звалити частину роботи на когось іншого, а за результатом прийти потім, займаючись в проміжку своїми справами. І це відноситься як до однотипної роботі, так і до абсолютно різноплановою. Напевно, доречно було б вдатися до аналогії ... Припустимо, існує два способи віддати автомобіль в сервіс - синхронний і асинхронний. У синхронному варіанті можна приїхати на сервіс, поспілкуватися з механіком, загнати разом з ним машину в бокс, допомогти йому дружньою порадою, розповісти кілька свіжих анекдотів або почути їх від нього ... Теж загалом-то з користю проведений час. Якщо ж просто віддати ключі механіку при зустрічі і забрати машину, коли вона буде готова, провівши проміжок часу між двома цими подіями на свій розсуд, то це вже буде асинхронний спосіб ремонту ...

    Так само йде справа і в додатку. Потік можна распараллеліть на декілька потоків, як виконують одну й ту ж роботу, розділивши її на частини, так і абсолютно різну, наприклад, одночасно зчитувати щось з диска і вирішувати обчислювальні задачі.

    Інтуїтивно всі відчувають, що асинхронні програми під усіх відношеннях краще, але чомусь пишуть їх тільки в самому крайньому випадку.

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

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

    Проте Microsoft з випуском SQL Server 2005 і супутніх клієнтських бібліотек вирішив взяти частину цієї нудної роботи на себе.

    Асинхронні можливості сервера

    Для початку розглянемо, які можливості надає новий SQL Server сам по собі, без урахування можливостей клієнта і ADO.Net 2.0

    Почнемо, мабуть, здалеку. Фраза про роботу з чергами недарма винесена в назву цієї статті, так як механізм черг є невід'ємною частиною гарної реалізації асинхронності. Як правило, в асинхронному програмі є, умовно кажучи, «основний потік», який роздає деякі завдання «службовим потоків» і згодом забирає від них результати. Одним з важливих моментів є саме процес видачі завдання і отримання результатів. Справа в тому, що службові потоки не завжди знаходяться в розпорядженні головного. Тому є багато причин. Число потоків, з якими можна працювати ефективно, обмежена, і вільних потоків, готових виконати завдання, може просто не бути, або ж службовий потік може зовсім перебувати на іншій машині ... Якщо основний потік при обміні інформацією буде взаємодіяти безпосередньо зі службовими, то йому доведеться чекати службові потоки, а це підриває саму ідею асинхронності. І тут на допомогу приходять черги. Вони дозволяють розірвати залежність основного потоку від службових. Основному потоку досить помістити завдання в чергу і йти далі у своїх справах. Службові потоки, як тільки у них з'явиться така можливість, заберуть з черги завдання і будуть його виконувати, після чого знову ж таки помістять результати у відповідну чергу, щоб основний потік забрав їх, коли в нього з'явиться час. І навіть якщо службовий потік знаходиться на іншій машині, то за наявності черг не важко ініціювати транспортну транзакцію при вступі завдання в чергу, знову ж таки не змушуючи основний потік чекати

    У майбутньої версії SQL Server є готовий механізм черг (як одна з основних частин Service Broker). Однак якщо з якихось причин розробнику доводиться будувати чергу самостійно, то і для цього з'явилися деякі нові можливості.

    Output або розширення обробки черг

    Присвячена цієї функціональності розділ у розділі BOL «Нові можливості» називається Queue Processing Extensions - розширення обробки черг. Але насправді, це всього лише один з найбільш очевидних застосувань даного механізму. Суть функціональності полягає в наступному: тепер у ряду операторів, що займаються маніпуляцією з даними, а саме INSERT, UPDATE і DELETE, з'явилося нове ключове слово OUTPUT. За допомогою цієї конструкції можна після виконання оператора отримати результат його роботи та перенаправити цей результат в яку-небудь таблицю або просто повернути клієнтського додатку. Якщо говорити простіше, з'явився доступ до триггерным псевдотаблічкам inserted і deleted прямо із запиту. Іншими словами, тепер є можливість дізнатися, що ж саме було змінено DML-оператором, не звертаючись зайвий раз до сервера.

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

    Найпростіший приклад може виглядати приблизно так:        

    - створюємо тестову таблицю:   

    -   

    CREATE TABLE OutputTest (   

    ID int IDENTITY,   

    [Time] datetime default   getDate (),   

    Limit as Left (Data, 8),   

    Data char (50))      

    - власне, перевіряємо,   як воно працює:   

    -   

    INSERT INTO OutputTest (Data) OUTPUT INSERTED .* VALUES (NewID ())      

    - насолоджуємось результатом:   

    -   

    ID Time Limit Data   

    1 2005-05-21 19:40:43.087   5C1D39E9   5C1D39E9-8E28-4ED7-B5E8-938EA84FFE18     

    Як легко помітити, вся магія полягає в конструкції OUTPUT INSERTED .*, зверніть увагу, що в тестовій таблиці присутня колонка identity, колонка зі значенням за замовчуванням і колонка з обчислюваним значенням. При цьому дані, отримані з inserted-таблички, містять вже пораховані значення в цих колонках. Тобто табличка inserted містить фактичні значення вставляються даних вже після внутрішніх обчислень, однак тригери не враховуються, тобто відпрацювання output відбувається після внутрішніх обчислень, але перед виконанням тригерів. Наприклад, при наявності тригера INSTEAD OF на таблиці, що змінює цю таблицю операція в output поверне всі дані, які повинні там бути, навіть якщо в результаті роботи тригера ніяких змін не відбудеться.        

    ПОПЕРЕДЖЕННЯ   

    Насправді тут є один виняток,   якщо на табличці висить тригер INSTEAD OF, то значення IDENTITY в OUTPUT   INSERTED обчислено не буде.     

    До вибіркою output можна застосовувати різні вирази і підзапит, у тому випадку якщо вони повертають одне значення. Наприклад, якщо є необхідність у момент зміни запису дізнатися, скільки часу пройшло з моменту останнього оновлення, запит може виглядати приблизно так:        

    UPDATE   OutputTest SET Data = newID (), [Time] = GetDate ()   

    OUTPUT   DateDiff (ss, DELETED. [Time], INSERTED. [Time]) Diff     

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

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

    DECLARE @ tmp_output TABLE (   

    ID_t int, Time_t datetime,   

    Limit_t nvarchar (8),   

    Data_t nvarchar (50))      

    INSERT INTO OutputTest (Data)   

    OUTPUT inserted .* INTO   @ tmp_output   

    VALUES (newid ())     

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

    На них не повинно бути призначено тригерів. У принципі, тригер може бути призначений, але повинен бути в змозі Disabled.

    Вони не повинні бути пов'язані зовнішнім ключем з іншими таблицями, і на цю таблицю не повинні посилатися зовнішні ключі.

    Не повинно бути CHECK-обмежень і правил (rules) в стані Enabled.

    Висновок не може бути перенаправлений під view або функцію. Очевидно, це пов'язано із забороною тригерів в цільової таблиці.

    Складно сказати, з чим ці обмеження пов'язані, але, швидше за все, це викликано прагненням максимально полегшити і прискорити вставку даних. Оскільки механізм output працює до тригерів, на досить низькому рівні, то бажання позбутися важкої функціональності цілком зрозуміло.

    І ще кілька загальних обмежень механізму output:

    Секціонірованние уявлення і віддалені таблиці не можуть бути джерелом output.

    У разі оператора INSERT джерелом output не можуть бути view.

    Порядок записів, які видаються output, не гарантується.

    Якщо виклик output відбувається в тригері, і висновок з output НЕ перенаправляється до таблиці, то, очевидно, опція disallow results from triggers не повинна бути встановлена, в іншому випадку відбудеться виняток.

    Також, якщо не відбувається перенаправлення виводу output, то змінна таблиця не повинна мати активних тригерів на дану операцію модифікації. Наприклад, якщо відбувається INSERT c output, без перенаправлення виводу в таблицю, то тригерів на INSERT бути не повинно, хоча UPDATE і DELETE тригери цілком можуть бути.

    Як це використовувати

    Як зрозуміло з назви даної функціональності, Microsoft пропонує використовувати її для роботи з чергами. По-перше, конструкцію DELETE ... OUTPUT зручно застосовувати для розгрібання черги, виконуючи читання та видалення прочитаної записи одним рухом.        

    DELETE   FROM output_test OUTPUT deleted .*     

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

    По-друге, механізм output можна використовувати і для формування черг. У даному випадку це сильно нагадує роботу звичайного тригера, у завдання якого входить складати змінені дані в окрему таблицю. Але тригер буде спрацьовувати для всіх змін без виключення, а output можна використовувати лише в певних DML-операторах. Іншими словами, якщо таблиця може змінюватися з двох місць, то тригер буде спрацьовувати в обох випадках, а output можна використовувати тільки для одного з них. Та й використовувати output, напевно, буде простіше, ніж тригер.

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

    Однак основну проблему комунікації між асинхронними процесами такий підхід не вирішує - це всього лише невеликий синтаксичний сахарок, дещо полегшує роботу з власноруч написаними чергами, але не більше того. В ідеалі ж механізм обміну, як уже говорилося, повинен забезпечувати транзакційного, відсутність дублікатів, автоматичну роботу з чергами, обробку груп повідомлень, гарантувати черговість і т.д ... Все це багатство було реалізовано і ввійшло в наступну версію SQL Server під ім'ям Service Broker.

    Service Broker

    При першому погляді на Service Broker виникає питання: "А що у що, власне, вбудовано, СУБД в підсистему повідомлень або навпаки. :)"

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

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

    РАДА   

    Книга, цілком присвячена Service   Broker і виходить в самий найближчий час, називається The Rational Guide To   SQL Server 2005 Service Broker Beta Preview   (http://www.mannpublishing.com/Catalog/BookDetail.aspx?BookID=37). Вона   написана Роджером Уолтером (Roger Wolter), основна робота якого   полягає саме в керівництві групою, що розробляє цей самий Service   Broker. Так що краще за нього навряд чи хто-небудь про цей механізм розповість. :)     

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

    Робота з Service Broker-му реалізована через набір об'єктів, які управляються за допомогою звичайних DDL операторів CREATE, ALTER, DROP -- нічого нового. Команди по роботі з цими об'єктами також є невеликим DML розширенням T-SQL. Наприклад, команда отримання повідомлення повертає звичайний Реляційний набір даних і мало чим відрізняється від SELECT, так що тут не повинно бути ніяких складнощів. У посилці та отриманні повідомлення беруть участь наступні об'єкти (тут наведені далеко не всі, лише необхідний мінімум):

    QUEUE (черга): Service Broker використовує черги для того, щоб не було залежності між відправником і одержувачем повідомлення. Відправник просто поміщає повідомлення в чергу і йде займатися своїми справами, не чекаючи одержувача, а доставку повідомлення покладає на плечі власне Service Broker-а, будучи впевненим, що той впорається. Одержувач ж може забрати й обробити повідомлення, коли йому буде зручно, знаючи, що його діяльність жодним чином не впливає на ефективність роботи відправника, а всі повідомлення збудовані в належному порядку. При цьому є можливість запустити кілька «одержувачів» одночасно, домагаючись тим самим паралельної обробки черги для досягнення більшої ефективності.

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

    Насправді, діалог є окремим випадком спілкування (conversation). Conversation - це не об'єкт, а більш нізкоуровневе поняття -- постійний, надійний канал зв'язку. У MSSQL 2005 діалог - єдиний тип спілкування, але в наступних версіях обіцяють додати monolog (монолог), односпрямований потік один-ко-багатьом, і, можливо, щось ще. Однак у поточної версії діалог і спілкування можна вважати синонімами.

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

    MESSAGE TYPE (тип повідомлення) Будь-яке повідомлення повинно бути асоційоване з певним типом. Це мітка, яка передається разом з повідомленням і дозволяє одержувачеві зрозуміти, якого типу повідомлення до нього приїхало. За бажанням, якщо повідомлення є XML, то позначка може бути асоційована з довільною XML-схемою. У цьому випадку при отриманні проводиться перевірка відповідності повідомлення цій схемі, і якщо повідомлення перевірку не проходить, то воно відкидається.

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

    SERVICE (сервіс): Сервіс пов'язує кілька контрактів з чергою. Назва сервісу є синонімом для кінцевої точки діалогу. Таким чином, контракт визначає, повідомлення яких типів можуть бути послані через чергу через діалог, а сервіс є кінцевою точкою, через яку повідомлення потрапляє в чергу.

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

    Відправник і одержувач - досить умовні поняття, вони мають сенс тільки на етапі створення каналу спілкування. Після того, як канал створений, повідомлення можуть вільно гуляти в обох напрямках. Іншими словами, відправник - це та сторона, яка створює діалог. При цьому саме створення діалогу не викликає створення фізичного каналу зв'язку, канал створюється лише в той момент, коли необхідно доставити перше повідомлення, і утримується до тих пір, поки діалог не завершиться.

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

    Тепер саме час приступити до практичних експериментів. Для початку створимо всі необхідні об'єкти:        

    - тип повідомлення, просто   текст, для простоти без жодних перевірок і xml   

    -   

    CREATE MESSAGE TYPE [TestType] VALIDATION = NONE      

    - тепер можна створити   контракт, який дозволяє повідомлення цього типу   

    - для будь-якої зі сторін   

    -   

    CREATE CONTRACT [TestContract] ([TestType] SENT BY ANY)      

    - для відправляє боку   необхідно створити чергу   

    - і сервіс на основі цієї   черги   

    -   

    CREATE QUEUE [SourceQueue]   

    CREATE SERVICE [SourceService] ON QUEUE [SourceQueue]      

    - Для приймаючої сторони   так само потрібно створити приймаючу чергу   

    - і приймає сервіс,   причому приймає сервіс обов'язково   

    - повинен мати контракт,   хоча для відправляє це не обов'язково   

    -   

    CREATE QUEUE [TargetQueue]   

    CREATE SERVICE [TargetService] ON QUEUE [TargetQueue] ([TestContract])     

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

    Спочатку займемося одержувачем. Для отримання повідомлення служить команда RECEIVE, що сильно нагадує звичайний SELECT, тільки замість імені таблиці вказується ім'я черги. До слова, і команда SELECT для черги працює (оскільки з точки зору бази даних чергу - це звичайна таблиця), показуючи її вміст, але нічого з неї не видаляючи. Команда ж RECEIVE вибирає дані з черги, видаляючи вибрані повідомлення. Однак якщо чергу порожня, RECEIVE відпрацює вхолосту і поверне порожній набір даних, а хотілося б, щоб хтось чатував чергу, і RECEIVE б спрацьовувала, як тільки в черги щось з'явиться. На щастя, у цьому немає нічого складного, досить обернути RECEIVE в WAITFOR. Отже, в окремому вікні виконуємо наступну команду для своєчасного отримання повідомлення:        

    WAITFOR (RECEIVE   cast (message_body as nvarchar (MAX)) FROM [TargetQueue])     

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

    DECLARE @ convHandler uniqueidentifier      

    - початок діалогу   

    -   

    BEGIN DIALOG @ convHandler   

    FROM SERVICE [SourceService]   

    TO SERVICE 'TargetService'   

    ON CONTRACT [TestContract];      

    - посилка повідомлення   

    -   

    SEND ON CONVERSATION @ convHandler   

    MESSAGE TYPE [TestType]   (N'Message !!!')      

    - завершення діалогу   

    -   

    END CONVERSATION @ convHandler     

    Якщо після відправлення повідомлення повернутися у віконце, де чекали його отримання, можна побачити, що повідомлення успішно отримано.

    Варто зауважити, що TargetService при створенні діалогу взято в лапки, а SourceService - ні. Справа в тому, що TargetService може бути створений на зовсім іншому сервері, і просто відсутня на сервер, де починається діалог такого сервісу.

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

    Асинхронні тригери

    Тепер розглянемо, як можна використати комунікативні можливості Service Broker на сервері. Наприклад, можна використовувати його для реалізації асинхронних тригерів, причому не тільки для DML-і DDL-операцій, але і для подій, що відслідковуються профайлером (trace events), і якщо DML-тригери доведеться реалізовувати частково із застосуванням звичайних, то для DDL-тригерів і подій профайлера передбачений спеціальний механізм.

    Асинхронні DML-тригери

    Почнемо з DML, ідея яких, загалом-то, має бути очевидна. Припустимо, у нас є дуже велика таблиця (Very_Big_Table), для звітів за якою треба періодично вважати якісь агрегатні значення. Оскільки таблиця дуже велика, то агрегати вважаються дуже довго. Звіт не завжди повинен бути актуальним, але завжди - узгодженим, і будуватися повинен максимально швидко. Це означає, що в ідеалі агрегати повинні бути пораховані заздалегідь. Робити перерахунок даних у звичайному тригері накладно для операцій оновлення, так як розрахунок агрегатів відбувається довго, як вже було згадано. І тут на допомогу приходить Service Broker. У звичайному тригері на зміну Very_Big_Table створюється діалог (строго кажучи, мало що заважає створити діалог заздалегідь, хіба що проблеми із запам'ятовуванням мітки при розгортанні) і відправляється повідомлення, про те що таблиця змінилася. Це займає мінімум часу, а змінює процес йде далі займатися своїми справами. Одержувач ж починає не поспішаючи перераховувати ці занудні агрегати, щоб до моменту, коли знадобиться звіт, все вже було готово.

    Ось як це може виглядати. Спочатку створимо необхідні тестові таблички:        

    CREATE TABLE Very_Big_Table (ID int IDENTITY, Data bigint, [Time]   DateTime)   

    GO      

    - заповнимо таблицю даними   

    -   

    INSERT INTO Very_Big_Table (Data, [Time])   

    SELECT object_id, create_date FROM sys.objects   

    GO      

    - табличка для   обчисленого агрегату   

    -   

    CREATE TABLE Big_Aggregate (Agg bigint, [Time] DateTime)   

    GO      

    - Ну і проініціалізіруем   її   

    -   

    INSERT INTO Big_Aggregate (Agg, [Time])   

    SELECT Sum (Data), GetDate () FROM Very_Big_Table     

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

    CREATE TRIGGER AsyncAggregate ON Very_Big_Table   

    FOR INSERT, UPDATE, DELETE   

    AS   

    DECLARE @ convHandler   uniqueidentifier      

    BEGIN DIALOG @ convHandler   

    FROM SERVICE [SourceService]   

    TO SERVICE 'TargetService'   

    ON CONTRACT [TestContract];      

    SEND ON CONVERSATION   @ convHandler   

    MESSAGE TYPE [TestType]   (N'The data hase been changed ')      

    END CONVERSATION @ convHandler   

    GO     

    Передавати в повідомленні ніякої цінної інформації нам не треба, так як приймаюча сторона повинна просто дізнатися, про те, що таблиця помінялася, а ознакою цього є сам факт доставки повідомлення. Більш того, в даній ситуації немає необхідності навіть викликати команду SEND, так як закриття діалогу (END CONVERSATION) викликає посилку спеціального повідомлення про це сумну подію на приймаючу сторону. Однак у реальній ситуації може знадобитися передати деяку інформацію, і якщо її необхідно структурувати, то доведеться скористатися XML.

    Тепер займемося приймаючою стороною. Для початку створимо процедуру перерахунку агрегату:        

    CREATE PROCEDURE AggRecalculate AS      

    - очищення черги   

    -   

    RECEIVE * FROM [TargetQueue]      

    - невелика затримка для імітації   дійсно довгого розрахунку   

    -   

    WAITFOR DELAY '00: 00:02 '   

    UPDATE Big_Aggregate   

    SET Agg = (SELECT SUM (Data)   FROM Very_Big_Table),   

    [Time] = GetDate ()   

    GO     

    Процедура готова, але є одна проблема. Як виконати цю процедуру при появі повідомлення в черзі? Звичайно, можна, як і раніше, обернути RECEIVE в WAITFOR, але в цьому випадку хтось повинен запус процедуру, щоб вона почала чекати повідомлень з черги. І мало того, повідомлення-то у нас може бути не одна. Отже, потрібно щоб після отримання хтось активізував процедуру знову. Іншими словами, потрібен певний монітор, який стежив би за станом черзі і при появі в ній повідомлень викликав нашу процедуру. До щастя, все вже зроблено за нас. Такий монітор мається на Service Broker, і для його включення досить трохи змінити параметри черги, вказавши, яку процедуру треба викликати при отриманні повідомлення:        

    ALTER QUEUE [TargetQueue]   

    WITH ACTIVATION (   

    STATUS = ON,   

    PROCEDURE_NAME =   AggRecalculate,   

    MAX_QUEUE_READERS = 1,   

    EXECUTE AS OWNER)     

    Ключове слово тут, звичайно ж, ACTIVATION, тобто активація. Однак якщо параметр STATUS у неї виставлений в OFF, вона не спрацює. Як нескладно здогадатися що в списку PROCEDURE_NAME вказується ім'я процедури, яка буде викликана при активації, а в EXECUTE AS - від імені якого користувача ця процедура буде викликана. Параметр MAX_QUEUE_READERS визначає максимальну кількість процедур, яка одночасно може бути запущено для розгрібання черги. Якщо під час роботи процедури надійшли нові повідомлення, то запускається ще один примірник цієї процедури, і так до максимального дозволеного кількості або спустошення черги.

    Тепер все готове для експерименту, можна приступати. Спочатку відновимо нашу «очень_большую_табліцу», і відразу заберемо дані з таблички агрегатів, потім почекаємо трохи і знову заберемо агреговані дані, щоб побачити, як вони змінилися після роботи процедури перерахунку, автоматично запущеної Service Broker-му.        

    UPDATE Very_Big_Table SET Data = Data + 10 WHERE ID = 1   

    SELECT * FROM Big_Aggregate   

    WAITFOR DELAY '00: 00:05 '   

    SELECT * FROM Big_Aggregate      

    - Результат:   

    -   

    Agg Time   

    --------------------   -----------------------   

    76577545551 13:44:37.987   

    76577545561 13:59:24.630     

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

    Асинхронні DDL і SQL-Trace тригери (Event Notification)

    Для реалізації асинхронних тригерів на DDL-операції і події профайлера існує спеціальний механізм, Event Notification (повідомлення про подію).        

    ПРИМІТКА   

    Треба враховувати, що у зв'язку з   асинхронність даного механізму породили це повідомлення зміни в базі   або на сервері, не скасоване у випадку відкату повідомлення, як це було б в   DDL-тригері. Вони - вже доконаний факт. І ще один нюанс: оскільки   події профайлера працюють поза транзакцій, то навіть якщо зміна на   сервер, що викликало посилку повідомлення, що не матиме успіх, то саме   повідомлення все одно буде доставлене до одержувача, однак для DDL-подій   це не працює, тому що DDL-операції працюють в рамках транзакції і в разі   скасування DDL транзакції повідомлення надіслано не буде.     

    Як не складно здогадатися, цей механізм відстежує події, на які є передплатники, та надсилає відповідне повідомлення. Для того щоб механізм повідомлень заробив, досить створити чергу і сервіс одержувача з наперед визначеним контрактом [http://schemas.microsoft.com/SQL/Notifications/PostEventNotification], всі інше - і контракт, і діалог, і сервіс з чергою відправника, вже реалізовано. Потім треба створити об'єкт EventNotification, що зв'язує потрібне подія з сервісом - і готово. На практиці, припустимо, для асинхронного аудиту підключень до сервера і відключень від оного, це може виглядати наступним так:        

    - спочатку створимо чергу   одержувача, при бажанні   

    - тут можна призначити   процедуру обробки нових повідомлень   

    -   

    CREATE QUEUE [LoginQueue]   

    GO      

    - потім необхідно створити   сервіс зі спеціальним контрактом,   

    - в якому вже є   необхідні типи повідомлень   

    -   

    CREATE SERVICE [LoginService] ON QUEUE [LoginQueue] (   

    [http://schemas.microsoft.com/SQL/Notifications/PostEventNotification])   

    GO      

    - Ну а тепер можна   створити і сам Event Notification, що зв'язує   

    - серверні події з   сервісом доставки повідомлення   

    -   

    CREATE Event Notification auditLogin   

    ON SERVER FOR Audit_Login,   Audit_Logout   

    TO SERVICE 'LoginService',   'current database'     

    Тут 'current database' - це константа, яка говорить про те, що в якості механізму доставки буде використовуватися примірник Service Broker-а, встановлений в поточній базі. Вказівка цього екземпляра є параметром при створенні повідомлення.

    Ось, загалом-то, і все. Тепер залишилося тільки отримати гарний XML з черги після чергового входу/виходу з системи і придумати, що з ним робити. Отримати його можна все тим же, вже знайомим способом:        

    RECEIVE   cast (message_body as xml) FROM [LoginQueue]     

    Сам XML це результат виклику тієї ж самої функції Eventdata (), що використовується і в DDL-тригери.

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

    Асинхронні можливості клієнтських додатків

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

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

    Асинхронне виконання запитів

    Крім виконання асинхронних операцій на сервері і роботи з чергами, в ADO.Net 2.0 додана спеціальна функціональність по асинхронної роботі з БД з боку клієнта. Ця функціональність підтримується тільки провайдером SqlClient (OleDB і решта його не підтримують). Зате (приємна новина) з боку сервера жорстких обмежень немає, і асинхронні запити будуть працювати з SQL Server від Microsoft, починаючи з сьомої версії, при умови, що режим роботи з ними - не Shared Memory, а операційна система -- Windows 2000/XP/2003.

    Строго кажучи, і в попередній версії Framework-а організація асинхронної обробки даних не була такою вже великою проблемою. Однак при цьому доводилося виділяти додатковий потік і блокувати його в очікуванні виконання запиту. Для клієнтських програм цей процес не становить великої проблеми, але для серверних рішень, які змушені обслуговуватибезліч клієнтів одночасно, це може послужити джерелом неприємностей. Вся ж принадність даної реалізації полягає в тому, що додатковий потік не створюється. Замість цього для досягнення належного ефекту використовуються можливості асинхронного мережного введення/виводу. Замість того, щоб створювати новий потік і примушувати його чекати синхронної операції введення/виводу для надсилання запиту до БД і отримання відповіді, використовуються асинхронні можливості мережевого протоколу Windows 2000/XP/2003 (з цим і пов'язані обмеження на використання ОС і режиму Shared Memory для версій сервера нижче SQL 2005), що дозволяють одному потоку відіслати запит і йти далі у своїх справах.

    Для виконання запитів в асинхронному режимі розробники додали кілька методів, однак дотримувалися мінімалістській політики, і додали лише самі необхідні методи. Тому далеко не всі синхронні варіанти Execute * обзавелися асинхронними аналогами, точніше, тільки три з них. Це ExecuteReader, що отримав BeginExecuteReader і EndEsecuteReader, ExecuteNonQuery, що отримав BeginExecuteNonQuery і EndExecuteNonQuery, і ExecuteXmlReader, що отримав, як не складно здогадатися BeginExecuteXmlReader і EndExecuteXmlReader. Передбачається наступна схема застосування цього багатства: метод Begin * отримує всі вхідні параметри та передає їх для виконання серверу, залишаючи після себе потоку на пам'ять лише якийсь об'єкт, який реалізує спеціальний інтерфейс на ім'я IAsyncResult. Цей інтерфейс може бути використаний для відстеження стану виконання операції. З методу ж End * за допомогою цього залишеного об'єкта можна отримати назад результат виконання запиту, коли той буде готовий.        

    РАДА   

    Інтерфейс IAsyncResult далеко не новий,   як і супутні методи синхронізації. Вся ця механіка повинна бути добре   знайома тим, хто стикався з асинхронними делегатами, асинхронної роботою з   файлами та іншими багато-завданнями в. Net.     

    Одним з ключових моментів при виконанні асинхронних операцій є спосіб, за допомогою якого ініціатор операції може дізнатися про її завершення. Для такого відповідального заходу ADO.Net надає цілих три способи:

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

    Об'єкт синхронізації. Об'єкт IAsyncResult, повертається всіма методами Begin *, містить властивість WaitHandle з подією, і ця подія може бути використано такими примітивами синхронізації, як WaitHandle.WaitAny і WaitHandle.WaitAll. Це дозволяє викликає потоку чекати виконання декількох або всіх запущених операцій, причому не тільки запитів до БД, але й, можливо, інших асинхронних процедур чи викликів ОС, які також обслуговуються вищезгаданими примітивами.

    Опитування (Polling).

         
     
         
    Реферат Банк
     
    Рефераты
     
    Бесплатные рефераты
     

     

     

     

     

     

     

     
     
     
      Все права защищены. Reff.net.ua - українські реферати ! DMCA.com Protection Status