Administra índices vectoriales

Para enviar comentarios o solicitar asistencia para esta función, envía un correo electrónico a bq-vector-search@google.com.

En este documento, se describe cómo crear y administrar índices vectoriales.

Un índice vectorial es una estructura de datos diseñada para permitir que la función VECTOR_SEARCH realice una búsqueda de vectores más eficiente de las incorporaciones. Cuando VECTOR_SEARCH puede usar un índice vectorial, la función usa la técnica de búsqueda del vecino más cercano aproximado para ayudar a mejorar el rendimiento de la búsqueda a cambio de reducir la recuperación y, por lo tanto, muestra resultados más aproximados.

Funciones y permisos

Para crear un índice vectorial, necesitas el permiso de IAM bigquery.tables.createIndex en la tabla en la que creas el índice. Para descartar un índice vectorial, necesitas el permiso bigquery.tables.deleteIndex. Cada uno de los siguientes roles predefinidos de IAM incluye los permisos que necesitas para trabajar con índices vectoriales:

  • Propietario de datos de BigQuery (roles/bigquery.dataOwner)
  • Editor de datos de BigQuery (roles/bigquery.dataEditor)

Crea un índice vectorial

Para crear un índice vectorial, usa la declaración de lenguaje de definición de datos (DDL) CREATE VECTOR INDEX:

  1. Ve a la página de BigQuery.

    Ir a BigQuery

  2. En el editor de consultas, ejecuta la siguiente instrucción de SQL:

    CREATE [ OR REPLACE ] VECTOR INDEX [ IF NOT EXISTS ] INDEX_NAME
    ON DATASET_NAME.TABLE_NAME(COLUMN_NAME)
    OPTIONS(index_type = INDEX_TYPE,
      distance_type = DISTANCE_TYPE,
      ivf_options = '{"num_lists":NUM_LISTS}')
    

    Reemplaza lo siguiente:

    • INDEX_NAME: es el nombre del índice vectorial que creas. Debido a que el índice siempre se crea en el mismo proyecto y conjunto de datos que la tabla base, no es necesario especificarlos en el nombre.
    • DATASET_NAME: es el nombre del conjunto de datos que contiene la tabla.
    • TABLE_NAME: es el nombre de la tabla que contiene la columna con datos de incorporaciones.
    • COLUMN_NAME: es el nombre de una columna que contiene los datos de las incorporaciones. La columna debe tener un tipo de ARRAY<FLOAT64>. La columna no puede tener ningún campo secundario. Todos los elementos del array deben ser distintos de NULL y todos los valores de la columna deben tener las mismas dimensiones de array.
    • INDEX_TYPE: es el algoritmo que se usará para compilar el índice vectorial. IVF es el único valor admitido. Si especificas IVF, se compila el índice vectorial como índice de archivo invertido (IVF). Un IVF usa un algoritmo k-means para agrupar los datos vectoriales y, luego, particiona los datos vectoriales en función de esos clústeres. Cuando usas la función VECTOR_SEARCH con el objetivo de buscar datos vectoriales, puede usar estas particiones para reducir la cantidad de datos que necesita leer a fin de determinar un resultado.
    • DISTANCE_TYPE: especifica el tipo de distancia predeterminado que se usará cuando se realice una búsqueda vectorial mediante este índice. Los valores admitidos son EUCLIDEAN y COSINE. EUCLIDEAN es la configuración predeterminada.

      La creación del índice siempre usa la distancia EUCLIDEAN para el entrenamiento, pero la distancia que se usa en la función VECTOR_SEARCH puede ser diferente.

      Si especificas un valor para el argumento distance_type de la función VECTOR_SEARCH, se usa ese valor en lugar del valor DISTANCE_TYPE.

    • NUM_LISTS: un valor INT64 menor o igual que 5,000 que determina cuántas listas crea el algoritmo de IVF. El algoritmo IVF divide todo el espacio de datos en una cantidad de listas igual a NUM_LISTS, y los datos que están más cerca entre sí tienen más probabilidades de incluirse en la misma lista. Si NUM_LISTS es pequeño, tienes menos listas con más datos, mientras que un valor mayor crea más listas con menos datos.

      Puedes usar NUM_LISTS junto con el argumento fraction_lists_to_search en la función VECTOR_SEARCH para crear una búsqueda de vectores eficiente. Si tienes datos que se distribuyen en muchos grupos pequeños en el espacio de incorporaciones, especifica un NUM_LISTS alto para crear un índice con más listas y especifica un valor fraction_lists_to_search más bajo para analizar menos listas en la búsqueda vectorial. Usa un valor de NUM_LISTS más bajo y un valor de fraction_lists_to_search más alto cuando tus datos se distribuyan en menos grupos, más grandes. El uso de un valor num_lists alto puede hacer que el índice vectorial tarde más en compilarse.

      Si no especificas NUM_LISTS, BigQuery calcula un valor apropiado.

