Microsoft SQL Server 2000

Олексій Шуленін, Журнал "Windows 2000 Magazine"

Нові можливості для розробника додатків. Частина 2

Розширені властивості об'єктів

SQL Server 2000 дозволяє розширювати набір метаданих, жорстко асоційованих з об'єктами бази, до яких, зокрема, відносяться назву поля таблиці, його тип і т. д. Створення додаткових властивостей являє собою універсальний спосіб зберігання коментарів до таблиць, масок вводу до полів, правил форматування і т. д. Таким чином можна, з одного боку, краще документувати базу даних, а з іншого, – визначати клієнтський інтерфейс та іншу специфічну для користувача або програми інформацію на рівні сервера. При перейменування об'єкта властивості зберігаються. Для створення нової властивості потрібно вказати його ім'я (стандартного типу sysname довжиною 128 символів) і значення, яке має тип SQL_VARIANT і може містити величини розміром до 7500 байт. Як приклад створимо користувальницьке властивість під назвою «Опис», що містить значення «Це адреса клієнта» для поля Address таблиці dbo.Customers.

Звертаю увагу читачів, що рівні level0, level1, level2 описують ієрархічний шлях до об'єкта. У даному випадку ієрархія має вигляд «Користувач БД -> Таблиця -> Поле таблиці». Ієрархія може мати не більше трьох рівнів. В якості об'єктів верхнього (нульового) рівня можуть виступати користувач бази або визначений користувачем тип. Очевидно, що залежно від об'єкта верхнього рівня список об'єктів рівня, наступного за ним по ієрархії, може змінюватися. Так, для таблиці це можуть бути поля, індекси, обмеження, тригери; для представлення – поля і тригери instead-of; для процедури – Параметри і т. д. Повний перелік ієрархій приводиться в документації по SQL Server у розділі «Using Extended Properties of Database Objects». Імена розширених властивостей є унікальними в межах реалізації даній ієрархії, тобто ми можемо створити точно таке ж властивість для іншого поля тієї ж таблиці, для такого ж поля іншої таблиці, а також для іншого користувача, тому що, як відомо, dbo.Customers і [Інший користувач БД]. Customers розглядаються як дві різні таблиці. Розширені властивості об'єктів БД зберігаються в системній таблиці sysproperties. Оновлення розширених властивостей проводиться з допомогою збереженої процедури.

Користувальницькі властивості можна створювати для об'єктів будь-якого рівня, наприклад для таблиць.

Для одного і того ж об'єкта можна створити скільки завгодно різних властивостей.

Системна функція fn_listextendedproperty дозволяє визначити значення для користувача властивості по його імені та шляхи до об'єкту:

select * from:: fn_listextendedproperty ('Опис', 'user', 'dbo', 'table', 'Customers', 'column', 'Phone')

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

select * from:: fn_listextendedproperty (NULL, 'user', 'dbo', 'table', 'Customers', 'column', 'Phone')

Аналогічно, якщо NULL вказати замість імені об'єкта, функція поверне всі властивості об'єктів даного рівня. Наприклад, отримати список розширених властивостей для всіх полів таблиці Customers можна так:

select * from:: fn_listextendedproperty (NULL, 'user', 'dbo', 'table', 'Customers', 'column', NULL)

NULL в типі рівня означає виведення властивостей об'єкта більш високого рівня ієрархії, в даному випадку – таблиці Customers:

select * from:: fn_listextendedproperty (NULL, 'user', 'dbo', 'table', 'Customers', NULL, NULL) 

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

Адміністратор бази даних може видалити всі користувальницькі властивості об'єктів бази, очистивши таблицю sysproperties.

Кодова сторінка та порядок сортування

У попередній версії SQL Server була реалізована підтримка Unicode, що дозволило зберігати в одному полі таблиці текстову інформацію на різних мовах. Тим не менш кодова сторінка та порядок сортування визначалися на рівні сервера. У SQL Server 2000 такого обмеження немає, Unicode collation можна задавати аж до окремого поля таблиці. Поняття «коллація» (collation) об'єднує в собі кодову сторінку і порядок сортування. SQL Server 2000 підтримує два типи коллацій. В основному це ті, що встановлюються разом з операційною системою, плюс ще порівняно невелике число коллацій приходить разом із самим SQL Server для забезпечення сумісності з попередніми версіями. Імена останніх починаються з «SQL_». І ті й інші можна подивитися за допомогою функції fn_helpcollations ():

select * from ::fn_helpcollations()

