In-Memory OLTP в SQL Server 2014. Частина I

    Функціональність In-Memory OLTP (проект Hekaton) покликана прискорити обробку типових OLTP-операцій в SQL Server. Як відомо, навантаження, що припадає на сервер баз даних, будь то Microsoft SQL Server або Oracle / MySQL або SAP / Sybase або IBM DB2 і т.д., можна умовно розбити на два класи: збір даних і аналіз того, що зібрали, тому що навіщо в іншому випадку було збирати? Перший клас задач називається OLTP (On-Line Transactional Processing). Для нього характерні короткі обновляющие транзакції, що зачіпають відносно невелике число таблиць в базі. Приклади таких додатків — операційний день у банку, білінг в телекомі і т.д. Другий клас задач називається OLAP (On-Line Analytical Processing) і характеризується масивним тривалим читанням, що охоплює значну кількість таблиць і збирає з них, як правило, по максимуму записів, багатим предикатами зв'язування, сортування, групування, агрегатними функціями і т.д. Як правило, структури даних для різних класів задач розділяють, щоб не заважали один одному, і якщо перша структура бази будується з урахуванням численних правил Кодда, то друга, навпаки, денормалізована і виконується за схемою «зірка (сніжинка)».
Устремління СУБД в пам'ять проявилося на початку нового тисячоліття, коли з'ясувалося, що незважаючи на закон Мура тактова частота і швидкодія процесорів ростуть аж ніяк не по експоненті, а навпаки, виходять на плоску пряму насичення незважаючи на ILP і інші хитрощі. У той же час ціни на оперативну пам'ять, колись непристойно дорогу, катастрофічно знижуються і в порівнянні з 90-ми впали в тисячі разів. Ага, сказали собі виробники серверів баз даних. У 2005-му Oracle прикупив in-memory СУБД TimesTen, IBM в 2007-му — компанію Solid, а в Microsoft в даному випадку нічого з боку брати не стали, бо вирішили виховати у своєму колективі.
 
