Анатомия облачной системы управления векторными базами данных
Для меня большая честь, что наша последняя статья, "Manu: A Cloud Native Vector Database Management System", была принята на VLDB'22, ведущую международную конференцию в области исследований баз данных. В этой статье я расскажу о ключевой философии и принципах проектирования, лежащих в основе Manu (проектное название Milvus 2.0), cloud native базы данных, специально созданной для управления векторными данными. За дополнительной информацией вы можете обратиться к нашим предыдущим статьям и нашему GitHub repository.
Предыстория
Когда мы проектировали Milvus 1.0, нашей главной целью было просто поддержать управление векторами и оптимизировать производительность векторного поиска. Но по мере того как на протяжении лет мы взаимодействовали со всё большим числом пользователей, мы обнаружили некоторые общие бизнес-требования к векторным базам данных, которые было трудно удовлетворить в рамках изначальной архитектуры.
Эти потребности можно сгруппировать в следующие категории: постоянно меняющиеся требования, более гибкая политика согласованности, эластичность на уровне компонентов и более простая, более эффективная модель обработки транзакций.
Постоянно меняющиеся требования
Бизнес-требования всё ещё не полностью определены, когда речь идет об обработке векторных данных.
В самые ранние дни наиболее востребованным был поиск K-ближайших соседей. Но затем стало появляться всё больше требований, включая поиск по диапазону, поддержку различных пользовательских метрик расстояния, гибридный поиск, мультимодальные запросы и другую всё более разнообразную семантику запросов.
Это требует, чтобы архитектура векторной базы данных была достаточно гибкой для быстрой и оперативной поддержки новых требований.
Более гибкая политика согласованности
Возьмем в качестве примера рекомендации контента: в этом сценарии предъявляются высокие требования к своевременности. Новый контент нужно рекомендовать пользователям в течение нескольких минут или даже секунд, поэтому система не может тратить день или больше на обновление своих рекомендаций. В таких сценариях трудно гарантировать бизнес-результаты, обеспечивая только eventual consistency, тогда как при настойчивом требовании strong consistency возникнут большие системные накладные расходы.
Для решения этой проблемы мы предлагаем следующее решение: в соответствии с бизнес-требованиями пользователи могут указать максимальную задержку, которую можно допустить до того, как вставленные данные станут доступны для запросов. Система, в свою очередь, настраивает определенные механизмы обработки данных, чтобы гарантировать конечный результат для бизнеса.
Эластичность на уровне компонентов
Требования к ресурсам и интенсивность нагрузки сильно различаются для каждого компонента векторной базы данных в разных приложениях. Например, компонентам векторного поиска и запросов требуются большие вычислительные ресурсы и ресурсы памяти для обеспечения их производительности, тогда как архивированию данных и управлению метаданными для работы требуется лишь немного ресурсов.
С точки зрения приложений, наиболее критичным требованием для рекомендательных систем является способность выполнять крупномасштабные параллельные запросы, и поэтому в этих системах более высокая нагрузка ложится только на компонент запросов. Аналитические приложения, с другой стороны, часто нуждаются в импорте большого объема офлайн-данных, поэтому нагрузка приходится на вставку данных и построение индекса — два взаимосвязанных компонента.
Чтобы повысить эффективность использования ресурсов, необходимо, чтобы каждый функциональный модуль обладал независимой и эластичной масштабируемостью, чтобы распределение ресурсов системы могло точнее соответствовать фактическим потребностям приложения.
Более простая, более эффективная модель обработки транзакций
Строго говоря, модель транзакций — это пространство для оптимизации, которое можно использовать в проектировании системы, а не бизнес-требование.
По мере того как машинное обучение развивается в своей описательной мощности, компании стремятся объединять данные из нескольких измерений одной сущности, чтобы представить ее в виде единого унифицированного вектора. Например, в профилировании пользователей объединяются такая информация, как личные профили, предпочтения и социальные связи. В результате векторные базы данных можно поддерживать всего с одной таблицей, без необходимости реализовывать JOIN-подобные операции, распространенные в традиционных базах данных. Таким образом, системе необходимо поддерживать только ACID на уровне строк в одной таблице, и она может обходиться без сложных транзакций, затрагивающих несколько таблиц, что оставляет большое пространство для развязки компонентов и оптимизации производительности в системе.
Цели
Как второй крупный релиз Milvus, Manu позиционируется как cloud-native распределенная система векторных баз данных.
Когда мы приступили к проектированию Manu, мы учли различные бизнес-требования, упомянутые выше, и объединили их с общими требованиями к распределенной системе. В результате были сформулированы пять широких целей для Manu: долгосрочная эволюционируемость, настраиваемая согласованность, хорошая эластичность, высокая доступность и высокая производительность.
Долгосрочная эволюционируемость
Чтобы удерживать сложность системы на управляемом уровне по мере развития функциональности, нам необходимо хорошо развязать систему, чтобы отдельные компоненты могли эволюционировать, добавляться или заменяться независимо, с минимальным влиянием на другие компоненты.
Настраиваемая согласованность
Системе необходимо поддерживать delta consistency, чтобы пользователи могли задавать задержку видимости запросов для вновь вставленных данных. Delta consistency требует, чтобы все запросы могли возвращать все релевантные данные как минимум до единицы времени delta, которая может быть задана пользовательским приложением на основе бизнес-требований.
Хорошая эластичность
Чтобы повысить эффективность использования ресурсов, системе необходимо обеспечить мелкозернистую эластичность на уровне компонентов, а также политику распределения ресурсов, которая учитывает различные аппаратные зависимости компонентов.
Высокая доступность и производительность
Высокая доступность — базовое требование ко всем облачным базам данных, которое подразумевает, что в случае отказа нескольких сервисных узлов или компонентов другие сервисы не затрагиваются, а система способна к эффективному восстановлению после сбоев.
Высокая производительность — клише для векторных баз данных. В процессе проектирования нам необходимо строго контролировать накладные расходы, возникающие на уровне системного фреймворка, чтобы обеспечить хорошую производительность.
Архитектура Manu
Manu использует четырехуровневую архитектуру, которая обеспечивает развязку чтения от записи, stateless от stateful, а также хранения от вычислений.
Как показано на рисунке ниже, сверху вниз Manu имеет четыре уровня, т. е. уровень доступа, уровень координаторов, уровень рабочих узлов и уровень хранения. Manu также использует систему логов в качестве своего магистрального компонента, который соединяет развязанные компоненты.
Архитектура Manu.
Уровень доступа
Уровень доступа состоит из stateless-прокси, которые служат пользовательскими конечными точками.
Эти прокси получают запросы от клиентов, распределяют запросы соответствующим компонентам и собирают результаты перед возвратом их клиентам. Кроме того, прокси кэшируют копию метаданных для проверки допустимости поисковых запросов (например, существует ли коллекция для поиска).
Уровень координаторов
Уровень координаторов управляет состоянием системы, поддерживает метаданные и координирует компоненты системы для обработки задач.
Существует четыре типа координаторов, каждый из которых спроектирован независимо для различной функциональности. Таким образом, сбои системы могут быть изолированы, а компоненты могут развиваться отдельно. В целях надежности каждый координатор может иметь несколько экземпляров (например, один основной и две резервные копии).
Корневой координатор
Корневой координатор обрабатывает запросы определения данных, такие как создание/удаление коллекций, и поддерживает метаинформацию коллекций (например, свойства коллекций, тип данных каждого свойства).
Координатор данных
Координатор данных отвечает за персистентность данных. Он координирует узлы данных для преобразования запросов на обновление данных в binlog и записывает подробную информацию о коллекциях (например, список сегментов каждой коллекции, путь хранения каждого сегмента).
Координатор индексов
Координатор индексов управляет индексированием данных. Он координирует узлы индексов для задач индексирования и записывает индексную информацию каждой коллекции (например, тип индекса, связанные параметры, путь хранения и т. д.).
Координатор запросов
Координатор запросов отслеживает состояние узлов запросов и корректирует назначение сегментов (вместе со связанными индексами) узлам запросов для балансировки нагрузки.
Рабочий уровень
Рабочий уровень выполняет множество задач в системе.
Все рабочие узлы не имеют состояния — они получают доступные только для чтения копии данных для выполнения задач и не нуждаются в координации друг с другом. Поэтому количество рабочих узлов можно гибко регулировать в соответствии с нагрузкой. Кроме того, Manu использует разные рабочие узлы для разных задач, так что каждый тип узлов может масштабироваться независимо в соответствии с фактической нагрузкой и требованиями QoS.
Уровень хранения
Уровень хранения персистентно хранит информацию о состоянии системы, метаданные, коллекции и связанные индексы.
Manu использует высокодоступные распределенные KV-хранилища (key-value), такие как etcd, для хранения информации о состоянии системы и метаданных. Когда метаданные обновляются, данные сначала записываются в KV-хранилище, а затем синхронизируются с соответствующими координаторами. Данные большого объема, такие как данные коллекций и индексов, управляются с помощью сервисов объектного хранения, таких как AWS S3. Высокая задержка, присущая объектному хранению, не является узким местом производительности, поскольку рабочие узлы берут из объектного хранилища копии данных только для чтения и кэшируют их локально перед обработкой данных, поэтому большая часть обработки данных выполняется локально.
Журнальная магистраль
Журнальная магистраль.
Чтобы лучше разделить системные компоненты (например, WAL, binlog, узлы данных, узлы индексов и узлы запросов), так чтобы каждый из них мог масштабироваться и развиваться независимо, Manu следует парадигме "log as data" и использует систему журналов в качестве своей магистрали, которая соединяет разделенные системные компоненты. В Manu на журналы могут персистентно подписываться различные системные компоненты, поэтому они называются подписчиками журналов.
Журналы в Manu можно разделить на write-ahead log (WAL) и binlog. WAL — это инкрементальная часть системного журнала, тогда как binlog — базовая часть. Они дополняют друг друга по задержке, емкости и стоимости.
Как показано на рисунке выше, логгеры являются точками входа системы журналов, публикуя данные в WAL. Узлы данных подписываются на WAL и преобразуют строковые WAL в колоночные binlog. Все компоненты только для чтения, такие как узлы индексов и узлы запросов, являются независимыми подписчиками сервиса журналов, чтобы поддерживать себя в актуальном состоянии.
Система журналов также служит для передачи межкомпонентных сообщений. Другими словами, компоненты могут транслировать системные события через журналы. Например, узлы данных могут сообщать другим компонентам, какие сегменты были записаны в объектное хранилище, а узлы индексов могут информировать всех координаторов запросов сразу после построения новых индексов. Более того, разные типы сообщений организованы в разных каналах. Каждому компоненту нужно подписываться только на соответствующий ему канал вместо прослушивания всех транслируемых журналов.
Рабочий процесс обработки данных
В этом разделе подробно рассматривается рабочий процесс обработки данных внутри Manu и описывается процесс вставки данных, построения индексов и выполнения запросов.
Вставка данных
Рабочий процесс вставки данных.
На рисунке выше показан рабочий процесс вставки данных в Manu и соответствующие задействованные компоненты.
После обработки прокси-сервером запросы на вставку данных распределяются по нескольким корзинам на основе алгоритмов хеширования. Как правило, в системе Manu есть несколько логгеров, обрабатывающих сущности в каждой хеш-корзине на основе консистентного хеширования. Сущности в каждой хеш-корзине записываются в канал журнала упреждающей записи (WAL), который сопоставлен только с этой корзиной. Когда логгер получает запрос на вставку данных, он присваивает этому запросу глобально уникальный порядковый номер журнала (LSN) и записывает его в соответствующий канал WAL. LSN генерируется центральным оракулом службы времени (TSO). Каждый логгер должен получать LSN от TSO и сохранять LSN локально через регулярные интервалы.
Чтобы обеспечить низкую задержку и высокую детализацию log pub/sub, сущности в Manu хранятся в WAL построчно, и каждый компонент, подписывающийся на WAL, считывает из него данные в потоковом режиме. В большинстве случаев WAL может быть реализован с помощью облачной очереди сообщений, такой как Kafka или Pulsar. Узлы данных подписываются на WAL и преобразуют построчные WAL в столбцовые binlogs. Столбцовая природа binlog упрощает сжатие и доступ к данным. Пример такой эффективности связан с индексными узлами. Индексные узлы считывают из binlog только требуемый векторный столбец для построения индекса и поэтому не подвержены усилению чтения.
Построение индекса
В Manu существует два сценария построения индекса — пакетное индексирование и потоковое индексирование. Пакетное индексирование происходит, когда пользователь строит индекс для всей коллекции (например, когда все векторы обновляются новой моделью эмбеддингов). В этом случае координатор индекса получает пути ко всем сегментам в коллекции от координатора данных и поручает индексным узлам построить индекс для каждого сегмента. Потоковое индексирование происходит, когда пользователи непрерывно вставляют новые сущности, а индексы строятся асинхронно на лету, не прерывая поисковые сервисы.
Когда узел данных записывает новый сегмент в binlog, координатор данных уведомляет координатор индекса о необходимости создать задачу для индексного узла по построению индекса на новом сегменте. Как в сценариях пакетного, так и потокового индексирования, после того как требуемый индекс построен для сегмента, индексный узел сохраняет его в объектном хранилище и отправляет путь хранения координатору индекса, уведомляя координатор запросов, чтобы узлы запросов могли загрузить индекс для обработки запросов.
Выполнение запросов
Manu разбивает коллекцию на сегменты и распределяет сегменты между узлами запросов для параллельного выполнения запросов. Прокси кэшируют копию распределения сегментов по узлам запросов, обращаясь к координатору запросов, и направляют поисковые запросы узлам запросов, которые содержат сегменты искомой коллекции. Узлы запросов выполняют векторные запросы на своих локальных сегментах, объединяют результаты и возвращают их прокси. Прокси дополнительно агрегирует результаты от каждого узла запросов и возвращает окончательные результаты клиенту.
Узлы запросов получают данные из трех источников — WAL, файлов индекса и binlog. Для исторических данных узлы запросов считывают соответствующие binlogs или файлы индекса из объектного хранилища. А для инкрементальных данных узлы запросов напрямую считывают из WAL в потоковом режиме. Получение инкрементальных данных из binlog приведет к задержке видимости данных, что особенно верно для больших поисковых запросов. Другими словами, вновь вставленные данные станут доступны для запросов только через длительный период времени, что не соответствует потребности в высокой согласованности в некоторых сценариях.
Как упоминалось ранее, Manu использует модель дельта-согласованности, чтобы дать пользователям возможность более гибко настраивать уровни согласованности. Дельта-согласованность гарантирует, что обновленные данные (включая вставленные и удаленные данные) могут быть запрошены и найдены в пределах delta единиц времени после того, как запрос на обновление данных получен Manu.
Manu достигает дельта-согласованности, добавляя LSN с временными метками ко всем запросам на вставку данных и запросам поиска. При выполнении запросов поисковый узел проверяет временную метку запроса (Lr) и временную метку последнего запроса на обновление данных, обработанного поисковым узлом (Ls). Запрос выполняется только тогда, когда интервал между Lr и Ls меньше delta. В противном случае запрос ожидает выполнения до тех пор, пока не будут обработаны обновления данных, записанные в WAL. Однако если в течение длительного периода времени нет обновлений данных, временной интервал между Ls и текущим системным временем станет настолько малым, что запросы будут заблокированы. Чтобы предотвратить такую проблему, Manu регулярно вставляет управляющую информацию в WAL, заставляя поисковый узел обновлять свою временную метку.
Оценка производительности
В статье мы также интегрировали Manu в реальные приложения и провели общую оценку производительности системы. Ниже приведена часть результатов оценки.
Производительность запросов Manu и других систем векторного поиска.
На рисунке выше Manu сравнивается с четырьмя другими анонимными open-source системами векторного поиска с точки зрения производительности запросов. Мы видим, что Manu явно превосходит другие системы векторного поиска при выполнении запросов на наборах данных SIFT и DEEP.
Производительность запросов Manu при разном количестве узлов.
На рисунке выше показана производительность запросов Manu при изменении количества поисковых узлов. Мы видим, что при запросах к разным наборам данных с разными метриками сходства производительность запросов Manu демонстрирует приблизительно линейную зависимость от количества поисковых узлов.
Производительность запросов Manu при разных уровнях согласованности.
Рисунки выше демонстрируют производительность запросов Manu при разных уровнях согласованности. Горизонтальные координаты представляют значения delta, как в дельта-согласованности. Каждый рисунок отражает частоту отправки управляющей информации в WAL, которая заставляет поисковые узлы синхронизировать время. Из рисунка видно, что задержка запросов Manu резко уменьшается по мере увеличения значения delta. Поэтому пользователям Manu необходимо выбирать подходящее значение delta в соответствии со своими потребностями в производительности и согласованности.
Заключение
В этой статье, основываясь на реальных требованиях к векторной базе данных, мы представили дизайн Manu и рабочие процессы ее основных функций. Вкратце, две основные особенности Manu заключаются в следующем:
Manu использует логовую магистраль для соединения компонентов системы, что обеспечивает независимое масштабирование и развитие каждого компонента и упрощает распределение ресурсов и изоляцию отказов.
Благодаря логовой системе и LSN, Manu использует модель дельта-согласованности, чтобы обеспечить гибкий компромисс между согласованностью, стоимостью и производительностью.
Подводя итог, основной вклад нашей VLDB статьи заключается в представлении реального спроса на векторную базу данных и разработке базовой архитектуры облачно-нативной векторной базы данных. В настоящее время архитектура все еще далека от совершенства, и некоторые из наших будущих направлений включают:
Как извлекать векторы, полученные из мультимодального контента;
Как лучше использовать облачные сервисы хранения, включая локальные диски, облачные диски и другие сервисы хранения, чтобы сделать извлечение данных более эффективным;
Как максимизировать производительность индексирования и поиска с помощью нового вычислительного, хранилищного или коммуникационного оборудования, такого как FPGA、GPU、RDMA、NVM 、RDMA.
Примечание
Год назад я посетил ACM SIGMOD 2021 в Сиане вместе с Чарльзом Се, CEO Zilliz. Идея написать эту статью пришла мне в голову, когда мы возвращались в Шанхай на GA-релиз Milvus 2.0 (Manu). Мы с Чарльзом оба почувствовали, что облачно-нативные базы данных становятся новой горячей темой в академической среде. Это было таким совпадением, что Manu как раз является облачно-нативной и специализированной системой баз данных для массивных векторов. В результате мы взялись написать эту статью о Manu и облачно-нативной системе управления базами данных.
Мы надеемся, что наша статья сможет пролить свет на эту тему и привлечь больше ученых и коллег из индустрии присоединиться к нам в изучении и исследовании облачно-нативных систем управления векторными базами данных.
Мы также хотим выразить благодарность доценту Бо Тану, доценту-исследователю Сяо Яню и Лонгу Сяну за их вклад. Эта статья совместно написана командой Zilliz и Database Group из Southern University of Science and Technology.
Читать далее

My Wife Wanted Dior. I Spent $600 on Claude Code to Vibe-Code a 2M-Line Database Instead.
Write tests, not code reviews. How a test-first workflow with 6 parallel Claude Code sessions turns a 2M-line C++ codebase into a daily shipping pipeline.

Vector Databases vs. Key-Value Databases
Use a vector database for AI-powered similarity search; use a key-value database for high-throughput, low-latency simple data lookups.

Legal Document Analysis: Harnessing Zilliz Cloud's Semantic Search and RAG for Legal Insights
Enhance legal document analysis with Zilliz Cloud’s Semantic Search and RAG. Improve accuracy, efficiency, and scalability for contracts, case law, and compliance.



