MariaDB на Google Summer of Code: Підсумки GSoC16

Дещо запізнілий звіт про MariaDB, наші проекти на останньому GSoC, наших студентів, королів і капусту.

Минулий 2015 — й- GSoC у нас вийшов дуже невдалий. Всього було вісім студентів, але багато провалилися ще в середині літа (на midterm evaluation), причому троє були з одного університету в Камеруні (і явно з одного курсу), з прекрасними заявками, але вони дружно не зробили нічого, від слова «зовсім», ну, може одну сходинку коментаря підправили за півтора місяці. А після провалу на midterm вони намагалися оскаржити наше рішення в Google, і навіть прислали нам лист з туманними погрозами. Мовляв, недобре стільки студентів провалювати, імідж собі псувати, в наступному році Google місць не дасть.

Але Google їх не послухався і дав. І цей рік, напевно за контрастом, вийшов на рідкість вдалий.

В цьому році нам виділили 10 слотів, на які претендували 54 студента. Враховуючи сумний досвід минулого року, ми стежили, щоб всі прийняті студенти були з різних вузів — якщо і будуть халтурити, то незалежно один від одного. І було як завжди — деякі студенти писали на наш список розсилки заздалегідь, з'являлися на IRC, обговорювали проекти. Два студента навіть зробили робочі прототипи. Такі активні, звичайно, всі були прийняті. Інші залишилися за бортом, конкурс був великий. З прийнятих десяти студентів вийшло, що:

  • Один проект був для MHA. MariaDB виступає як «umbrella organization» і проекти можуть бути будь-які MariaDB/MySQL/Percona екосистемі
  • Один проект був для MaxScale, тобто теж не для сервера. См. вище
  • Один студент нічого не робив, та його відсіяли в липні
  • З трьома сталося щось дивне — вони добре працювали, проблем не передбачалося. І раптом уже в кінці серпня Google написав, що в їх даних виявилися якісь нестиковки, їх запідозрили в обмані і викинули з програми. Вийшло дуже негарно, люди старалися, працювали все літо. Але ми на ситуацію вплинути ніяк не могли, хоча й намагалися.
  • П'ять студентів пройшли до кінця і закінчили свої проекти. 50% — це для нас великий успіх. Про цих п'ятьох — нижче і докладніше.
Данило Медведєв: NO PAD collations
Як написано в документації (взагалі-то мигцем і між рядків), при порівнянні рядків різної довжини MariaDB Server (і Percona Server і MySQL) доповнюють більш коротку рядок пробілами поки довжини не зрівняються (тобто, звичайно, нічого насправді не доповнюється, але результат виходить такий, як ніби доповнювалося). Виглядає це так:

MariaDB [test]> select "abcd" > "abc";
+----------------+
| "abcd" > "abc" |
+----------------+
| 1 |
+----------------+
1 row in set (0.00 sec)

MariaDB [test]> select "abc\t" > "abc";
+-----------------+
| "abc\t" > "abc" |
+-----------------+
| 0 |
+-----------------+
1 row in set (0.00 sec)

У другому випадку довга рядок лексикографічно менше, ніж коротка, тому що
'\t'
менше пробілу. Це все добре і по стандарту. Але не зовсім. У SQL у стандарті collation є така властивість, вона може бути PADSPACE або NOPAD. У першому випадку короткий рядок домагається пробілами при порівнянні, у другому, відповідно, цього не відбувається. Так що виходить, що досі всі порівняння проводилися як ніби вони PADSPACE. І тепер Данило Медведєв зробив нам NO PAD collations. Ефект не завжди помітний, але він є:

MariaDB [test]> set collation_connection=utf8_general_nopad_ci;
Query OK, 0 rows affected (0.00 sec)

MariaDB [test]> select "abc\t" > "abc";
+-----------------+
| "abc\t" > "abc" |
+-----------------+
| 1 |
+-----------------+
1 row in set (0.00 sec)

Цей проект був повністю закінчений і тепер він уже в MariaDB 10.2.2.

Галина Шалигіна: Condition pushdown into non-mergeable views
Галина — не новачок у нас. Вона добре знайома з однією з найбільш складних підсистем MariaDB — оптимізатором запитів і є автором реалізації оператора
WITH
, рекурсивних та нерекурсивных CTE.