Для перегляду властивостей коллаціі використовується функція CollationProperty. Властивостями коллаціі є: LCID – ідентифікатор в наборі коллацій Windows (очевидно, дорівнює NULL для коллацій, що належать власне SQL Server), ComparisonStyle – спосіб порівняння символів різних регістрів для коллацій Windows (дорівнює NULL для бінарних коллацій і коллацій SQL Server) і CodePage – відповідна коллаціі кодова сторінка для типів char, varchar, text, що не підтримують Unicode (на відміну від nchar, nvarchar, ntext). Наприклад,

CollationProperty ('Cyrillic_General_CS_AI', 'CodePage') = 1251: select collationproperty ('Cyrillic_General_CS_AI', 'CodePage'), collationproperty ('Cyrillic_General_CS_AI', 'LCID')

Як і раніше, при установці SQL Server пропонується вибрати коллацію рівня сервера, проте тепер вона може мінятися від бази до бази і від поля до поля. Коллація рівня бази даних визначається при її створенні.

Якщо коллація БД не була вказана явно, як така за замовчуванням буде використовуватися загальна серверна коллація. Аналогічно іде справа з таблицями. Коллацію можна задавати явно для окремих полів, в іншому випадку вони автоматично успадковують коллацію рівня бази даних.

У даному випадку поле Фрукт2 буде мати коллацію Latin1_General_CI_AI:

select column_name, collation_name from information_schema.columns where table_name = N'Фрукти 'and column_name in (N'Фрукт1', N'Фрукт2 ')

Заповнимо нашу таблицю.

Тепер очевидно, що, хоча поля Фрукт1 і Фрукт2 містять, здавалося б, однакові дані, ORDER BY веде себе по-різному в залежності від коллаціі поля:

select [Фрукт1] from [Фрукти] order by Фрукт1 select [Фрукт1] from [Фрукти] order by Фрукт2

Поміняємо коллацію поля Фрукт2 на Cyrillic_General_Bin:

alter table [Фрукти] alter column [Фрукт2] nvarchar (20) collate Cyrillic_General_Bin

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

select [Фрукт1] from [Фрукти] order by Фрукт1 select [Фрукт1] from [Фрукти] order by Фрукт1 collate Сyrillic_General_CI_AI

Дізнатися коллацію рівня сервера можна, запитавши відповідне властивість:

select serverproperty ('ServerName'), serverproperty ('Collation')

Змінити її можна тільки, застосувавши rebuild до бази даних master.

Нове в індексах

У попередній версії SQL Server з'явилася підтримка паралелізму всередині запиту. Обробка єдиного запиту декількома процесорами одночасно дозволяє помітно скоротити час виконання аналітичних запитів по великим об'ємам даних. Детальніше про побудову паралельних планів див., наприклад, «Процесор запитів Microsoft SQL Server 7.0. Про деякі стратегіях оптимізатора при побудові складних, паралельних і розподілених планів »/ / СУБД № 3 за 1998 р. Ця можливість одержала подальший розвиток в SQL Server 2000. Зокрема, операції створення індексів тепер також можуть виконуватися в паралельному режимі. При цьому на основe побудованої (можливо, не шляхом повного сканування, а лише на представницькій вибірці) статистики розподілу ключів оптимізатор оцінює діапазони фрагментів, на які повинні бути розбиті дані відповідно до можливим ступенем паралелізму. Залучаються додаткові потоки, кожен з яких будує індексний структуру з використанням фільтра свого діапазону, після чого на координує потоці відбувається об'єднання результатів в єдиний індекс. Паралелізм всередині запиту дозволяє домогтися гарної масштабованості операцій побудови індексів в залежності від кількості процесорів.

При визначенні індексів SQL Server 2000 дозволяє визначити порядок зростання чи зменшення, в якому повинні зберігатися дані полів, що входять в індекс, наприклад: create index IX_Empl on Employees (LastName asc, BirthDate desc, HireDate desc)

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

select * from Employees order by LastName asc, BirthDate desc, HireDate desc

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

select * from Employees order by LastName asc, BirthDate asc, HireDate asc

Для одноколоночних індексів задавати порядок взагалі не має сенсу, оскільки в цьому випадку SQL Server однаково ефективно здійснює навігацію по індексу незалежно від напрямку. Приклад:

drop index Employees.IX_Empl create index IX_Empl_1 on Employees (BirthDate desc)

Тоді як для запиту

select * from Employees order by  BirthDate desc,

так і для

select * from Employees order by BirthDate АSC

