Crea un índice de ScaNN

Usa embeddings almacenados para generar índices de vectores de ScaNN y consultar embeddings con AlloyDB para PostgreSQL.

El índice ScaNN es un índice de cuantización basado en árboles creado por Google para la búsqueda aproximada de vecinos más cercanos. Proporciona un menor tiempo de compilación del índice y un menor espacio en memoria en comparación con HNSW. Además, proporciona un QPS más rápido en comparación con HNSW según la carga de trabajo.

Antes de comenzar

Antes de comenzar a crear índices, debes completar los siguientes requisitos previos.

Crea un índice optimizado automáticamente

Los índices ScaNN ajustados automáticamente simplifican la creación de índices, ya que permiten que AlloyDB administre y ajuste la estructura del índice. Si necesitas un control detallado sobre el ajuste del índice, crea un índice de ScaNN ajustado manualmente.

Los índices ajustados automáticamente se pueden optimizar de dos maneras:

  • (Predeterminado) Recuperación y latencia de la búsqueda de vectores a costa del tiempo de compilación del índice
  • Equilibra el tiempo de compilación del índice y el rendimiento de la búsqueda

Para crear un índice de ScaNN ajustado automáticamente, ejecuta el siguiente comando.

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)

Reemplaza lo siguiente:

  • INDEX_NAME: Es el nombre del índice que deseas crear. Por ejemplo, my_scann_index Los nombres de los índices se comparten en toda la base de datos. Verifica que cada nombre de índice sea único para cada tabla de tu base de datos.

  • TABLE: Es la tabla a la que se agregará el índice.

  • EMBEDDING_COLUMN: Columna que almacena datos vector.

  • DISTANCE_FUNCTION: Es la función de distancia que se usará con este índice. Elige una de estas opciones:

    • Distancia de L2: l2

    • Producto punto: dot_product

    • Distancia de coseno: cosine

Este comando crea un índice de ScaNN optimizado para el rendimiento de la búsqueda y realiza el mantenimiento automático del índice. Si quieres cambiar alguna de estas opciones de configuración, ejecuta el siguiente comando:

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (MODE='AUTO',
      OPTIMIZATION='OPTIMIZATION',
      auto_maintenance='AUTO_MAINTENANCE')

Reemplaza lo siguiente:

  • INDEX_NAME: Es el nombre del índice que deseas crear. Por ejemplo, my_scann_index Los nombres de los índices se comparten en toda la base de datos. Verifica que cada nombre de índice sea único para cada tabla de tu base de datos.

  • TABLE: Es la tabla a la que se agregará el índice.

  • EMBEDDING_COLUMN: Columna que almacena datos vector.

  • DISTANCE_FUNCTION: Es la función de distancia que se usará con este índice. Elige una de estas opciones:

    • Distancia de L2: l2

    • Producto punto: dot_product

    • Distancia de coseno: cosine

  • (Opcional) OPTIMIZATION: Establece uno de los siguientes valores:

    • (Predeterminado) SEARCH_OPTIMIZED: Optimiza la recuperación y la latencia de la búsqueda de vectores a costa de tiempos de compilación de índices más largos.

    • BALANCED: Equilibra el tiempo de compilación del índice y el rendimiento de la búsqueda.

    Si se establece OPTIMIZATION, también se debe incluir MODE='AUTO'.

  • (Opcional) AUTO_MAINTENANCE: Controla si el mantenimiento automático del índice está habilitado o inhabilitado. Para obtener más información sobre el mantenimiento automático, consulta Cómo mantener índices vectoriales.

    • (Predeterminado) ON: AlloyDB realiza el mantenimiento automático del índice.

    • OFF: AlloyDB no realiza mantenimiento automático en el índice.

Crea un índice ajustado manualmente

Si tu aplicación tiene requisitos específicos para los tiempos de recuperación y de compilación del índice, puedes crear y ajustar manualmente el índice de ScaNN.

Para crear manualmente un índice de ScaNN para una columna que contiene embeddings de vectores almacenados, consulta los siguientes comandos.