Для цього GSoC Галина вибрала оптимізацію уявлень, для яких не можна зробити злиття з головним запитом. Краще всього, звичайно, виконувати запит з уявленнями методом злиття (view merge), при якому подання підставляється як макрос у головний запит. Наприклад, такий запит:

CREATE VIEW v1 AS SELECT a1+b1 AS c1, d1 FROM t1 WHERE e1>5;
SELECT x2 FROM v1, t2 WHERE c1=y2 AND d1=10;

Можна перетворити на:

SELECT x2 FROM t1, t2 WHERE a1+b1=y2 AND d1=10 AND e1>5 ;
І тут оптимізатор бачить всі використовувані таблиці, всі умови, і може вибрати найкращий план. Але не всі вистави можна оптимізувати таким способом, наприклад, злиття не можна робити якщо у поданні використовується
GROUP BY
. У таких випадках доводиться виконувати уявлення як окремий запит, зберігаючи результат у тимчасову таблицю, і потім використовувати її в головному запиті. У підсумку оптимізатор не бачить «всієї картини» і змушений розглядати обидва запиту окремо. Тобто, у попередньому прикладі він би не зміг використати умову
d1=10
при вибірці з таблиці
t1
.

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

SELECT a1+b1 AS c1, d1 FROM t1 WHERE e1>5 AND d1=10;

Що може прискорити виконання запиту на порядки.

Ця робота теж була повністю закінчена, і вона вже доступна в 10.2.3

Shubham Barai, Sachin Setiya: Arbitrary length UNIQUE constraints
Завдання полягало в реалізації обмежень унікальності довільної довжини. Зазвичай, коли хтось при створенні таблиці пише
UNIQUE(a,b,c)
, MariaDB (і Percona, і MySQL) створюють індекс по полях a,b,c. Звідси і виходить обмеження — унікальність комбінації полів можна гарантувати тільки тоді, коли сумарна довжина цих полів не перевищує максимальної допустимої довжини ключа індексу. Але це ж нелогічно! «Індекс» — це спосіб оптимізувати доступ до даних, що його взагалі немає в SQL-стандарті. А UNIQUE — це логічне обмеження даних, прямо з SQL-стандарту, і зовсім незрозуміло, чому він має страждати через якихось обмежень конкретної реалізації прискорення запитів. Наше рішення було створити ще одне поле, приховане, користувачам його показувати не треба, і писати туди хеш того, для чого робимо UNIQUE. А по цьому полю можна створити звичайний індекс. При запису в ньому треба шукати, чи є конфлікти, і якщо є — витягувати конфліктуючі запису і вже безпосередньо порівнювати значення, хеші можуть адже і випадково співпасти. Більш того, MyISAM це вже вміє робити, навіть робить — саме так працює
SELECT DISTINCT
. Але для користувальницьких таблиць це було недоступно. Що і хотілося виправити.

З цим завданням виникла складна ситуація. Ми її пропонували на GSoC вже кілька років, ніхто не хотів. А в цьому році на неї прийшло дві заявки, і обидві досить розумні. Потім обидва студенти написали працює (!) прототип — перший раз за вісім років ментором в GSoC. І потім знову ж обидва (!) заявили, що, мовляв, це занадто просто, а давай вони ще і в InnoDB це зроблять. Довелося брати обох. Але я одному з них злегка підправив завдання.

Shubham пірнув у хащі InnoDB, і став розбиратися з її індексами, логами, транзакціями, recovery та іншої магією. А Sachin прикинувся, що забув, що всередині MyISAM ця функціональність вже є, викинув свій прототип, і почав реалізовувати все на рівні сервера, движково-незалежним способом. У підсумку обидва закінчили завдання успішно, низькорівнева реалізація трохи швидше, а високорівнева працює з різними движками, дозволяє оптимізатору використовувати ці невидимі поля для оптимізації запитів, і приносить із собою інші цікаві плюшки. Ми поки ще до кінця не визначилися, яку беремо — все таке смачне… Так що 10.2 це вже скоріше всього не потрапить. Буде в 10.3, напевно.