буде взятий індекс IX_Empl_1, причому порядок сортування не впливає на швидкість виконання запиту. Функція INDEXKEY_PROPERTY () дозволяє визначити порядок, відповідно до якого те чи інше поле входить в індекс.

Вже існуючі індекси можна задіяти при побудові нових. Знову ж таки хочу повернутися до ситуації, коли для таблиці Employees існував тільки індекс IX_Empl. Включимо в Query Analyzer параметр Show execution plan (меню Query, не плутати з Estimated execution plan) і створимо індекс

create index IX_Empl_2 on Employees (BirthDate desc, HireDate desc).

У панелі результатів на закладці Execution plan представлений план побудови індексу. Видно, що, замість сканування таблиці цілком, SQL Server заради економії часу обмежився переглядом тільки сторінок індексу IX_Empl. Те ж відбудеться і при створенні індексу по полях LastName asc, BirthDate desc, з тією лише різницею, що подальша сортування тут, очевидно, не буде потрібно.

У SQL Server 7.0 з'явилися поля, не зберігаються в таблиці, а обчислювані на основі операторів і функцій (у тому числі визначених користувачем – у версії 2000) над звичайними полями. У SQL Server 2000 по обчислюваним полях можна створювати індекси. Розберемо ситуації, в яких це нововведення може виявитися корисним. Створимо табличку і виконаємо запит, який, як легко здогадатися, призведе до повного сканування Ords. Постараємося його оптимізувати. Для цього потрібно ввести в таблицю обчислюване поле і створити з нього індекс, після чого виконаємо запит, еквівалентний попередньому.

Тепер запит виконується з використанням індексу iOrdYear, що набагато краще. Спробуємо оцінити кількісно це «набагато», об'єднавши обидва запиту і звернувши увагу на колонку TotalSubtreeCost, з якої видно, що індекс по обчислюваному полю дозволив майже на порядок скоротити вартість виконання запиту.

Тепер, як водиться, трохи про сумне, а саме про вимоги, яким має відповідати вираження в обчислюваному полі, щоб по ньому можна було побудувати індекс. Вираз не повинно включати полів з інших таблиць, а також функцій над векторними аргументами на кшталт SUM (), AVG () і т. д. Крім того, воно повинно бути детермінованим і точним, тобто кожного разу при одних і тих самих аргументах повертати однакове значення і не містити операцій над речовими типами. Перевірити дотримання цих умов можна за допомогою функції COLUMNPROPERTY (). Наприклад, наступне обчислюване поле не є детермінованим, так що буде повернуто значення 0. Вираз може бути детермінованим, але неточним.

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

Дане вище визначення детермінованого вираження потребує додаткових коментарів, тому що з нього випливає, що практично жодна заслуговує на увагу функція не є строго детермінованої, за винятком вироджених випадків типу константи. Справді, не можна, наприклад, визнати детермінованим вираз x / y, оскільки воно приховано залежить від установки SET ARITHABORT. На результат складання двох рядків впливає SETCONCAT_NULL_YIELDS_ NULL, перетворення рядка в дату неявно визначається установкою SET DATEFORMAT і т. д. Поет-му, кажучи про детермінізм в SQL Server, домовилися вважати, що ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER, ANSI_NULLS, ANSI_ PADDING, ANSI_WARNINGS виставлені в ON, a NUMERIC_ ROUNDABORT – в OFF. В іншому випадку ні один вираз не буде детермінованим.

Можна зробити те ж саме на рівні сервера для всіх призначених для користувача сесій, хоча, коли ми відкриваємо з'єднання по ODBC або OLE DB, всі ці параметри вже виставлені в правильне з точки зору детермінізму положення, за винятком ARITHABORT, так що, в принципі, в масштабах сервера підправляти має сенс тільки його: sp_configure 'user options', 64. До речі, що може статися з існуючими індексами по обчислюваним полях і іншими об'єктами, що залежать від вимог детермінізму, якщо який-небудь з перерахованих вище параметрів встановити в «недозволене» значення? Відповідь на це питання буде дано в наступному розділі, а поки читач має право розглядати його як самостійна вправа.

Індексовані подання