Índice de árbol de dos niveles

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      auto_maintenance=AUTO_MAINTENANCE);
  • INDEX_NAME: Es el nombre del índice que deseas crear. Por ejemplo: my_scann_index. Los nombres de los índices se comparten en toda tu base de datos. Asegúrate de que cada nombre de índice sea único para cada tabla de tu base de datos.
  • TABLE: Es la tabla a la que se agregará el índice.
  • EMBEDDING_COLUMN: Columna que almacena datos de "vector".
  • DISTANCE_FUNCTION: Función de distancia que se usará con este índice. Elige una de las siguientes opciones:
    • Distancia de L2: l2
    • Producto punto: dot_product
    • Distancia de coseno: cosine
  • NUM_LEAVES_VALUE: Cantidad de particiones que se aplicarán a este índice. Se puede establecer en cualquier valor entre 1 y 30 millones. Para obtener más información sobre cómo elegir este valor, consulta Cómo ajustar un índice de ScaNN.
  • QUANTIZER: Tipo de cuantificador que se usará. Ten en cuenta que el índice de ScaNN se puede cargar en el motor de columnas para acelerar aún más la búsqueda de vectores. Elige una de las siguientes opciones:
    • (Predeterminado) SQ8: Proporciona un equilibrio entre el rendimiento de las consultas y la pérdida mínima de recuperación. Por lo general, es menos del 1 o 2%.
    • Vista previa AH: El hash asimétrico (AH) se comprime hasta 4 veces más en comparación con SQ8. Para obtener un mejor rendimiento de las consultas cuando el motor de columnas está habilitado y los datos de índice y tabla se propagan en el motor de columnas, considera esta opción para obtener un mejor rendimiento de las consultas. Para obtener más información, consulta las prácticas recomendadas para ajustar ScaNN.
    • FLAT: Proporciona el mayor nivel de recuperación, un 99% o más, a costa del rendimiento de la búsqueda.
  • (Opcional) AUTO_MAINTENANCE: Controla si el mantenimiento automático del índice está habilitado o inhabilitado. Para obtener más información sobre el mantenimiento automático, consulta Cómo mantener índices vectoriales.
    • (Predeterminado) ON: AlloyDB realiza el mantenimiento automático del índice.
    • OFF: AlloyDB no realiza mantenimiento automático en el índice.

Índice de árbol de tres niveles

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      auto_maintenance=AUTO_MAINTENANCE,
      max_num_levels = 2);
  • INDEX_NAME: Es el nombre del índice que deseas crear. Por ejemplo: my_scann_index. Los nombres de los índices se comparten en toda tu base de datos. Asegúrate de que cada nombre de índice sea único para cada tabla de tu base de datos.
  • TABLE: Es la tabla a la que se agregará el índice.
  • EMBEDDING_COLUMN: Columna que almacena datos de "vector".
  • DISTANCE_FUNCTION: Función de distancia que se usará con este índice. Elige una de las siguientes opciones:
    • Distancia de L2: l2
    • Producto punto: dot_product
    • Distancia de coseno: cosine
  • NUM_LEAVES_VALUE: Cantidad de particiones que se aplicarán a este índice. Se puede establecer en cualquier valor entre 1 y 30 millones. Para obtener más información sobre cómo elegir este valor, consulta Cómo ajustar un índice de ScaNN.
  • QUANTIZER: Tipo de cuantificador que se usará. Ten en cuenta que el índice de ScaNN se puede cargar en el motor de columnas para acelerar aún más la búsqueda de vectores. Elige una de las siguientes opciones:
    • (Predeterminado) SQ8: Proporciona un equilibrio entre el rendimiento de las consultas y la pérdida mínima de recuperación. Por lo general, es menos del 1 o 2%.
    • Vista previa AH: El hash asimétrico (AH) se comprime hasta 4 veces más en comparación con SQ8. Para obtener un mejor rendimiento de las consultas cuando el motor de columnas está habilitado y los datos de índice y tabla se propagan en el motor de columnas, considera esta opción para obtener un mejor rendimiento de las consultas. Para obtener más información, consulta las prácticas recomendadas para ajustar ScaNN.
    • FLAT: Proporciona el mayor nivel de recuperación, un 99% o más, a costa del rendimiento de la búsqueda.
  • (Opcional) AUTO_MAINTENANCE: Controla si el mantenimiento automático del índice está habilitado o inhabilitado. Para obtener más información sobre el mantenimiento automático, consulta Cómo mantener índices vectoriales.
    • (Predeterminado) ON: AlloyDB realiza el mantenimiento automático del índice.
    • OFF: AlloyDB no realiza mantenimiento automático en el índice.