Sachin Setiya: Hidden columns
А це і є та вищезгадана плюшка. Щоб зробити невидиме поле (а воно ще й віртуальне, на диск цей хеш можна не писати), ми придумали додати те, що в Oracle називається Invisible columns, а в DB2 — Implicitly Hidden. Якщо просто — то при створенні поля можна вказати, що воно невидиме. Після цього
SELECT *
та
INSERT INTO table VALUE (...)
його не помічати. Не показувати або, відповідно, не писати в нього зазначені значення. Але якщо згадати це поле імені — в будь-якій команді — то воно веде себе, як звичайне видиме поле. Задумувалося це для розширення схеми так, щоб не зламати працюють (можливо ще і з закритими вихідними кодами) додатка. Додаються приховані поля, старі запити їх не бачать, а нові — називають їх по імені, і все працює.

Тільки ми цю ідею дещо розширили, додавши різні «рівні невидимості». Перший рівень — вище, як Oracle та DB2. Ну, є ще нульовий — все видно, це нецікаво. Другий рівень — поле видно тільки в
SELECT
і тільки якщо згадати по імені. Тобто воно не видно в
INSERT
/
UPDATE
— його значення не можна змінити. І воно не видно в
CREATE
/
ALTER
— його не можна створити або видалити. Воно створюється автоматично. Приклад — ROWID та інші псевдо-поля. І третій рівень, полі не видно взагалі ніде. Це як раз і потрібно було, щоб створювати невидимі поля з хешем. А ще можна їх використовувати для функціональних індексів типу
INDEX(a+b)
— теж, створюємо невидиме віртуальне поле і його індексуємо. А ще можна… загалом, коли ця плюшка з'явилася, ідеї не змусили себе чекати.

Як частина попередньої задачі, це теж, швидше за все, потрапить тільки в 10.3

Varun Gupta: SQL aggregate functions
Теж ідея, яку обговорювали вже давно. Але толком вдалося сформулювати тільки до цього GSOC-у. У стандарті написано, як створювати користувальницькі функції SQL, і MariaDB це, звичайно, вміє:

CREATE FUNCTION COUNT_X(INT x) INT RETURNS
RETURN (SELECT COUNT(*) FROM data_table WHERE value=x);

Але це звичайні функції. А от свої агрегатні функції SQL створювати не можна. Принаймні в стандарті цього немає. В Oracle є, і в PostgreSQL є, і, наприклад, в HSQLDB теж. Мені хотілося, щоб і в MariaDB було. Довго думали над синтаксисом — стандарту ж немає, у всіх по-своєму. Порівнювали як у інших. У підсумку, вирішили не відрізнятися, і зробили як у всіх — тобто, по-своєму. Звичайно ж, наш варіант природніше і простіше всього. Напевно ті, хто придумували цей синтаксис в Oracle/PostgreSQL/HSQLDB теж вважають, що їхній варіант кращий.

Буде працювати так:

CREATE AGGREGATE FUNCTION agg_and(INT x) INT RETURNS
BEGIN
DECLARE z INT DEFAULT 65536;
DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN z;
LOOP
FETCH GROUP NEXT ROW;
SET z= z&x);
END LOOP;
END

Для порівняння, щоб зрозуміти звідки ноги ростуть, от абсолютно по стандарту написана не агрегатна функція, яка обчислює AND всіх значень якогось стовпця в таблиці:

CREATE FUNCTION col_and() ПОВЕРТАЄ INT
BEGIN
DECLARE INT x;
DECLARE z INT DEFAULT 65536;
DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN z;
DECLARE cur CURSOR FOR SELECT col FROM data_table;
OPEN cur;
LOOP
FETCH cur INTO x;
SET z= z&x);
END LOOP;
END

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

На жаль, це теж навряд чи потрапить до 10.2. Хоча проект теж закінчений практично повністю.

У цьому році не було, чомусь, ні одного проекту, закінченого, ну, скажімо, на три чверті. Так щоб і кинути шкода, і нести незручно. Все, що хотіли, було зроблено. І це здорово!

Чекаємо наступного року…
Джерело: Хабрахабр

0 коментарів

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