Класичне уявлення (view) можна розглядати як свого роду псевдонім для конкретного оператора SELECT, що дозволяє задіяти його у запитах приблизно так само, як звичайну таблицю. У поданні, за великим рахунком, зберігається тільки визначення SQL-запиту, який виконується всякий раз при зверненні до даного подання. У цьому сенсі індексовані подання (indexed views), відомі також як матеріалізовані, йдуть далі, зберігаючи не тільки визначення, а й результати запиту, які підтримуються в актуальному стані при модифікації даних. Таким чином, індексовані подання дійсно ведуть себе аналогічно індексами, і SQL Server 2000 дозволяє матеріалізувати звичайне подання саме шляхом створення індексу для нього. Подання може містити агрегатні функції, оператори зв'язування таблиць або їх комбінацію. Очевидно, що для матеріалізації результатів подання першого створюється для нього індекс повинен бути: а) кластерним (щоб в листі зберігалися самі дані, а не посилання на них) і б) унікальним (щоб однозначно ідентифікувати запис при внесенні змін). Варто мати на увазі, що кожна матеріалізація пов'язана з витратами, у зв'язку з виділенням місця під результати і деяким (взагалі кажучи, досить незначним) уповільненням операцій оновлення в об'єктах, що беруть участь у визначенні подання. Однак при невиправданому збільшенні числа матеріалізованих уявлень в базі даних ці витрати в сумі можуть проте надавати помітний негативний вплив на ресурсомісткість і продуктивність програми. Словом, подібно до того, як не має сенсу створювати індекси на всі випадки життя, не слід негайно кидатися матеріалізувати все і вся. Індексовані подання, як правило, виявляються найбільш ефективними в OLAP-додатках (задачах звітності, підтримки прийняття рішення), так як дозволяють скоротити час виконання складних аналітичних запитів на читання по масивним обсягами даних за рахунок зберігання проміжних результатів зв'язування таблиць, результатів групування і агрегатів. Саме тому підтримка індексованих уявлень реалізована тільки в корпоративній редакції (Enterprise Edition). Індексовані подання дозволили SQL Server 2000 продемонструвати найкращий за продуктивністю результат у 1699,8 аналітичних запитів на годину для 100-гігабайтної бази даних за тестами ТРС-Н (інформація на момент написання статті). SQL Server працював на сервері Compaq ProLiant 8000, оснащеному вісьмома процесорами Intel Pentium III Xeon 700 МГц. У "номінації" 300 Гбайт SQL Server 2000 належить саме економічне рішення – 200 $ / QphH при загальній продуктивності в 1402 аналітичних запиту на годину (результат показаний на платформі НР NetServer LXr 8500). Теоретично застосування індексованих уявлень не дасть виграшу в OLTP-додатках, тобто в задачах обліку та накопичення даних, для яких характерні часті короткі оновлюючі транзакції. Іншим прикладом невиправданого використання індексованих уявлень можуть служити угруповання по високоселективним полях, так як тут розмір подання буде по порядку величини відповідати самої таблиці.

Подання має бути жорстко прив'язана до схеми даних, тобто має бути створено Сreate VIEW … WITH SCHEMABINDING AS … У цьому випадку операції зміни входять до нього об'єктів (ALTER TABLE, ALTER COLUMN, ALTER VIEW і т. д., не кажучи вже про DROP) неприпустимі, якщо вони зачіпають визначення цього подання. Функції, які беруть участь в індексованих поданні, повинні бути детермінованими, тому що інакше може виникнути протиріччя, наприклад, між GETDATE () і результатом, збереженим вчора. Оптимізатор приймає рішення про використання індексу для подання, якщо:

Проілюструємо вплив параметра прив'язки подання до схеми (див. Лістинг 17).

Перше поле спокійно видаляється з таблиці Products, оскільки нашому schemabinding view воно нічого не дає. Друге поле, навпаки, бере участь у визначенні подання, тому видалити його SQL Server не дасть. До речі, для того щоб створювати уявлення з параметром WITH SCHEMABINDING, користувач повинен мати права REFERENCES на всі об'єкти, що беруть участь у визначенні подання. Перевіряється це, наприклад, так:

select permissions(object_id(‘Products’))  & 0x4

Матеріалізуючись наше уявлення шляхом створення унікального кластерного індексу. Ще раз нагадаю, що ця функціональність підтримується тільки в SQL Server 2000 Enterprise Edition (можна перевірити select @ @ Version або подивитися перший запис у журналі SQL Server, яку він створює при старті).

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

Розглянемо типову аналітичну задачу. Припустимо, потрібно знайти найбільш успішно продукт, що продається за деякий період часу і суму виручки по ньому. Порівняємо ефективність вирішення задачі з використанням індексованого уявлення і без нього. Для індексованого подання вартість виконання запиту (див. поле TotalSubtreeCost в першому рядку видачі плану) складе приблизно 0,0064.

