5 возможностей MariaDB, о которых стоит знать
Важные возможности MariaDB: векторный поиск, системно-версионные и application-time таблицы, выбор движка для каждой таблицы и utf8mb4 по умолчанию в 11.8 LTS.
Пять наиболее значимых возможностей MariaDB для команды, работающей с MySQL 8.0, — это встроенный векторный поиск, таблицы с системным версионированием (system-versioned tables), периоды прикладного времени (application-time periods), выбор движка хранения для каждой таблицы и utf8mb4 в качестве серверной кодировки по умолчанию вместе с коллациями UCA 14.0.0, появившимися в MariaDB 11.6 и перенесёнными в линейку 11.8 LTS.
Если вы взвешиваете решение о выборе версии для инфраструктуры на MySQL 8.0, MariaDB — один из вариантов, который стоит внести в список.
Далее мы разберём каждую возможность по очереди: что она делает и какой SQL для неё нужно написать. Ссылки на версии указывают на MariaDB 11.8 LTS и MariaDB 12.3 LTS — релиз, который установит большинство команд, начинающих работу сейчас.
Ключевые выводы
- MariaDB поставляет векторный поиск по сходству прямо в составе community-сервера: колонка
VECTOR(N)иVECTOR INDEX, построенный на модифицированном алгоритме HNSW, без установки каких-либо расширений. - MariaDB использует векторный индекс только тогда, когда в
ORDER BYстоит чистый вызов функции расстояния, соответствующей метрике, с которой был построен индекс, а за ним следуетLIMIT; несовпадающая функция расстояния приводит к откату на полное сканирование таблицы. WITH SYSTEM VERSIONINGвместе сSELECT ... FOR SYSTEM_TIME AS OFдаёт запросы на момент времени без триггеров аудита и отдельной теневой таблицы истории.PERIOD FORобъявляет бизнес-валидность для таблицы, а объявление одновременно системного версионирования и периода прикладного времени делает таблицу битемпоральной.- Кодировка по умолчанию — utf8mb4, начиная с MariaDB 11.6, с коллациями на базе UCA 14.0.0, а 11.8 — первый релиз с долгосрочной поддержкой, в котором это закреплено.
Как MariaDB выполняет векторный поиск без расширений?
MariaDB хранит и ищет эмбеддинги внутри самого community-сервера: колонка VECTOR(N), VECTOR INDEX на модифицированном алгоритме HNSW и функции расстояния для косинусного и евклидова сходства. Страница проекта MariaDB Vector относит статус general availability к 11.8 LTS, а справочник по типу VECTOR ограничивает колонку 16 383 измерениями. Ничего дополнительно устанавливать не нужно, и рядом с вашими строками не появляется второе хранилище, которое рано или поздно рассинхронизируется с ними.
CREATE TABLE support_articles (
id BIGINT UNSIGNED PRIMARY KEY,
tenant_id INT NOT NULL,
body TEXT,
embedding VECTOR(768) NOT NULL,
VECTOR INDEX (embedding) M=8 DISTANCE=cosine
) ENGINE=InnoDB;
768 соответствует размерности выхода нескольких широко используемых открытых моделей эмбеддингов предложений; задайте то значение, которое выдаёт ваша модель. В этом DDL стоит обратить внимание на два ограничения. Индексируемая колонка должна быть NOT NULL, а M принимает значения от 3 до 200, где более высокие значения повышают точность ценой размера индекса и скорости записи — см. справочник create-table-with-vectors. На странице проекта также указано ограничение: один векторный индекс на таблицу.
Сервер хранит векторы и ищет по ним, но не создаёт их. Поэтому массив приходит из вашей модели эмбеддингов и вставляется как текст:
INSERT INTO support_articles (id, tenant_id, body, embedding)
VALUES (1, 17, 'Resetting a device token',
VEC_FromText('[0.021, -0.113, 0.447, ...]'));
Поскольку эмбеддинги живут в обычной таблице InnoDB, условие WHERE и ранжирование по сходству выполняются как один транзакционный оператор. Векторный сервис, приделанный сбоку от базы данных, на такое не способен: ограничение поиска ближайших соседей строками одного тенанта не требует ни соединения между двумя системами, ни постфильтрации результатов, за извлечение которых вы уже заплатили.
SELECT id, body
FROM support_articles
WHERE tenant_id = 17
ORDER BY VEC_DISTANCE_COSINE(embedding, VEC_FromText('[...]'))
LIMIT 10;
Почему векторный индекс не используется?
Индекс вступает в игру только тогда, когда запрос сортирует по простому вызову функции расстояния, используя метрику, под которую индекс был построен, и ограничивает количество строк через LIMIT. Обратитесь к индексу, построенному под косинусную метрику, с евклидовой функцией — и справочная документация прямо указывает, что индекс не сможет обслужить такой запрос и он деградирует до полного сканирования таблицы:
-- index above was built with DISTANCE=cosine
SELECT id, body
FROM support_articles
WHERE tenant_id = 17
ORDER BY VEC_DISTANCE_EUCLIDEAN(embedding, VEC_FromText('[...]'))
LIMIT 10;
Универсальная функция VEC_DISTANCE позволяет избежать этой ловушки, разрешаясь в евклидову или косинусную метрику в соответствии с нижележащим индексом, как описано в обзоре векторов; эти две метрики — единственные поддерживаемые. Если для вас важна полнота (recall) при малых значениях LIMIT, переменная mhnsw_ef_search задаёт, сколько кандидатов-результатов поиск по индексу держит в работе: 20 по умолчанию, диапазон от 1 до 10000. Поиск никогда не рассматривает меньше этого числа, даже если LIMIT запрашивает меньше, так что повышение значения покупает качество ценой времени поиска.
Для сравнения: в MySQL 8.0 типа VECTOR нет; векторный тип появился в серии 9.x, а поиск по векторам с поддержкой индекса документирован как возможность HeatWave, где HeatWave GenAI строит индексы самостоятельно для векторных колонок, к которым часто идут запросы. В PostgreSQL ту же задачу решает pgvector — расширение, которое нужно установить и включить.
Таблицы с системным версионированием: как запросить состояние на прошлый вторник?
Таблица с системным версионированием хранит каждую вытесненную версию каждой строки внутри самой таблицы, поэтому чтение на момент времени — это просто предложение в обычном SELECT, а не триггер аудита, пишущий в таблицу истории, которую вам приходится поддерживать. Поскольку старые версии остаются рядом с актуальными, вы можете прочитать таблицу в том виде, в каком она была в любой момент прошлого, проследить, что изменилось, и поставить две даты рядом.
CREATE TABLE subscription (
account_id BIGINT PRIMARY KEY,
plan VARCHAR(32),
seats INT
) WITH SYSTEM VERSIONING;
SELECT account_id, plan, seats
FROM subscription
FOR SYSTEM_TIME AS OF TIMESTAMP '2025-11-04 09:00:00'
WHERE account_id = 4021;
Вот и вся функциональность. История возвращается, когда указано FOR SYSTEM_TIME, а справочник по таблицам с системным версионированием документирует формы запроса AS OF, BETWEEN, FROM ... TO и ALL. Явная форма DDL объявляет колонки GENERATED ALWAYS AS ROW START и ROW END с PERIOD FOR SYSTEM_TIME; краткая форма выше хранит ту же информацию за псевдоколонками ROW_START и ROW_END. В InnoDB можно также версионировать по транзакциям, используя колонки row-start и row-end типа BIGINT UNSIGNED и читая через FOR SYSTEM_TIME AS OF TRANSACTION. Собственная документация MariaDB о различиях перечисляет темпоральные таблицы среди возможностей, аналогов которым в MySQL нет.
Периоды прикладного времени для бизнес-валидности
Системное версионирование фиксирует, когда базе данных сообщили нечто; период прикладного времени фиксирует, когда факт был истинным на самом деле. Период прикладного времени — это промежуток, размеченный двумя темпоральными колонками совпадающего типа и ширины, и он версионирует ваши данные на уровне приложения, а не на уровне сервера. Классический случай — цена с окном действия.
CREATE TABLE price_list (
sku VARCHAR(32),
price DECIMAL(10,2),
valid_from DATE,
valid_to DATE,
PERIOD FOR validity(valid_from, valid_to),
UNIQUE (sku, validity WITHOUT OVERLAPS)
);
UPDATE price_list FOR PORTION OF validity
FROM '2026-01-01' TO '2026-04-01'
SET price = 18.00
WHERE sku = 'KB-114';
Объявление периода делает обе колонки NOT NULL и незаметно добавляет проверку, что первое значение предшествует второму. WITHOUT OVERLAPS в первичном или уникальном ключе затем отклоняет строки, периоды которых пересекаются. Это предложение документировано начиная с MariaDB 10.5.3; сами периоды, включая FOR PORTION, появились раньше — в MariaDB 10.4.3. Объявите для одной таблицы и период, и системное версионирование — и вы получите битемпоральную таблицу, в которой оба вида версионирования работают одновременно на одних и тех же строках. Именно это отличает цену, которая была ошибочной во вторник, от цены, которую всего лишь внесли с опозданием.
Можно ли выбирать движок хранения для каждой таблицы?
MariaDB задаёт движок хранения на уровне таблицы, поэтому транзакционные таблицы остаются на InnoDB, а другие таблицы в той же схеме используют что-то ещё. Помимо стандартного набора, документация о различиях называет ColumnStore для распределённой аналитической обработки, MyRocks, движок S3 для облачного архивирования, Aria как замену MyISAM, а также CONNECT, SEQUENCE, Spider, SphinxSE, FederatedX и OQGRAPH.
CREATE TABLE event_archive (
id BIGINT PRIMARY KEY,
body TEXT
) ENGINE=Aria;
Некоторые из них поставляются отдельными пакетами плагинов, а не компилируются в сервер, поэтому проверьте документацию самого движка для устанавливаемого вами релиза, прежде чем считать, что голого предложения ENGINE= достаточно.
utf8mb4 по умолчанию и более новые коллации
MariaDB сменила серверную кодировку по умолчанию с latin1 на utf8mb4 в версии 11.6 и попутно обновила коллации до UCA 14.0.0. Если вы обновляетесь между релизами с долгосрочной поддержкой, оба изменения приходят в 11.8, и анонс релиза 11.8 LTS от 8 июня 2025 года представляет их как часть этого выпуска. Это MariaDB отказывается от собственного умолчания, которое старше эмодзи, а не момент, когда она обгоняет MySQL по самому умолчанию. Часть, касающаяся коллаций, переживает обновление: порядок сортировки и сравнение следуют более новому алгоритму юникодной коллации, чем в MySQL, поэтому при сравнении вывода двух систем стоит перепроверять результаты сортировки.
Возможности MariaDB и релизы, в которых они появились
| Возможность | Что можно делать | Доступность в MariaDB |
|---|---|---|
| Векторный поиск | Хранить эмбеддинги в VECTOR(N), индексировать через VECTOR INDEX, фильтровать и ранжировать одним оператором | Тип VECTOR добавлен в 11.7.1, GA в 11.8 LTS, присутствует в 12.3 LTS |
| Таблицы с системным версионированием | Читать таблицу в том виде, в каком она была на прошлую метку времени или транзакцию | Доступно в релизах 11.8 и 12.3 LTS |
| Периоды прикладного времени | Моделировать окна бизнес-валидности и обновлять отдельный их срез | Периоды и FOR PORTION с 10.4.3, WITHOUT OVERLAPS с 10.5.3, присутствуют в текущих LTS |
| Битемпоральные таблицы | Версионировать одну таблицу и по системному, и по бизнес-времени | Присутствует в текущих LTS |
| utf8mb4 по умолчанию, UCA 14.0.0 | Unicode без изменения конфигурации сервера | По умолчанию с 11.6, первый LTS с этим — 11.8 |
Одно замечание по обновлению, которое стоит держать в голове: в анонсе релиза 11.8 отмечено, что расширение диапазона TIMESTAMP не потребовало конвертации данных при условии, что таблицы с системным версионированием не используются, и именно они названы единственным известным осложнением, поскольку внутреннее представление меток времени изменилось.
Выберите из этих пяти ту возможность, которая ложится на уже имеющуюся у вас проблему. Если это история аудита — создайте одноразовую таблицу WITH SYSTEM VERSIONING, измените строку дважды и прочитайте её обратно через FOR SYSTEM_TIME AS OF; за считаные минуты вы поймёте, заменяет ли это триггер, который вы до сих пор поддерживаете.
Частые вопросы
Можно ли включить системное версионирование для уже существующей таблицы?
Да. ALTER TABLE t ADD SYSTEM VERSIONING включает его для существующей таблицы, а ALTER TABLE t DROP SYSTEM VERSIONING отключает, удаляя всю историю. Обе операции перестраивают таблицу, поэтому на больших таблицах могут выполняться долго. Последующие изменения схемы зависят от настройки system_versioning_alter_history: при значении ERROR изменение версионированной таблицы завершается ошибкой; при KEEP изменение проходит, но исторические запросы показывают новую структуру таблицы.
Как не дать таблице с системным версионированием расти бесконечно?
Существуют три документированных варианта: обрезать историю оператором DELETE HISTORY, для которого требуется привилегия DELETE HISTORY; партиционировать по SYSTEM_TIME и удалять исторические партиции, учитывая, что нельзя удалить текущую партицию или единственную историческую; либо отключить и заново включить системное версионирование, что очищает историю ценой перестроения таблицы. TRUNCATE TABLE задачу не решит: сервер отклоняет его с ошибкой 4137, чтобы история сохранилась.
Можно ли проиндексировать одну векторную колонку и под косинусное, и под евклидово расстояние в MariaDB?
Нет. Векторный индекс строится под одну функцию расстояния, допустимые значения — cosine и euclidean (по умолчанию), и поиск с другой функцией расстояния не сможет использовать такой индекс. Кроме того, MariaDB допускает лишь один векторный индекс на таблицу, поэтому обслуживание второй метрики означает удаление индекса и его перестроение с другим значением DISTANCE, а не добавление ещё одного рядом.
Влияет ли utf8mb4 по умолчанию в MariaDB 11.8 на репликацию на более старые серверы?
Да. Кодировка по умолчанию сменилась с latin1 на utf8mb4, а коллация по умолчанию — на utf8mb4_uca1400_ai_ci в MariaDB 11.6, и 11.8 — первый релиз с долгосрочной поддержкой, несущий это изменение. В более старых релизах такой коллации нет, поэтому первичный сервер MariaDB 11.8 не сможет реплицировать на реплику MariaDB 10.6, если только вы не вернёте серверу прежние умолчания.