En el siguiente ejemplo, se crea un índice vectorial en la columna embedding de my_table:

CREATE TABLE my_dataset.my_table(embedding ARRAY<FLOAT64>);

CREATE VECTOR INDEX my_index ON my_dataset.my_table(embedding)
OPTIONS(index_type = 'IVF');

En el siguiente ejemplo, se crea un índice vectorial en la columna embedding de my_table y se especifica el tipo de distancia que se usará y las opciones de IVF:

CREATE TABLE my_dataset.my_table(embedding ARRAY<FLOAT64>);

CREATE VECTOR INDEX my_index ON my_dataset.my_table(embedding)
OPTIONS(index_type = 'IVF', distance_type = 'COSINE',
ivf_options = '{"num_lists": 2500}')

Comprende la actualización de índices

BigQuery administra completamente los índices vectoriales y los actualizan de forma automática cuando cambia la tabla indexada. Si borras la columna indexada en una tabla o cambias el nombre de la tabla, el índice vectorial se borra de forma automática.

Si creas un índice vectorial en una tabla de menos de 10 MB, el índice vectorial no se propaga. Del mismo modo, si borras datos de una tabla indexada y el tamaño de la tabla es inferior a 10 MB, el índice vectorial se inhabilita temporalmente. En este caso, las consultas de búsqueda vectorial no usan el índice, y el código indexUnusedReasons en la sección vectorSearchStatistics del recurso Job es BASE_TABLE_TOO_SMALL. Sin el índice, VECTOR_SEARCH de forma automática aplica la fuerza bruta para encontrar los vecinos más cercanos de las incorporaciones.

Las consultas que usan la función VECTOR_SEARCH siempre muestran resultados correctos, incluso si alguna parte de los datos aún no está indexada.

Obtén información sobre los índices vectoriales

Puedes verificar la existencia y la preparación de un índice vectorial mediante una consulta a INFORMATION_SCHEMA. Las siguientes vistas contienen metadatos en índices vectoriales:

  • La vista INFORMATION_SCHEMA.VECTOR_INDEXES contiene información sobre los índices vectoriales de un conjunto de datos.

    Una vez que se completa la declaración CREATE VECTOR INDEX, el índice debe propagarse antes de que puedas usarlo. Puedes usar las columnas last_refresh_time y coverage_percentage para verificar la preparación de un índice vectorial. Si el índice vectorial no está listo, puedes usar la función VECTOR_SEARCH en una tabla, que podría ejecutarse más lento sin el índice.

  • La vista INFORMATION_SCHEMA.VECTOR_INDEX_COLUMNS tiene información sobre las columnas indexadas por vector para todas las tablas en un conjunto de datos.

  • La vista INFORMATION_SCHEMA.VECTOR_INDEX_OPTIONS contiene información sobre las opciones que usan los índices vectoriales en un conjunto de datos.

Ejemplos de índices vectoriales

En el siguiente ejemplo, se muestran todos los índices vectoriales activos en las tablas del conjunto de datos my_dataset, ubicado en el proyecto my_project. Incluye los nombres, las declaraciones DDL que se usan para crearlos y el porcentaje de cobertura. Si una tabla base indexada tiene menos de 10 MB, su índice no se propaga. En ese caso, el valor coverage_percentage será 0.

SELECT table_name, index_name, ddl, coverage_percentage
FROM my_project.my_dataset.INFORMATION_SCHEMA.VECTOR_INDEXES
WHERE index_status = 'ACTIVE';

El resultado es similar al siguiente:

+-------------+-------------+-----------------------------------------------------------------------------------------------+---------------------+
| table_name  | index_name  | ddl                                                                                           | coverage_percentage |
+-------------+-------------+-----------------------------------------------------------------------------------------------+---------------------+
| small_table | myindex1    | CREATE VECTOR INDEX `myindex1` ON `my_project.my_dataset.small_table`(embeddings)             | 100                 |
|             |             | OPTIONS (distance_type = 'EUCLIDEAN', index_type = 'IVF', ivf_options = '{"numLists": 3}')    |                     |
+-------------+-------------+-----------------------------------------------------------------------------------------------+---------------------+
| large_table | myindex2    | CREATE VECTOR INDEX `myindex2` ON `my_project.my_dataset.large_table`(vectors)                |  42                 |
|             |             | OPTIONS (distance_type = 'EUCLIDEAN', index_type = 'IVF', ivf_options = '{"numLists": 12}')   |                     |
+-------------+-------------+-----------------------------------------------------------------------------------------------+---------------------+

Ejemplos de columnas de índice vectorial

La siguiente consulta extrae información sobre las columnas que tienen índices vectoriales:

SELECT table_name, index_name, index_column_name, index_field_path
FROM my_project.dataset.INFORMATION_SCHEMA.VECTOR_INDEX_COLUMNS;

El resultado es similar al siguiente:

+------------+------------+-------------------+------------------+
| table_name | index_name | index_column_name | index_field_path |
+------------+------------+-------------------+------------------+
| table1     | indexa     | a                 | a                |
| table2     | indexb     | b                 | b                |
| table3     | indexc     | c                 | c                |
+------------+------------+-------------------+------------------+

Ejemplos de opciones de índices vectoriales

En la siguiente consulta, se extrae información sobre las opciones de índice vectorial:

SELECT table_name, index_name, option_name, option_type, option_value
FROM my_project.dataset.INFORMATION_SCHEMA.VECTOR_INDEX_OPTIONS;

El resultado es similar al siguiente:

+------------+------------+------------------+------------------+--------------------+
| table_name | index_name | option_name      | option_type      | option_value       |
+------------+------------+------------------+------------------+--------------------+
| table1     | indexa     | distance_type    | STRING           | EUCLIDEAN          |
| table1     | indexa     | index_type       | STRING           | IVF                |
| table2     | indexb     | ivf_options      | STRING           | {"num_lists": 100} |
| table2     | indexb     | index_type       | STRING           | IVF                |
+------------+------------+------------------+------------------+--------------------+

Uso del índice vectorial

La información sobre el uso del índice vectorial está disponible en los metadatos del trabajo en el que se ejecutó la consulta de búsqueda vectorial. Puedes ver los metadatos del trabajo mediante la consola de Google Cloud, la herramienta de línea de comandos de bq, la API de BigQuery o las bibliotecas cliente.

Cuando usas la consola de Google Cloud, puedes encontrar información de uso del índice vectorial en los campos Modo de uso del índice vectorial y Motivos por los que no se usó el índice vectorial.

Cuando usas la herramienta de bq o la API de BigQuery, puedes encontrar información de uso del índice vectorial en la sección VectorSearchStatistics del recurso Job.

El modo de uso del índice indica si se usó un índice vectorial cuando se proporciona uno de los siguientes valores:

  • UNUSED: No se usó ningún índice vectorial.
  • PARTIALLY_USED: Algunas funciones VECTOR_SEARCH en la consulta usaron índices vectoriales y otras no.
  • FULLY_USED: Cada función VECTOR_SEARCH en la consulta usó un índice vectorial.

Cuando el valor del modo de uso de índice es UNUSED o PARTIALLY_USED, los motivos por los que no se usó el índice indican por qué no se usaron los índices vectoriales en la consulta.

Por ejemplo, los siguientes resultados que muestra bq show --format=prettyjson -j my_job_id muestran que el índice no se usó porque la opción use_brute_force se especificó en la función VECTOR_SEARCH:

"vectorSearchStatistics": {
  "indexUnusedReasons": [
    {
      "baseTable": {
        "datasetId": "my_dataset",
        "projectId": "my_project",
        "tableId": "my_table"
      },
      "code": "INDEX_SUPPRESSED_BY_FUNCTION_OPTION",
      "message": "No vector index was used for the base table `my_project:my_dataset.my_table` because use_brute_force option has been specified."
    }
  ],
  "indexUsageMode": "UNUSED"
}

Opciones de administración de índices