«Дематеріалізуя» уявлення, видаливши первинний індекс ix. Це призведе до автоматичного видалення всіх інших пов'язаних з ним індексів і вплине на вартість попереднього запиту.

Округлити, отримаємо 0.1586, або майже в 25 разів більше, ніж при використанні індексу на виставу.

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

Розподілені подання

Розбиття логічно єдиного масиву даних з метою їх подальшої паралельної обробки є одним з найбільш поширених методів підвищення масштабованості. На сьогодні існує два основних типу паралельних архітектур, які витримали випробування часом і, мабуть, здатні зберегти свої позиції в найближчому майбутньому – це SMP і кластери. Про деякі можливості SQL Server з підтримки симетричною мультипроцессорной обробки, зокрема про паралелізм всередині запиту, я вже розповідав вище. На жаль, масштабованість SMP-систем обмежена необхідністю використання загальної пам'яті, дискового простору, пристроїв введення / виводу та інших ресурсів. Теоретично ми можемо як завгодно нарощувати систему вертикально, встановлюючи все більше процесорів, замінюючи їх більш швидкими моделями, розширюючи об'єм пам'яті, збільшуючи ємність дисків і т. д. Однак все одно необхідність доступу до загальних ресурсів залишиться слабкою ланкою, і в умовах зростання навантаження рано чи пізно настане момент, коли ця ланка почне стримувати загальну продуктивність системи. Такий же недолік властивий і кластерним архитектурам, розрахованим на використання поділюваних (shared) ресурсів, наприклад єдиного дискового масиву або простору пам'яті. Іноді в літературі їх називають shared everything, хоча це, звичайно, неправильно. Не існує систем типу «усе спільне», і навіть винятковий в цьому плані випадок – SMP, де будь-який процесор може «достукатися» до будь-пам'яті, виконувати будь-яку операцію читання-запису, переривати інші процесори і т. д. – має принаймні один несиметричний ресурс – кеш. Таким чином, правильніше охарактеризувати цю категорію паралельних систем як shared something (до неї входять і SMP, та кластери c поділом доступу до загальних дисків на основі DLM, та кластери ccNUMA) і протиставити їй концепцію систем з відсутністю загальних ресурсів (shared nothing). У відомому сенсі цієї концепції відповідають добре відомі кластери Microsoft Cluster Server (два вузли у разі Windows 2000 Advanced Server, чотири вузли під Windows 2000 DataCenter). Незважаючи на наявність фізично загального дискового масиву, кожен вузол в даний момент часу володіє на ньому фрагментом, не пересічним з іншими. Однак цей приклад не цілком вдалий, так як зараз я кажу про кластери в першу чергу з точки зору паралельної обробки, а не відмовостійкості. В ідеалі кластер типу shared nothing може складатися із звичайних комп'ютерів, що називається «з вітрини», без яких би то не було поділюваних ресурсів (у тому числі і на фізичному рівні) і спеціальних вимог до обладнання, інфраструктурі і т. п. Формування від множинних примірників програми, запущеного на кожному вузлі, єдиного образу системи зовні, узгодження і перерозподіл даних між екземплярами – ці та інші завдання треба вирішити розробникам, перш ніж адміністратори отримають можливість «розмазувати» єдину базу даних по декількох SQL Server у мережі. Проте такий підхід представляється набагато більш перспективним. Він не вимагає жорсткої залежно від апаратних рішень. Продуктивність кластеру підвищується простим додаванням в систему недорогого сервера споживчого рівня. Відсутність навантаження на загальні ресурси істотно розширює лінійна ділянка кривої масштабованості, що підтверджується нещодавніми результатами тестів ТРС-С для OLTP-додатків. На сьогодні SQL Server займає чотири з п'ять перших місць в таблиці абсолютних рекордів продуктивності. Його кращий результат у 262 244 транзакції в хвилину був досягнутий завдяки реалізації в SQL Server 2000 початкового етапу концепції shared nothing – розподілених фрагментованих уявлень (distributed partitioned views). У ході випробувань використовувався кластер з 12-ти серверів Compaq ProLiant 8500 по вісім процесорів Intel Pentium III Xeon 700 MГц кожен. Не маючи під рукою подібної конфігурації, я постараюся пояснити, як це працює, чисто умоглядно.

Насправді, практично все необхідне для підтримки розподілених представлень – прілінкованние сервери і розуміння процесором запитів, як фрагментовані дані, – реалізовано ще в попередній версії. Завдяки прілінкованним серверів з'явилася можливість пов'язувати в запитах локальні і видалені дані, що належать зовнішньому OLE DB-ресурсу (в тому числі іншому SQL Server). З іншого боку, припустимо, мої дані по продажах за місяць зберігаються в таблицях JanuarySales, …, DecemberSales, я будую подання щодо продажів за рік