Índice de árbol de cuatro niveles

Los índices de árbol de cuatro niveles se encuentran en Vista previa.

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      max_num_levels = 3);
  • INDEX_NAME: Es el nombre del índice que deseas crear. Por ejemplo: my_scann_index. Los nombres de los índices se comparten en toda tu base de datos. Asegúrate de que cada nombre de índice sea único para cada tabla de tu base de datos.
  • TABLE: Es la tabla a la que se agregará el índice.
  • EMBEDDING_COLUMN: Columna que almacena datos de "vector".
  • DISTANCE_FUNCTION: Función de distancia que se usará con este índice. Elige una de las siguientes opciones:
    • Distancia de L2: l2
    • Producto punto: dot_product
    • Distancia de coseno: cosine
  • NUM_LEAVES_VALUE: Cantidad de particiones que se aplicarán a este índice. Se puede establecer en cualquier valor entre 1 y 30 millones. Para obtener más información sobre cómo elegir este valor, consulta Cómo ajustar un índice de ScaNN.
  • QUANTIZER: Tipo de cuantificador que se usará. Ten en cuenta que el índice de ScaNN se puede cargar en el motor de columnas para acelerar aún más la búsqueda de vectores. Elige una de las siguientes opciones:
    • (Predeterminado) SQ8: Proporciona un equilibrio entre el rendimiento de las consultas y la pérdida mínima de recuperación. Por lo general, es menos del 1 o 2%.
    • Vista previa AH: El hash asimétrico (AH) se comprime hasta 4 veces más en comparación con SQ8. Para obtener un mejor rendimiento de las consultas cuando el motor de columnas está habilitado y los datos de índice y tabla se propagan en el motor de columnas, considera esta opción para obtener un mejor rendimiento de las consultas. Para obtener más información, consulta las prácticas recomendadas para ajustar ScaNN.
    • FLAT: Proporciona el mayor nivel de recuperación, un 99% o más, a costa del rendimiento de la búsqueda.

Cómo convertir un índice ajustado manualmente en un índice ajustado automáticamente

Para convertir un índice ajustado manualmente en un índice ajustado automáticamente, completa los siguientes pasos:

  1. Restablece todos los parámetros de consulta definidos para tu índice ajustado manualmente.

    ALTER INDEX INDEX_NAME RESET (PARAMETER_NAME);
    

    Reemplaza las siguientes variables:

    • INDEX_NAME: Es el nombre del índice que deseas convertir. Por ejemplo, my_scann_index Los nombres de los índices se comparten en toda la base de datos. Verifica que cada nombre de índice sea único para cada tabla de tu base de datos.

    • PARAMETER_NAME: Lista separada por comas que contiene los nombres de los parámetros de consulta que deseas restablecer. Por ejemplo, num_leaves, quantization

      Ten en cuenta que debes restablecer todos los demás parámetros de consulta antes de restablecer num_leaves.

  2. Vuelve a indexar tu índice ajustado manualmente para convertirlo en un índice ajustado automáticamente.

    REINDEX INDEX CONCURRENTLY INDEX_NAME;
    

Crea un índice de ScaNN para los tipos de datos de real[]

Para crear un índice para una columna de embedding que use el tipo de datos real[] en lugar de vector, convierte la columna en el tipo de datos vector:

CREATE INDEX INDEX_NAME ON TABLE
USING scann (CAST(EMBEDDING_COLUMN AS vector(DIMENSIONS)) DISTANCE_FUNCTION)

Reemplaza lo siguiente:

  • INDEX_NAME: Es el nombre del índice que deseas crear. Por ejemplo, my_scann_index Los nombres de los índices se comparten en toda la base de datos. Verifica que cada nombre de índice sea único para cada tabla de tu base de datos.

  • TABLE: Es la tabla a la que se agregará el índice.

  • DIMENSIONS: Es la cantidad de dimensiones que admite el modelo.

  • EMBEDDING_COLUMN: Columna que almacena datos vector.

  • DISTANCE_FUNCTION: Es la función de distancia que se usará con este índice. Elige una de estas opciones:

    • Distancia de L2: l2

    • Producto punto: dot_product

    • Distancia de coseno: cosine

Cómo ver el progreso de la indexación

Para ver el progreso de la indexación, usa la vista pg_stat_progress_create_index:

SELECT * FROM pg_stat_progress_create_index;

La columna phase muestra el estado actual de la creación del índice. Una vez que se completa la fase de compilación del índice, no se ve la fila del índice.

Crea un índice diferido para tablas vacías o con una cantidad insuficiente de filas

De forma predeterminada, no puedes crear un índice de ScaNN en una tabla que esté vacía o que tenga menos filas que el valor de la opción de índice num_leaves.

Para evitar esta limitación, habilita la creación diferida de índices para que AlloyDB la difiera hasta que la cantidad de filas de la tabla alcance el umbral que define num_leaves. Una vez que se alcanza el umbral, AlloyDB comienza la compilación del índice en segundo plano.

Esta operación diferida es un proceso no bloqueante que permite que otras operaciones de la base de datos, como las lecturas y escrituras, continúen sin interrupciones. Dado que la recompilación del índice se realiza en segundo plano, la creación diferida del índice es adecuada cuando las tablas incorporan filas de datos en lotes pequeños. La recompilación del índice se activa automáticamente después de que la cantidad de filas alcanza el umbral.

Sin embargo, si planeas insertar una gran cantidad de filas en la tabla en una sola transacción, te recomendamos que dividas la transacción en varias o que generes un índice de ScaNN sin habilitar la creación diferida de índices.

Habilita la creación diferida de índices

Para habilitar la creación diferida de índices, sigue estos pasos:

  1. Habilita la marca scann.enable_index_maintenance (habilitada de forma predeterminada) y la marca scann.enable_preview_features. La marca scann.enable_preview_features también habilita otras funciones de vista previa.

    gcloud alloydb instances update INSTANCE_ID \
       --database-flags scann.enable_index_maintenance=on \
       --database-flags scann.enable_preview_features=on \
       --region=REGION_ID \
       --cluster=CLUSTER_ID \
       --project=PROJECT_ID
    

    Reemplaza lo siguiente:

    • INSTANCE_ID: El ID de la instancia.
    • REGION_ID: Es la región en la que se coloca la instancia, por ejemplo, us-central1.
    • CLUSTER_ID: Es el ID del clúster en el que se coloca la instancia.
    • PROJECT_ID: Es el ID del proyecto en el que se coloca el clúster.
  2. Crea un índice de ScaNN. Si creas un índice en modo manual, asegúrate de que el parámetro auto_maintenance esté configurado como on. Para obtener más información, consulta Crea un índice ajustado manualmente.

Limitaciones

  • El proceso en segundo plano de creación automática de índices usa valores de marcas a nivel de la base de datos. Incluso si configuras marcas a nivel de la sesión con el comando SET LOCAL, el proceso considera el valor de la marca establecido a nivel de la base de datos.
  • Si planeas insertar de forma masiva una gran cantidad de datos en una tabla vacía en una sola transacción, te recomendamos que ejecutes la transacción de inserción única y, luego, crees un índice de ScaNN.