Para crear índices y hacer que BigQuery los mantenga, tienes dos opciones:

  • Usa el grupo de ranuras compartido predeterminado: Cuando los datos que planeas indexar están por debajo del límite por organización, puedes usar el grupo de ranuras compartido gratuito para la administración de índices.
  • Usa tu propia reserva: para lograr un progreso de indexación más predecible y coherente en tus cargas de trabajo de producción más grandes, puedes usar tus propias reservas para la administración de índices.

Usar ranuras compartidas

Si no configuraste tu proyecto para usar una reserva dedicada para la indexación, la administración de índices se controla en el grupo de ranuras compartido gratuito, sujeto a las siguientes restricciones.

Si agregas datos a una tabla que hace que el tamaño total de las tablas indexadas exceda el límite de tu organización, BigQuery pausa la administración de índices para todas las tablas indexadas. Cuando esto sucede, el campo index_status en la vista INFORMATION_SCHEMA.VECTOR_INDEXES muestra PENDING DISABLEMENT y el índice se pone en cola para su eliminación. Si bien la inhabilitación del índice está pendiente, este se usa en las consultas y se te cobra por el almacenamiento del índice. Después de borrar un índice, el campo index_status muestra el índice como TEMPORARILY DISABLED. En este estado, las consultas no usan el índice y no se te cobra por el almacenamiento de este. En este caso, el código IndexUnusedReason es BASE_TABLE_TOO_LARGE.

Si borras los datos de la tabla y el tamaño total de las tablas indexadas es inferior al límite por organización, la administración de índices se reanuda para todas las tablas indexadas. El campo index_status en la vista INFORMATION_SCHEMA.VECTOR_INDEXES es ACTIVE, las consultas de búsqueda pueden usar el índice de búsqueda y se te cobra por el almacenamiento del índice.

BigQuery no garantiza la capacidad disponible del grupo compartido ni la capacidad de procesamiento de indexación que ves. En aplicaciones de producción, se recomienda usar ranuras dedicadas para el procesamiento de índices.

Usa tu propia reserva

En lugar de usar el grupo de ranuras compartido predeterminado, tienes la opción de designar tu propia reserva para indexar tus tablas. El uso de tu propia reserva garantiza un rendimiento predecible y coherente de los trabajos de administración de índices, como la creación, la actualización y las optimizaciones en segundo plano.

  • No hay límites de tamaño de tabla cuando se ejecuta un trabajo de indexación en tu reserva.
  • El uso de tu propia reserva te brinda flexibilidad en la administración de índices. Si necesitas crear un índice muy grande o aplicar una actualización importante a una tabla indexada, puedes agregar más ranuras a la asignación de forma temporal.

Para indexar las tablas en un proyecto con una reserva designada, crea una reserva en la región en la que se encuentran las tablas. Luego, asigna el proyecto a la reserva con job_type configurado como BACKGROUND:

SQL

Usa la declaración DDL CREATE ASSIGNMENT.

  1. En la consola de Google Cloud, ve a la página de BigQuery.

    Ir a BigQuery

  2. En el editor de consultas, escribe la siguiente oración:

    CREATE ASSIGNMENT
      `ADMIN_PROJECT_ID.region-LOCATION.RESERVATION_NAME.ASSIGNMENT_ID`
    OPTIONS (
      assignee = 'projects/PROJECT_ID',
      job_type = 'BACKGROUND');
    

    Reemplaza lo siguiente:

    • ADMIN_PROJECT_ID por el ID del proyecto de administración que posee el recurso de reserva
    • LOCATION: la ubicación de la reserva
    • RESERVATION_NAME por el nombre de la reserva
    • ASSIGNMENT_ID por el ID de la asignación

      El ID debe ser único para el proyecto y la ubicación, debe empezar y terminar con una letra minúscula o un número y contener solo letras en minúscula, números y guiones.

    • PROJECT_ID: el ID del proyecto que contiene las tablas que se indexarán. Este proyecto está asignado a la reserva.

  3. Haz clic en Ejecutar.

Si deseas obtener información sobre cómo ejecutar consultas, visita Ejecuta una consulta interactiva.

bq

Usa el comando bq mk:

bq mk \
    --project_id=ADMIN_PROJECT_ID \
    --location=LOCATION \
    --reservation_assignment \
    --reservation_id=RESERVATION_NAME \
    --assignee_id=PROJECT_ID \
    --job_type=BACKGROUND \
    --assignee_type=PROJECT

Reemplaza lo siguiente:

  • ADMIN_PROJECT_ID por el ID del proyecto de administración que posee el recurso de reserva
  • LOCATION: la ubicación de la reserva
  • RESERVATION_NAME por el nombre de la reserva
  • PROJECT_ID por el ID del proyecto que se asignará a esta reserva

Ve tus trabajos de indexación

Se crea un trabajo de indexación nuevo cada vez que se crea o se actualiza un índice en una sola tabla. Para ver información sobre el trabajo, consulta las vistas de INFORMATION_SCHEMA.JOBS*. Puedes filtrar los trabajos de indexación si configuras job_type IS NULL AND SEARCH(job_id, '`search_index`') en la cláusula WHERE de tu consulta. En el siguiente ejemplo, se enumeran los cinco trabajos de indexación más recientes en el proyecto my_project:

SELECT *
FROM
 region-us.INFORMATION_SCHEMA.JOBS
WHERE
  project_id  = 'my_project'
  AND job_type IS NULL
  AND SEARCH(job_id, '`search_index`')
ORDER BY
 creation_time DESC
LIMIT 5;

Elige el tamaño de tu reserva

Para elegir la cantidad correcta de ranuras para tu reserva, debes considerar cuándo se ejecutan los trabajos de administración de índices, cuántas ranuras usan y cómo se ve tu uso en el tiempo. BigQuery activa un trabajo de administración de índices en las siguientes situaciones:

  • Debes crear un índice en una tabla.
  • Los datos se modifican en una tabla indexada.
  • El esquema de una tabla cambia y esto afecta qué columnas se indexan.
  • Los datos y metadatos del índice se optimizan o actualizan de forma periódica.

La cantidad de ranuras que necesitas para un trabajo de administración de índices en una tabla depende de los siguientes factores:

  • El tamaño de la tabla
  • La frecuencia de la transferencia de datos a la tabla
  • La tasa de declaraciones DML que se aplican a la tabla
  • El retraso aceptable para compilar y mantener el índice
  • La complejidad del índice, que por lo general se determina según los atributos de los datos, como la cantidad de términos duplicados
Supervisa el uso y el progreso

La mejor manera de evaluar la cantidad de ranuras que necesitas para ejecutar los trabajos de administración de índices de forma eficiente es supervisar el uso de las ranuras y ajustar el tamaño de reserva según corresponda. La siguiente consulta produce el uso diario de las ranuras para los trabajos de administración de índices. Solo se incluyen los últimos 30 días en la región us-west1:

SELECT
  TIMESTAMP_TRUNC(job.creation_time, DAY) AS usage_date,
  -- Aggregate total_slots_ms used for index-management jobs in a day and divide
  -- by the number of milliseconds in a day. This value is most accurate for
  -- days with consistent slot usage.
  SAFE_DIVIDE(SUM(job.total_slot_ms), (1000 * 60 * 60 * 24)) AS average_daily_slot_usage
FROM
  `region-us-west1`.INFORMATION_SCHEMA.JOBS job
WHERE
  project_id = 'my_project'
  AND job_type IS NULL
  AND SEARCH(job_id, '`search_index`')
GROUP BY
  usage_date
ORDER BY
  usage_date DESC
limit 30;

Cuando no hay suficientes ranuras para ejecutar trabajos de administración de índices, un índice puede dejar de sincronizarse con su tabla y los trabajos de indexación pueden fallar. En este caso, BigQuery vuelve a compilar el índice desde cero. Para evitar tener un índice fuera de sincronización, asegúrate de tener suficientes ranuras para admitir actualizaciones de índice desde la transferencia y optimización de datos. Para obtener más información sobre la supervisión del uso de ranuras, consulta los gráficos de recursos de administrador.

Borra un índice vectorial

Cuando ya no necesites un índice vectorial o quieras cambiar qué columna se indexa en una tabla, puedes borrar el índice de esa tabla mediante la sentencia DDL DROP VECTOR INDEX.

Por ejemplo:

DROP VECTOR INDEX my_index ON my_dataset.indexed_table;

Si se borra una tabla indexada, el índice se borra de forma automática.

¿Qué sigue?