create view YearSales as select * from JanuarySales union all … union all select * from DecemberSales, 

а потім запускаю запит

select * from YearSales where [Місяць] = 8.

Читання буде відбуватися не по всьому поданням в цілому, а тільки за таблицею AugustSales. Правда, для цього потрібно явно «дати зрозуміти», як фрагментовані дані, встановивши для таблиці JanuarySales обмеження check ([Місяць] = 1), FebruarySales – check ([Місяць] = 2) і т. д.

SQL Server 2000 дозволяє об'єднати ці дві можливості. Розділимо всі SQL Server у мережі на дві групи. До першої увійдуть сервери, службовці точками входу для користувачів, саме з ними ті будуть встановлювати з'єднання. На цих серверах дані зберігати необов'язково. Навпаки, друга група об'єднає ресурсні сервери, на яких зосереджені корпоративні бази даних. Кожна БД належить одного сервера. Користувачі можуть взагалі не підозрювати про існування цих серверів. У базах даних ресурсних серверів мають перебувати таблиці, які представляють собою непересічні горизонтальні фрагменти загальної множини даних. Умова фрагментації повинно бути прописано явно у вигляді обмеження CHECK на таблицю. На кожному сервері, службовців точками входу, повинні бути створені сервери, прілінкованние до серверів ресурсів. Після цього на точках входу створюються уявлення, що поєднують дані з ресурсних серверів. При обробці клієнтського запиту до подання оптимізатор, проаналізувавши обмеження, вже «знає», як влаштовані дані і де шукати той чи інший фрагмент. Виходячи з цього він будує розподілений план виконання так, що кожен ресурсний вузол отримує від нього підзапит, який належить тільки до його власних, фізично зберігаються на ньому даними. Результати обробки підзапитів відсилаються ресурсними вузлами назад на точку входу, якої залишається зібрати їх всі разом і повернути клієнту. Кожен SQL Server може одночасно виконувати функції точки входу і ресурсного сервера.

Ось, власне, і все. Як ми бачимо, поняття «кластер» носить в даному випадку чисто віртуальний характер. Нам не потрібно пов'язувати вузли як-небудь інакше, окрім як через локальну мережу, монтувати додаткове устаткування і т. п. Тепер я спробую відтворити сказане експериментально. Для цього буде потрібно два примірники SQL Server, що працюють на одній або різних машинах – не суть важливо. У моєму випадку це будуть SQL Server 2000 Enterprise Edition на ім'я Leshik1 і SQL Server 2000 Standard Edition на ім'я Leshik2 на тій же машині, що і перший. Leshik1 буде одночасно ресурсним сервером і точкою входу, а Leshik2 – чисто ресурсним сервером. Створимо непересічні фрагменти даних на обох ресурсних серверах.

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

На точці входу потрібно створити прілінкованний до ресурсів сервер і розподілене подання.

Імітуємо клієнта, який звернувся до точки входу (Leshik1):

select CustID, Contact, Company, Country, City from DistrCust where CustID between 'K' and 'P'

Графічний план виконання цього запиту складається з двох з'єднуються гілок. Одна починається з індексного пошуку по локальній таблиці CustAL, а інша – з віддаленого вкладені запити до CustMZ, який піде на сервер Leshik2 (див. значення SOURCE в аргументі оператора Remote Query). Процесор запитів очікує (Estimated row count), що з локального сервера буде отримано 10 записів, а з віддаленого – вісім, що відповідає дійсності (select count (*) from CustAL where CustID> = 'K' і select count (*) from Leshik2.Northwind.dbo.CustMZ where CustID <= 'P'). На етапі конкатенації відбувається об'єднання результатів з різних джерел. Таким чином, обробка даного запиту дійсно розпаралелюється по двох серверів.

Розглянемо інший приклад:

select CustID, Contact, Company, Country, City from DistrCust where CustID between 'U' and 'Y'