Почали з In-Memory OLAP. Перші плоди виховання втілилися в ColumnStore-движку VertiPaq з виходом SQL Server 2008 R2 у вигляді PowerPivot для Excel і SharePoint. Як випливає з назви, стовпчик індекс влаштований так, що кожна сторінка містить в собі шматок колонки у високо стислому вигляді (~ в 2 рази краще, ніж при з'явилася в SQL Server 2008 page-компресії), щоб утрамбувати більше даних у пам'ять. Традиційно від колоночного індексів виграє OLAP, тому що, як ми пам'ятаємо, це масивне читання. Як правило, потрібно прочитати вздовж по рядках (або за діапазоном) все колонки, за якими довідники (таблиці вимірювань, промені зірки) пов'язані з колонками зовнішніх ключів в таблиці фактів (ступиці), щоб побудувати між ними join (або semi-join). У SQL Server 2012 в цьому плані сталися дві корисні речі. По-перше, xVelocity (колишня VertiPaq) з'явилася в Analysis Services у вигляді так званих табличних моделей (Tabular Model), альтернативних традиційним багатовимірним, які існували ще, дай Б-г пам'яті, з SQL Server 7.0. По-друге, стовпчик індекс перестав бути річчю в собі, і його стало можливо побудувати в міру потреби явно — в T-SQL з'явилася команда CREATE [NONCLUSTERED] COLUMNSTORE INDEX. На колоночного індекси відразу була накладена хмара обмежень , найжорстокішим з яких було, звичайно, ось це — UPDATE statement failed because data cannot be updated in a table that has a nonclustered columnstore index. Consider disabling the columnstore index before issuing the UPDATE statement, and then rebuilding the columnstore index after UPDATE has completed. У SQL Server 2014 це зло побороли за допомогою оновлюваного кластерного колоночного індексу. Ну як колоночного… Щоб зробити стовпчик індекс обновлюваним, на нього навісили delta store і delete bitmap. Коли запис віддаляється, вона фізично не зникає, а на неї зводиться прапорець у delete bitmap. Коли відбувається вставка запису, вона потрапляє в delta store. І те, й інше — звичайні B-Tree (rowstore) з усіма витікаючими плюсами і мінусами. Є фоновий процес Tuple Mover, який повзає по delta store і конвертує додані записи в сегменти columnstore, але, взагалі кажучи, читання колоночного індексу означає читання не тільки columnstore, але ще й цих двох друзів-доважків, тому як потрібно відфільтрувати видалені записи і зробити union доданих. Проте, через два тижні після свого виходу SQL Sever 2014 продемонстрував рекордні результати в незалежних аналітичних тестах TPC-H , зайнявши перші рядки в турнірних таблицях по вагових категоріях 1, 3 і 10 ТБ обсягу БД в некластерние (standalone) заліку. Таким чином, будемо вважати, що з in-memory OLAP все обстоит чудово і перейдемо до in-memory OLTP.
 
Як вже говорилося, Hekaton — це не кодову назву чергової версії SQL Server, як були Деналі, Катмай, Юкон і т.д., а, власне, проект з розробки in-memory движка, тобто складової частини продукту. Цей компонент є найбільш яскравим нововведенням не тільки в поточній версії, але й, можливо, в масштабі всієї лінійки продуктів, починаючи з 16-бітного Ashton-Tate/Microsoft SQL Server 1.0, який побачив світ 25 років тому. Гекатон — слово грецьке і означає сто чи сам-сто = в сто раз, що кагбе натякає, що це не прідрацца крутіше порівняно з тим, у кого всього в десять. Хотілося б відразу застерегти від помилки, що Гекатон — якийсь розширений варіант dbcc pintable, тому що з прикріпленою до пам'яті таблицею робота відбувається, як з звичайної дискової, включаючи плани виконання, забезпечення транзакционной цілісності за допомогою блокувань і т.д. Гекатон — компактне самостійне ядро, інтегроване всередину виконавчого механізму SQL Server, що характеризується в порівнянні з традиційним database engine відсутністю інтепретіруемих планів виконання, блокувань як засобу забезпечення логічної цілісності даних і Латчі для фізичної цілісності. Нагадаю, що Латчі (не знаю, як вони ідеологічно вірно переводяться на російську, напевно, засувки) — це легковагі блокування, які накладаються на сторінки даних, індексні сторінки, якісь службові структури нпосредственно в момент їхнього читання або зміни в пам'яті на відміну від блокувань, які можуть діяти протягом всієї транзакції. Тому, не занурюючись в деталі, можна вважати, що дедлатчей не буває. Бувають нюанси, але не такі болючі. Інша відмінність полягає в тому, що блокуваннями можна управляти (при пом. Хінтів, рівня ізоляції). Латчі перебувають у суто веденні SQL Server. Я не буду детально вдаватися в їх внутрішній устрій, бажаючі можуть звернутися до BOL або на сторінку Євгенія Хабарова . Я взагалі не буду заглиблюватися в теорію, давайте краще перейдемо до прикладів.
 
Створимо БД і в ній файл-групу під in-memory OLTP.
 
 
CREATE DATABASE hekaton_test
ALTER DATABASE hekaton_test ADD FILEGROUP fg_hekaton_test CONTAINS MEMORY_OPTIMIZED_DATA 
ALTER DATABASE hekaton_test ADD FILE (name='fg_hekaton_test1', filename='c:\Temp\fg_hekaton_test1') TO FILEGROUP fg_hekaton_test     

Скрипт 1
 
Це файлстрімоподобная файл-група, в неї будуть Персистий дані з пам'яті, а в процесі recovery. відповідно, читатися взад. Подібно clustered columnstore вона складається з дата-файлів, в які послідовно пишуться вставлені в рез-ті insert або update запису і дельта-файлів, в яких зберігаються ідентифікатори віддалених записів. Спочатку зміни, як водиться, відображаються в пам'яті, а при checkpoint блоками по 256К (у разі data) і 4К (delta) скидається на диск, про що робиться відмітка в журналі транзакцій. Злиття пар data-delta відбувається автоматично при досягненні певного розміру і залежно від розміру оперативної пам'яті, а також може робитися вручну процедурою sys.sp_xtp_merge_checkpoint_files. Детальніше про цей процес можна прочитати тут .
Таким чином, розміщення таблиць в пам'яті не означає, що якщо сервер отрубился, все, що нажито непосильною працею, все буде втрачено. In-Memory OLTP — повністю транзакційна технологія і підтримує кошти відмовостійкості, включаючи AlwaysOn.
 
У свіжостворений БД створимо таблицю, оптимізовану для роботи в пам'яті.
 
 
use hekaton_test
CREATE TABLE dbo.ShoppingCart ( 
   ShoppingCartId int not null primary key nonclustered hash with (bucket_count=2000000), 
   UserId int not null index ix_UserId nonclustered hash with (bucket_count=1000000), 
   CreatedDate datetime2 not null, 
   TotalPrice money 
) 
WITH (MEMORY_OPTIMIZED=ON)  

Скрипт 2
 
Остання опція саме означає, що таблиця буде розміщена в пам'яті. Кожна MEMORY_OPTIMIZED таблиця повинна мати хоча б один індекс.Общее число не повинно перевищувати 8. Вхідні в індек поля не повинні бути nullable. Для вхідних в індекси полів n (var) char повинна використовуватися Коллацо BIN2. Кластерних індексів, за визначенням, немає. За своєю структурою класичних B-Tree теж. Для таблиць в пам'яті індекси бувають HASH (краще підходить для точкового пошуку) і RANGE (як випливає з назви, краще підходить для сканів за діапазонами).
Основним компонентом хеш-індексу служить так звана таблиця відповідностей (mapping table), в одній колонці якій зберігаються результати застосування хеш-функції до конкатенації полів, що утворюють індексний ключ, в іншій — покажчики на записи. Оскільки хеш-функція може давати однакові результати (виникають колізії) для абсолютно різних значень аргументу (для близьких вони якраз зобов'язані змінюватися), це насправді будуть покажчики на області пам'яті, де лежать ланцюжка переповнень. Кожна така ланцюжок представяляет собою двонаправлений список. Основним параметром при створенні хеш-індексу виступає bucket_count. Це число слотів в таблиці відповідностей. Чим менше їх буде, тим вища ймовірність колізії, тим довше ланцюжка переповнень будуть рости з кожного хеша. Відповідно, очевидно, що воно має бути не менше, ніж кількість унікальних значень в індексному ключі. Насправді, воно оцінюється як кількість унікальних значень, округлене вгору до наступного ступеня 2, і тут докладно пояснюється, чому.
Другий тип індексу, доступний in-memory, називається range (діапазонний) і дуже нагадує класичний кластерний. Його вузли утворюють впорядковану структуру, ефективну для сканів за діапазонами. Замість B-Tree використовується його модифікація Bw-Tree , найбільш яскравою відмінністю котороя, мабуть, є те, що вона не зберігає покажчики на повторювані значення. Якщо в таблиці є мільйон однакових значень, класичне дерево буде тупо тримати в листках мільйон покажчиків (на дані). Bw обходиться в цьому випадку одним, що дозволяє драматично (англомовні автори дуже люблять це слово) заощадити місце при засуванням цього господарства в пам'ять. Єдино, в цьому випадку знову виникають ланцюжки переповнень — ми ж не зберігаємо покажчики на всі записи, як дістатися до наступної з таким же значенням ключа? Виникає відчуття, що ніякої економії немає, просто байти на ptr переклали з листя в букети. Але ні, читайте, чому це не так, тут , а ми рушимо далі і створимо ще одну таблицю.
 
 
CREATE TABLE dbo.UserSession ( 
   SessionId int not null primary key nonclustered hash with (bucket_count=400000), 
   UserId int not null, 
   CreatedDate datetime2 not null, 
   ShoppingCartId int, 
   index ix_UserId nonclustered hash (UserId) with (bucket_count=400000) 
) 
WITH (MEMORY_OPTIMIZED=ON, <b>DURABILITY=SCHEMA_ONLY</b>)      

Скрипт 3
 
Зверніть увагу на останню опцію, виділену жирним кольором. Вона означає, що дані цієї таблиці не буде збережено між рестарт сервера (сама структура залишиться). Вище я говорив, що з приводу Гекатон існує оману, ніби все, що знаходиться в пам'яті, при рестарт втрачається. Так от для даних таблиць це дійсно так, але ви створюєте їх абсолютно усвідомлено, щоб зменшити накладні витрати, зокрема, на журналирование там, де це не потрібно. Це свого роду аналог тимчасових таблиць. До слова, табличні змінні в Гекатон також підтримуються. Вони оголошуються через попереднє створення табличного типу CREATE TYPE… AS TABLE… На відміну від звичайних табличних змінних вони зберігаються, зрозуміло, не в дискової tempdb, а відносяться до тієї бази, де були оголошені.
 
Розміщуються в пам'яті таблиці не підтримують автоматичне оновлення статистики, зокрема, ALTER DATABASE… SET AUTO_UPDATE_STATISTICS ON. Також не працює
exec sp_autostats @ tblname = 'ShoppingCart'
Index Name AUTOSTATS Last Updated
[Ix_UserId] OFF NULL
[PK__Shopping__7A789AE57302F83B] OFF NULL
exec sp_autostats @ tblname = 'ShoppingCart', @ flagc = 'ON'
 - Operations that require a change to the schema version, for example renaming, are not supported with memory optimized tables.
Однак статистику можна оновлювати вручну: UPDATE STATISTICS dbo.ShoppingCart WITH FULLSCAN, NORECOMPUTE.
Взагалі таблиці в пам'яті мають хмару обмежень. Чи не підтримується більшість табличних хінтів: Ні TABLOCK, XLOCK, PAGLOCK,… На NOLOCK не лається, але й не реагує, як ніби його й немає. Динамічні і keyset-курсори мовчазно переводяться в static. Не підтримуються оператори TRUNCATE TABLE і MERGE (коли таблиця в пам'яті виступає призначенням). Існують обмеження на типи використовуваних полів. Докладно прочитати про них можна тут , ми ж, щоб не реєструватися, подивимося, що у нас вийшло.
 
 
SELECT name, description FROM sys.dm_os_loaded_modules WHERE description = 'XTP Native DLL' 

Скрипт 4
 
У каталозі установки SQL Server C: \ Program Files \ Microsoft SQL Server \… \ DATA \ xtp \ 11 \ з'явилося дві dll, які називаються xtp_t_11_ <9 цифр>. Dll. Це наші таблиці ShoppingCart і UserSession.
Вбудований компілятор перетворює T-SQL-визначення таблиць і збережених процедур в Cшний код (можна подивитися в тому ж каталозі), з якого виходить машинний. Відповідні динамічні бібліотеки завантажуються в пам'ять і лінкуются всередину SQL Serverного процесу. При рестарт SQL Server бібліотеки компілюються і завантажуються заново на основі каталожної інформації з метаданих.
 
У наступній частині планується розглянути нативну компіляцію збережених процедур, рівні ізоляції транзакцій в Гекатон, блокування, журналирование і загальну продуктивність у порівнянні з традиційними дисковими об'єктами.
    
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.