Anatomía de un sistema de gestión de bases de datos vectoriales nativo de la nube
Me honra que nuestro artículo más reciente, "Manu: A Cloud Native Vector Database Management System" haya sido aceptado por VLDB'22, una conferencia internacional líder en investigación de bases de datos. En este artículo, analizaré la filosofía y los principios de diseño clave detrás de Manu (nombre del proyecto de Milvus 2.0), una base de datos nativa de la nube diseñada específicamente para la gestión de datos vectoriales. Puedes consultar nuestros artículos anteriores y nuestro repositorio de GitHub para obtener más información.
Antecedentes
Cuando diseñamos Milvus 1.0, nuestro objetivo principal era simplemente dar soporte a la gestión de vectores y optimizar el rendimiento de la recuperación vectorial. Pero a medida que interactuamos con más usuarios a lo largo de los años, descubrimos algunos requisitos empresariales comunes para las bases de datos vectoriales que eran difíciles de abordar con el marco inicial.
Estas necesidades pueden agruparse en las siguientes categorías: requisitos en constante cambio, una política de consistencia más flexible, elasticidad a nivel de componentes y un modelo de procesamiento de transacciones más simple y eficiente.
Requisitos en constante cambio
Los requisitos empresariales aún no están completamente definidos en lo que respecta al procesamiento de datos vectoriales.
En los primeros días, la búsqueda de los K vecinos más cercanos era lo más necesario. Pero luego comenzaron a surgir cada vez más requisitos, incluida la búsqueda por rango, el soporte para diversas métricas de distancia personalizadas, la búsqueda híbrida, la consulta multimodal y otras semánticas de consulta cada vez más diversas.
Esto requiere que la arquitectura de una base de datos vectorial sea lo suficientemente flexible como para admitir nuevos requisitos de forma rápida y ágil.
Una política de consistencia más flexible
Tomemos como ejemplo la recomendación de contenido: este escenario tiene altos requisitos de oportunidad. El nuevo contenido debe recomendarse a los usuarios en cuestión de minutos o incluso segundos, por lo que el sistema no puede tardar un día o más en actualizar su recomendación. En estos escenarios, es difícil garantizar los resultados empresariales proporcionando solo consistencia eventual, mientras que habrá una gran sobrecarga del sistema si insistimos en la consistencia fuerte.
Para abordar este problema, proponemos la siguiente solución: de acuerdo con los requisitos empresariales, los usuarios pueden especificar el retraso máximo que se puede tolerar antes de que los datos insertados puedan consultarse. El sistema, a su vez, ajusta ciertos mecanismos de procesamiento de datos para garantizar el resultado final para el negocio.
Elasticidad a nivel de componentes
Los requisitos de recursos y la intensidad de carga varían considerablemente para cada componente de una base de datos vectorial en diferentes aplicaciones. Por ejemplo, los componentes de recuperación y consulta vectorial requieren grandes recursos computacionales y de memoria para garantizar su rendimiento, mientras que el archivado de datos y la gestión de metadatos solo necesitan unos pocos recursos para funcionar.
En términos de aplicaciones, el requisito más crítico para los sistemas de recomendación es la capacidad de realizar consultas concurrentes a gran escala, por lo que en estos sistemas solo el componente de consulta soporta una carga mayor. Las aplicaciones de analítica, por otro lado, a menudo necesitan importar una gran cantidad de datos sin conexión, por lo que la presión de carga recae en la inserción de datos y la construcción de índices, dos componentes interrelacionados.
Para mejorar la utilización de los recursos, es necesario que cada módulo funcional tenga escalabilidad independiente y elástica, de modo que la asignación de recursos del sistema pueda ajustarse más estrechamente a las necesidades reales de una aplicación.
Un modelo de procesamiento de transacciones más simple y eficiente
Estrictamente hablando, el modelo de transacciones es un espacio de optimización que puede aprovecharse en el diseño del sistema más que un requisito empresarial.
A medida que el aprendizaje automático evoluciona en su capacidad descriptiva, las empresas tienden a fusionar datos de múltiples dimensiones de una sola entidad para representarla como un único vector unificado. Por ejemplo, en la creación de perfiles de usuario, se fusiona información como perfiles personales, preferencias y relaciones sociales. Como resultado, las bases de datos vectoriales pueden mantenerse con una sola tabla, sin tener que implementar operaciones similares a JOIN que son comunes en las bases de datos tradicionales. De este modo, el sistema solo necesita admitir ACID a nivel de fila en una sola tabla y puede prescindir de transacciones complejas que involucran múltiples tablas, dejando un amplio margen para el desacoplamiento de componentes y la optimización del rendimiento en el sistema.
Objetivos
Como la segunda versión principal de Milvus, Manu se posiciona como un sistema de base de datos vectorial distribuido y nativo de la nube.
Cuando nos propusimos diseñar Manu, consideramos los diversos requisitos empresariales mencionados anteriormente y los combinamos con los requisitos comunes de un sistema distribuido. Los resultados son cinco objetivos amplios para Manu: evolucionabilidad a largo plazo, consistencia ajustable, buena elasticidad, alta disponibilidad y alto rendimiento.
Evolucionabilidad a largo plazo
Para controlar la complejidad del sistema a un nivel manejable mientras evolucionan las funcionalidades, necesitamos desacoplar bien el sistema para garantizar que los componentes individuales puedan evolucionar, añadirse o reemplazarse de forma independiente, con una interferencia mínima en otros componentes.
Consistencia ajustable
El sistema necesita admitir consistencia delta para que los usuarios puedan especificar el retraso de visibilidad de las consultas para los datos recién insertados. La consistencia delta requiere que todas las consultas puedan devolver todos los datos relevantes al menos hasta la unidad de tiempo delta, que puede ser especificada por la aplicación del usuario en función de los requisitos empresariales.
Buena elasticidad
Para mejorar la eficiencia de la utilización de recursos, el sistema necesita lograr una elasticidad de grano fino a nivel de componente, así como una política de asignación de recursos que tenga en cuenta diversas dependencias de hardware de los componentes.
Alta disponibilidad y rendimiento
La alta disponibilidad es el requisito básico de todas las bases de datos en la nube, lo que requiere que, en caso de que fallen algunos nodos o componentes de servicio, otros servicios no se vean afectados y que el sistema sea capaz de una recuperación efectiva ante fallos.
El alto rendimiento es un cliché para las bases de datos vectoriales. En el proceso de diseño, necesitamos controlar estrictamente la sobrecarga generada a nivel del marco del sistema para garantizar un buen rendimiento.
Arquitectura de Manu
Manu adopta una arquitectura de cuatro capas que permite el desacoplamiento de lectura y escritura, de lo sin estado y lo con estado, y del almacenamiento y la computación.
Como se muestra en la figura siguiente, de arriba abajo, Manu tiene cuatro capas, es decir, capa de acceso, capa de coordinador, capa de worker y capa de almacenamiento. Manu también utiliza un sistema de logs como su columna vertebral, que conecta los componentes desacoplados.
Arquitectura de Manu.
Capa de acceso
La capa de acceso consiste en proxies sin estado que sirven como endpoints de usuario.
Estos proxies reciben solicitudes de los clientes, distribuyen las solicitudes a los componentes correspondientes y recopilan los resultados antes de devolverlos a los clientes. Además, los proxies almacenan en caché una copia de los metadatos para verificar la legitimidad de las solicitudes de búsqueda (por ejemplo, si existe la colección en la que se va a buscar).
Capa de coordinador
La capa de coordinador gestiona el estado del sistema, mantiene los metadatos y coordina los componentes del sistema para procesar tareas.
Hay cuatro tipos de coordinadores, cada uno diseñado de forma independiente para una funcionalidad diferente. De este modo, los fallos del sistema pueden aislarse y los componentes pueden evolucionar por separado. Por motivos de fiabilidad, cada coordinador puede tener múltiples instancias (por ejemplo, una principal y dos copias de respaldo).
Coordinador raíz
El coordinador raíz gestiona las solicitudes de definición de datos, como crear/eliminar colecciones, y mantiene la metainformación de las colecciones (p. ej., las propiedades de las colecciones, el tipo de dato de cada propiedad).
Coordinador de datos
El coordinador de datos se ocupa de la persistencia de los datos. Coordina los nodos de datos para transformar las solicitudes de actualización de datos en binlogs y registra la información detallada de las colecciones (p. ej., la lista de los segmentos de cada colección, la ruta de almacenamiento de cada segmento).
Coordinador de índices
El coordinador de índices gestiona la indexación de datos. Coordina los nodos de índices para las tareas de indexación y registra la información de índices de cada colección (p. ej., tipo de índice, parámetros relacionados, ruta de almacenamiento, etc.).
Coordinador de consultas
El coordinador de consultas supervisa el estado de los nodos de consulta y ajusta la asignación de segmentos (junto con los índices relacionados) a los nodos de consulta para el equilibrio de carga.
Capa de trabajadores
La capa de trabajadores ejecuta las múltiples tareas del sistema.
Todos los nodos trabajadores son sin estado: obtienen copias de solo lectura de los datos para realizar tareas y no necesitan coordinarse entre sí. Por lo tanto, el número de nodos trabajadores puede ajustarse de forma flexible según la carga. Además, Manu utiliza diferentes nodos trabajadores para diferentes tareas, de modo que cada tipo de nodo pueda escalarse de forma independiente según la carga real y los requisitos de QoS.
Capa de almacenamiento
La capa de almacenamiento almacena de forma persistente la información de estado del sistema, los metadatos, las colecciones y los índices asociados.
Manu utiliza almacenes KV (clave-valor) distribuidos de alta disponibilidad, como etcd, para almacenar la información de estado del sistema y los metadatos. Cuando se actualizan los metadatos, los datos se escribirán primero en el almacén KV y luego se sincronizarán con los coordinadores pertinentes. Los datos de gran volumen, como los de colecciones e índices, se gestionan con servicios de almacenamiento de objetos como AWS S3. La alta latencia que conlleva el almacenamiento de objetos no es un cuello de botella de rendimiento porque los nodos trabajadores toman copias de solo lectura de los datos desde el almacén de objetos y las almacenan en caché localmente antes de procesar los datos, por lo que la mayor parte del procesamiento de datos se realiza localmente.
Columna vertebral de logs
Columna vertebral de logs.
Para desacoplar mejor los componentes del sistema (p. ej., WAL, binlog, nodos de datos, nodos de índices y nodos de consulta), de modo que cada uno pueda escalarse y evolucionar de forma independiente, Manu sigue el paradigma de "log como datos" y utiliza un sistema de logs como su columna vertebral, que conecta los componentes desacoplados del sistema. En Manu, los logs pueden ser suscritos de forma persistente por diferentes componentes del sistema, que por lo tanto se denominan suscriptores de los logs.
Los logs en Manu se pueden dividir en el log de escritura anticipada (WAL) y binlog. El WAL es la parte incremental del log del sistema, mientras que el binlog es la parte base. Se complementan entre sí en retardo, capacidad y coste.
Como se muestra en la figura anterior, los loggers son los puntos de entrada del sistema de logs, que publican datos en el WAL. Los nodos de datos se suscriben al WAL y convierten los WAL basados en filas en binlogs basados en columnas. Todos los componentes de solo lectura, como los nodos de índices y los nodos de consulta, son suscriptores independientes del servicio de logs para mantenerse actualizados.
El sistema de logs también sirve para pasar mensajes entre componentes. En otras palabras, los componentes pueden difundir eventos del sistema mediante logs. Por ejemplo, los nodos de datos pueden informar a otros componentes qué segmentos se han escrito en el almacenamiento de objetos, y los nodos de índices pueden informar a todos los coordinadores de consultas tan pronto como se hayan creado nuevos índices. Además, diferentes tipos de mensajes se organizan en diferentes canales. Cada componente solo necesita suscribirse a su canal correspondiente en lugar de escuchar todos los logs difundidos.
Flujo de trabajo de procesamiento de datos
Esta sección desarrolla el flujo de trabajo de procesamiento de datos dentro de Manu e introduce el proceso de inserción de datos, creación de índices y ejecución de consultas.
Inserción de datos
Flujo de inserción de datos.
La figura anterior ilustra el flujo de trabajo de la inserción de datos en Manu y los componentes relevantes involucrados.
Después de ser procesadas por el proxy, las solicitudes de inserción de datos se distribuyen en varios buckets según algoritmos hash. Generalmente, hay múltiples loggers en el sistema Manu que manejan las entidades de cada bucket hash según hashing consistente. Las entidades de cada bucket hash se escriben en un canal de registro de escritura anticipada (WAL) que solo se asigna a este bucket. Cuando un logger recibe una solicitud de inserción de datos, asigna un número de secuencia de registro (LSN) globalmente único a esta solicitud y la escribe en el canal WAL correspondiente. El LSN es generado por el oráculo del servicio central de tiempo (TSO). Cada logger necesita recibir un LSN del TSO y guardar el LSN localmente a intervalos regulares.
Para garantizar que la publicación/suscripción de logs tenga un retraso bajo y sea de grano fino, las entidades se almacenan por filas en el WAL en Manu, y cada componente que se suscribe al WAL lee datos de él en modo streaming. En la mayoría de los casos, WAL puede implementarse mediante una cola de mensajes basada en la nube como Kafka o Pulsar. Los nodos de datos se suscriben a los WAL y convierten los WAL basados en filas en binlogs basados en columnas. La naturaleza basada en columnas del binlog facilita la compresión y el acceso a los datos. Un ejemplo de esta eficiencia se da con los nodos de índice. Los nodos de índice solo leen la columna vectorial requerida del binlog para la construcción del índice y, por lo tanto, están libres de amplificaciones de lectura.
Construcción de índices
Hay dos escenarios de construcción de índices en Manu: indexación por lotes e indexación en streaming. La indexación por lotes ocurre cuando el usuario construye un índice para una colección completa (por ejemplo, cuando todos los vectores se actualizan con un nuevo modelo de embedding). En este caso, el coordinador de índices obtiene las rutas de todos los segmentos de la colección desde el coordinador de datos e instruye a los nodos de índice para que construyan un índice para cada segmento. La indexación en streaming ocurre cuando los usuarios insertan continuamente nuevas entidades, y los índices se construyen asincrónicamente sobre la marcha sin interrumpir los servicios de búsqueda.
Cuando el nodo de datos escribe un nuevo segmento en el binlog, el coordinador de datos notifica al coordinador de índices para crear una tarea para que un nodo de índice construya un índice sobre el nuevo segmento. Tanto en los escenarios de indexación por lotes como en los de indexación en streaming, después de que se construye el índice requerido para un segmento, el nodo de índice lo persiste en el almacenamiento de objetos y envía la ruta de almacenamiento al coordinador de índices, notificando al coordinador de consultas para que los nodos de consulta puedan cargar el índice para procesar consultas.
Ejecución de consultas
Manu particiona una colección en segmentos y distribuye los segmentos entre nodos de consulta para ejecutar solicitudes de consulta en paralelo. Los proxies almacenan en caché una copia de la distribución de los segmentos en los nodos de consulta consultando al coordinador de consultas, y despachan las solicitudes de búsqueda a los nodos de consulta que contienen segmentos de la colección buscada. Los nodos de consulta realizan consultas vectoriales en sus segmentos locales, fusionan los resultados y los devuelven al proxy. El proxy agrega además los resultados por cada nodo de consulta y devuelve los resultados finales al cliente.
Los nodos de consulta obtienen datos de tres fuentes: el WAL, los archivos de índice y el binlog. Para los datos históricos, los nodos de consulta leen los binlogs o archivos de índice correspondientes desde el almacenamiento de objetos. Mientras que para los datos incrementales, los nodos de consulta leen directamente desde el WAL en modo streaming. Obtener datos incrementales desde el binlog causará latencia en la visibilidad de los datos, lo cual es especialmente cierto para solicitudes de búsqueda grandes. En otras palabras, los datos recién insertados solo estarán disponibles para consulta después de un largo período de tiempo, lo que no satisface la necesidad de alta consistencia en algunos escenarios.
Como se mencionó anteriormente, Manu adopta un modelo de consistencia delta para permitir a los usuarios ajustar los niveles de consistencia de manera más flexible. La consistencia delta garantiza que los datos actualizados (incluidos los datos insertados y eliminados) puedan consultarse y buscarse hasta delta unidades de tiempo después de que Manu reciba la solicitud de actualización de datos.
Manu logra la consistencia delta añadiendo LSNs con marcas de tiempo a todas las solicitudes de inserción de datos y consulta. Al ejecutar solicitudes de consulta, el nodo de consulta comprueba la marca de tiempo de la solicitud (Lr) y la marca de tiempo de la solicitud de actualización de datos más reciente procesada por el nodo de consulta (Ls). La solicitud de consulta se ejecuta solo cuando el intervalo entre Lr y Ls es menor que delta. De lo contrario, la solicitud de consulta espera a ejecutarse hasta que se procesen las actualizaciones de datos registradas en WAL. Sin embargo, si no hay actualizaciones de datos durante un largo periodo de tiempo, el intervalo de tiempo entre Ls y la hora actual del sistema se volverá tan pequeño que las consultas se bloquearán. Para evitar este problema, Manu inserta regularmente información de control en el WAL, obligando al nodo de consulta a actualizar su marca de tiempo.
Evaluación del rendimiento
En el artículo, también integramos Manu en aplicaciones del mundo real y llevamos a cabo una evaluación general del rendimiento del sistema. Los siguientes son parte de los resultados de la evaluación.
Rendimiento de consulta de Manu y otros sistemas de búsqueda vectorial.
La figura anterior compara Manu con otros cuatro sistemas anónimos de búsqueda vectorial de código abierto en términos de rendimiento de consulta. Podemos ver que Manu supera claramente a otros sistemas de búsqueda vectorial al realizar consultas en los conjuntos de datos SIFT y DEEP.
Rendimiento de consulta de Manu con diferentes números de nodos.
La figura anterior muestra el rendimiento de consulta de Manu cuando varía el número de nodos de consulta. Podemos ver que al consultar diferentes conjuntos de datos con diferentes métricas de similitud, el rendimiento de consulta de Manu presenta una relación aproximadamente lineal con el número de nodos de consulta.
Rendimiento de consulta de Manu bajo diferentes niveles de consistencia.
Las figuras anteriores demuestran el rendimiento de consulta de Manu bajo diferentes niveles de consistencia. Las coordenadas horizontales representan los valores de delta como en la consistencia delta. Cada figura refleja la frecuencia de la información de control enviada a WAL que obliga a los nodos de consulta a sincronizar el tiempo. Podemos ver en la figura que la latencia de consulta de Manu disminuye drásticamente a medida que aumenta el valor de delta. Por lo tanto, los usuarios de Manu necesitan elegir el valor delta adecuado de acuerdo con su necesidad de rendimiento y consistencia.
Conclusión
En este artículo, basándonos en requisitos del mundo real para bases de datos vectoriales, hemos presentado los diseños de Manu y los flujos de trabajo de sus principales funcionalidades. En resumen, las dos características principales de Manu son las siguientes:
Manu utiliza la columna vertebral de registros para conectar los componentes del sistema, lo que permite el escalado y la evolución independientes de cada componente y facilita la asignación de recursos y el aislamiento de fallos.
Con el sistema de registros y el LSN, Manu adopta un modelo de consistencia delta para permitir un equilibrio flexible entre consistencia, coste y rendimiento.
En resumen, la principal contribución de nuestro artículo de VLDB radica en la introducción de la demanda del mundo real de bases de datos vectoriales y el diseño de la arquitectura básica de una base de datos vectorial nativa de la nube. En la actualidad, la arquitectura aún está lejos de ser perfecta y algunas de nuestras direcciones futuras incluyen:
Cómo recuperar vectores extraídos de contenido multimodal;
Cómo aprovechar mejor los servicios de almacenamiento en la nube, incluidos discos locales, unidades en la nube y otros servicios de almacenamiento, para hacer que la recuperación de datos sea más eficiente;
Cómo maximizar el rendimiento de indexación y búsqueda con la ayuda de nuevo hardware de computación, almacenamiento o comunicación como FPGA、GPU、RDMA、NVM 、RDMA.
Nota final
Hace un año, asistí a ACM SIGMOD 2021 en Xi'an con Charles Xie, CEO de Zilliz. La idea de escribir este artículo se me ocurrió mientras íbamos de regreso a Shanghái para el lanzamiento GA de Milvus 2.0 (Manu). Tanto Charles como yo percibimos que las bases de datos nativas de la nube se estaban convirtiendo en el nuevo tema candente en el mundo académico. Fue una gran coincidencia que Manu sea precisamente un sistema de base de datos nativo de la nube y diseñado específicamente para vectores masivos. Como resultado, nos pusimos a escribir este artículo sobre Manu y el sistema de gestión de bases de datos nativo de la nube.
Esperamos que nuestro artículo pueda arrojar algo de luz y atraer a más académicos y colegas de la industria a unirse a nosotros para explorar e investigar los sistemas de gestión de bases de datos vectoriales nativos de la nube.
También queremos expresar nuestro agradecimiento al profesor asistente Bo Tang, al profesor asistente de investigación Xiao Yan y a Long Xiang por su contribución. Este artículo está escrito conjuntamente por el equipo de Zilliz y el Grupo de Bases de Datos de la Southern University of Science and Technology.
Sigue leyendo

What Is a Vector Lakebase?
A Vector Lakebase is a unified, lake-native data architecture for AI that combines vector-database-grade serving with open lake storage, reusable lake-level indexes, and a shared semantic layer.

From Vector Database to Vector Lakebase
Zilliz offers a fully managed Vector Lakebase powered by Milvus, unifying real-time vector search, lake-scale discovery, and Al data operations.

Build for the Boom: Why AI Agent Startups Should Build Scalable Infrastructure Early
Explore strategies for developing AI agents that can handle rapid growth. Don't let inadequate systems undermine your success during critical breakthrough moments.