У цьому випадку повинен бути задіяний тільки Leshik2, оскільки Leshik1 не містить записів, що відповідають критерію. У той же час в плані виконання знову видно дві гілки і оператор Index Seek, як ніби є надія щось отримати від локального сервера. У чому справа? Щоб відповісти на це питання, потрібно згадати про автопараметрізацію. Подивіться на умову SEEK в аргументі оператора індексного пошуку. Воно виглядає приблизно так: [CustAL]. [CustID]> = Convert ([@ 1]) AND [CustAL]. [CustID] <= Convert ([@ 2]), тобто 'U' і 'Y' фігурують у плані не як константи, а як параметри. У процесі компіляції процесору запитів немає діла до конкретних значень параметрів, тому він не може показати в плані, з яких серверів знадобляться фрагменти, а з яких ні. Здається, що він їх хоче взяти відразу звідусіль. Процесор запитів вставляє в кожну гілку оператор Filter, щоб прийняти рішення під час виконання запиту. Аргументом для нього служить умова виду WHERE: (STARTUP EXPR (…)). Якщо такі оператори існують для кожного з фрагментів, значить, запит буде виконуватися коректно і ефективно (непотрібні сервери задіяні не будуть). Щоб у цьому переконатися, потрібно встановити SET STATISTICS PROFILE ON і повторити запит. Зверніть увагу на другу видачу слідом за результатом. Поле Executes для Clustered Index Seek буде дорівнює 0, як і поле Rows для всієї локальної гілки. Тобто в даному випадку реально працює тільки Leshik2, що й потрібно було довести.

Аналогічно відбувається оновлення даних.

Перш ніж модифікувати дані, слід переконатися, що MS DTC на точці входу стартував.

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

update DistrCust set CustID = ‘AAAAA’  where CustID = ‘TORTU’

призведе до автоматичного переміщення відповідного запису з таблиці CustMZ в СustAL.

Слід зауважити, що розподілені фрагментовані подання, призначені для збільшення продуктивності за рахунок розпаралелювання процесу обробки, на відмовостійкість ніяк не впливають. (На практиці для вирішення цього завдання можна запропонувати захистити кожного з учасників віртуальної серверної ферми за допомогою MSCS.) Крім того, дані розподіляються по серверів вручну. У разі виходу з ладу одного з вузлів або, навпаки, при додаванні нового сайту в віртуальний кластер потрібно перерозподіляти дані по серверів, що знову-таки неможливо без участі адміністратора. Однак потрібно пам'ятати про те, що це лише один з перших кроків до створення повнофункціональних кластерів shared nothing, і робота в цьому напрямку продовжує йти повним ходом.

Зміни в резервному копіюванні

У SQL Server 7.0 два параметри впливали безпосередньо на процес створення резервних копій. Установка truncate log on checkpoint змушувала неактивну частину журналу транзакцій очищатися при настанні події check point (ініційованому як адміністратором, так і сервером). Управляти періодичністю виникнення check point з ініціативи сервера адміністратор міг тільки побічно, через recovery interval – час, який відводиться SQL Server на процес відновлення БД після перезапуску. Очевидно, що чим менше recovery interval, тим менше транзакцій SQL Server може дозволити собі накопичувати в журналі і тим частіше має відбуватися подія check point. Друга установка – це select into / bulkcopy. Вона дозволяла використання (і часткове протоколювання) операцій масованого копіювання типу bcp, SELECT INTO, WRITETEXT, UPDATETEXT. Зрозуміло, що об'ємні поновлення могли переповнити журнал транзакцій, тому в ньому відображалися тільки факти виділення сторінок під результати подібних операцій. Відповідно, повернути їх було можна (забравши сторінки назад), а повторити – не можна (немає вмісту). У SQL Server 2000 реалізована декларативна модель протоколювання. Всього існує три режими: FULL, BULK_LOGGED і SIMPLE. Для новоствореної бази даних режим успадковується від model, в якій за замовчуванням може мати різні значення в залежності від редакції SQL Server. Режим відновлення відноситься до властивостей бази даних, його можна переглянути за допомогою select databasepropertyex ('model', 'Recovery') і поміняти через ALTER DATABASE … SET RECOVERY … Режим SIMPLE практично еквівалентний truncate log on check point. До його переваг можна віднести відносно невеликий розмір журналу транзакцій, резервні копії якого зі зрозумілих причин не робляться, так що в разі збою всі зміни з моменту останнього повного або диференціального резервного копіювання будуть втрачені. Режим FULL, навпаки, передбачає максимально повний запис змін і збереження в журналі зафіксованих транзакцій, що дає можливість відновлення на момент часу і в тому випадку, якщо один з файлів даних виявився зіпсованим. BULK_LOGGED нагадує повний режим з тією лише різницею, що масивні операції в ньому фіксуються по мінімуму, тобто записується тільки, які протягу (extents) були цими операціями порушені. При створенні резервної копії журналу транзакцій в нього записується вміст цих протяжений, що вимагає доступу до файлів даних. Якщо дані при цьому зіпсовані, остаточна резервна копія журналу транзакцій не буде створена і доконані зміни в ньому будуть втрачені. Поновлення на момент часу в цьому режимі не підтримується. На відміну від версії 7.0, не існує обмежень на використання будь-яких операторів Т-SQL залежно від режиму, наприклад SELECT … INTO може застосовуватися в кожному з них.