Forzar la creación de índices en tablas vacías o pequeñas

AlloyDB usa validaciones para evitar la creación de un índice de ScaNN en una tabla vacía o con muy pocas filas por los siguientes motivos:

  • El índice de ScaNN se entrena con datos insuficientes. Esto puede generar una recuperación deficiente para las búsquedas de similitud de vectores.

  • Es posible que disminuya el rendimiento de escritura en la base de datos.

Te recomendamos que aplaces la creación de índices en caso de un rendimiento no óptimo.

Sin embargo, en algunos casos de desarrollo o prueba, es posible que debas crear un índice en una tabla vacía o pequeña. En estos casos, puedes forzar la creación de índices. Ten en cuenta que forzar la creación del índice requiere privilegios de SUPERUSER.

Para forzar la creación del índice, completa los siguientes pasos:

  1. Establece el parámetro scann.allow_blocked_operations creation a nivel de la sesión en true en la base de datos:

    SET scann.allow_blocked_operations = true;
    
  2. Si el usuario que usas para ejecutar estas consultas no tiene privilegios de SUPERUSER, asígnale los siguientes:

    CREATE USER USERNAME WITH SUPERUSER PASSWORD PASSWORD;
    

    Reemplaza las siguientes variables:

    • USERNAME: Es el nombre del usuario al que deseas otorgar privilegios de SUPERUSER.
    • PASSWORD: Contraseña del usuario.

Compila índices en paralelo

Para compilar tu índice más rápido, AlloyDB podría generar automáticamente varios trabajadores paralelos según tu conjunto de datos y el tipo de índice que elijas. Esto suele suceder cuando se crea un índice de ScaNN de tres o cuatro niveles, o bien si tu conjunto de datos supera los 100 millones de filas.

Si bien AlloyDB optimiza automáticamente la cantidad de trabajadores paralelos, puedes ajustarlos con los siguientes parámetros de planificación de consultas de PostgreSQL:

Para evitar problemas de memoria insuficiente cuando crees el índice de ScaNN, asegúrate de que las marcas de base de datos maintenance_work_mem y shared_buffers estén configuradas en un valor inferior a la memoria total de la máquina.

Ejecuta una consulta

Después de almacenar y, también, indexar las embeddings en tu base de datos, puedes comenzar a consultar tus datos. No puedes ejecutar búsquedas masivas con la extensión de alloydb_scann.

Para encontrar los vecinos semánticos más cercanos a una cadena de texto, puedes usar la función google_ml.embedding() para traducir el texto a un vector.

Como google_ml.embedding() devuelve un array real, debes convertir de forma explícita la llamada a función en vector antes de aplicarla a uno de los operadores de vecinos más cercanos, por ejemplo, <-> para la distancia L2. Luego, estos operadores pueden usar el índice de ScaNN para encontrar las filas de la base de datos con las incorporaciones semánticamente más similares.

SELECT * FROM TABLE
ORDER BY EMBEDDING_COLUMN DISTANCE_FUNCTION_QUERY
  google_ml.embedding(
      model_id => 'MODEL_ID',
      content => 'CONTENT')::vector
LIMIT ROW_COUNT

Reemplaza las siguientes variables:

  • TABLE: Es la tabla que contiene la embedding con la que se comparará el texto.

  • EMBEDDING_COLUMN: columna que contiene las incorporaciones almacenadas.

  • DISTANCE_FUNCTION_QUERY: Función de distancia que se usará con esta búsqueda. Elige el equivalente de la consulta para la función de distancia cuando creaste el índice:

    • Distancia de L2: <->

    • Producto interno: <#>

    • Distancia de coseno: <=>

  • MODEL_ID: ID del modelo de incorporación registrado que deseas usar.

  • CONTENT: Es la cadena de texto que deseas traducir en una incorporación y buscar.

  • ROW_COUNT: Es la cantidad de filas que se devolverán. Por ejemplo, especifica 1 si deseas la mejor coincidencia única.

¿Qué sigue?