12k
All articles

5 возможностей MariaDB, о которых стоит знать

Важные возможности MariaDB: векторный поиск, системно-версионные и application-time таблицы, выбор движка для каждой таблицы и utf8mb4 по умолчанию в 11.8 LTS.

OpenReplay Team
OpenReplay Team
5 возможностей MariaDB, о которых стоит знать

Пять наиболее значимых возможностей 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.0Unicode без изменения конфигурации сервераПо умолчанию с 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, если только вы не вернёте серверу прежние умолчания.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.