Підвищилася продуктивність процесу диференціального резервного копіювання. У SQL Server 2000 ведеться бітова карта протяжений, що зазнали змін з моменту останнього повного резервного копіювання, що позбавляє від необхідності сканування всієї БД в пошуках модифікованих даних. Крім того, передбачено диференціальне резервне копіювання не тільки всієї БД цілком, але і окремих вхідних в неї файлів. З'явилася можливість часткового відновлення повної резервної копії на рівні груп файлів. Таким чином, можна відновити, наприклад, окремий індекс або таблицю, асоційовані з певною групою файлів, не розгортаючи всю резервну копію. Була додана функція установки пароля на стрічку або конкретну резервну копію на ній: BACKUP DATABASE | LOG … WITH MEDIAPASSWORD = …, PASSWORD = … Потрібно мати на увазі, що установка пароля не шифрує самі дані і не може запобігти їх інтерпретацію засобами, що лежать нижче шару абстракції MTF, а також перезапис. Функція під назвою Snapshot Backup / Restore дозволяє інтегрувати в процес створення резервних копій та їх відновлення програмно-апаратні рішення незалежних виробників (наприклад, дзеркалювання або миттєве створення копій даних при їх запису), що підтримують інтерфейси VDI. У принципі, це еквівалентно створення повної резервної копії файлу або бази даних, однак дає переваги в швидкості (відновлення з дзеркального диска відбувається буквально за секунди) і в плані розвантаження сервера баз даних.

Створення сервера «теплого» резерву (log shipping) у попередніх версіях SQL Server здійснювалося за допомогою утиліт, що входять до BackOffice Resource Kit. Тепер ця можливість на правах базової функції включена в SQL Server 2000 Enterprise Edition. Якщо коротко, то вона полягає у автоматизації наступного процесу. Маємо два сервери: «бойовий» і резервний. Спочатку БД на резервному сервері повністю синхронізується з «бойових». Потім через певні проміжки часу на ньому робляться резервні копії журналу транзакцій, які відновлюються на резервному сервері. У разі виходу з ладу «бойового» сервера неузгодженість даних не буде перевищувати цього інтервалу. На відміну від «теплої» сервер «гарячого» резерву містить абсолютно актуальну копію даних. Він може бути реалізований, наприклад, за допомогою MSCS.

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

Вставка другому записі в MyTbl відбувається всередині іменованої транзакції. Оператор WITH MARK зберігає мітку в журналі транзакцій. Після цього можна додати ще третю запис і створити резервну копію журналу транзакцій.

Відновлюємо БД і «накочується» на неї копію журналу транзакцій до мітки. Це відповідає моменту, коли в таблиці MyTbl знаходилася тільки запис 'aaa'.

Те ж саме для ситуації відновлення по мітку, коли в таблиці було вже два записи.

Висновок

Природно, всю функціональність SQL Server 2000 неможливо охопити в рамках однієї статті. За межами даного огляду залишилися питання інтеграції зі службою каталогів, механізми безпеки, в тому числі делегування на основі Kerberos і шифрування трафіку по всіх мережевих бібліотекам, розвиток служб тиражування, включаючи оновлення даних на передплатника при недоступному видавця на основі черг повідомлень і реплікацію схеми даних, підтримка XML на рівні OLE DB-провайдера SQL Server, удосконалення в службі повнотекстового пошуку, службі перетворення даних, аналітичних службах і багато іншого, що заслуговує уважного вивчення. Я сподіваюся, що даний матеріал, незважаючи на його, за великим рахунком, досить вступний характер, допоможе читачам краще освоїтися в світі нових можливостей SQL Server 2000 і з максимальною віддачею використовувати їх для вирішення своїх конкретних завдань.

Про автора

Олексій Шуленін. Системний інженер відділу бізнес-додатків російського представництва Microsoft. Має сертифікати MCSE, MCDBA, MSS, MCSD. З ним можна зв'язатися за адресою: rusdev@microsoft.com.

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


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

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

Ваш отзыв

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

*

*