En esta página, se describe cómo agregar índices de búsqueda. La búsqueda en el texto completo se ejecuta en las entradas del índice de búsqueda.
Cómo usar los índices de búsqueda
Puedes crear un índice de búsqueda en cualquier columna que desees agregar.
disponibles para las búsquedas en el texto completo. Para crear un índice de búsqueda, usa la declaración DDL CREATE SEARCH INDEX
. Cómo actualizar un índice
usa la declaración DDL ALTER SEARCH INDEX
. Spanner compila y mantiene automáticamente el índice de búsqueda, lo que incluye agregar y actualizar datos en el índice de búsqueda en cuanto cambia en la base de datos.
Particiones de índices de búsqueda
Un índice de búsqueda puede ser particionado o no particionado, según el tipo de consultas que quieras acelerar.
Un ejemplo de cuándo un índice particionado es la mejor opción es cuando la aplicación consulta un buzón de correo electrónico. Cada consulta se restringe a un buzón de correo.
Un ejemplo de cuándo una consulta no particionada es la mejor opción es cuando hay una consulta en todas las categorías de productos de un catálogo de productos.
Casos de uso del índice de la Búsqueda
Además de la búsqueda en el texto completo, los índices de búsqueda de Spanner admiten lo siguiente:
- Búsquedas de subcadenas, que es un tipo de consulta que busca una cadena más corta (la subcadena) dentro de un texto más grande.
- Combinar condiciones en cualquier subconjunto de datos indexados en un solo análisis de índice
Mientras que los índices de búsqueda admiten la indexación de datos no textuales, como números y cadenas de concordancia exacta, el caso de uso más común para un índice de búsqueda es indexar texto en un documento.
Ejemplo de índice de la Búsqueda
Para mostrar las capacidades de los índices de búsqueda, supongamos que hay una tabla que almacena información sobre álbumes de música:
CREATE TABLE Albums (
AlbumId STRING(MAX) NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY(AlbumId);
Spanner tiene varias funciones de tokenización que crean
tokens. Para modificar la tabla anterior y permitir que los usuarios ejecuten una búsqueda en el texto completo para encontrar
títulos de álbumes, usa la función TOKENIZE_FULLTEXT
para crear tokens a partir de
los títulos de los álbumes. Luego, crea una columna que use el tipo de datos TOKENLIST
.
para contener el resultado de la asignación de token de TOKENIZE_FULLTEXT
. Para este ejemplo,
creamos la columna AlbumTitle_Tokens
.
ALTER TABLE Albums
ADD COLUMN AlbumTitle_Tokens TOKENLIST
AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN;
En el siguiente ejemplo, se usa el DDL CREATE SEARCH INDEX
para crear un índice de búsqueda
(AlbumsIndex
) en los tokens de AlbumTitle
(AlbumTitle_Tokens
):
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens);
Después de agregar el índice de búsqueda, usa consultas SQL para encontrar álbumes que coincidan con los criterios de búsqueda. Por ejemplo:
SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
Coherencia de los datos
Cuando se crea un índice, Spanner usa procesos automatizados para reabastecerlos para garantizar la coherencia. Cuando se confirman las operaciones de escritura, los índices se actualizan en la misma transacción. Spanner automáticamente y hace verificaciones de la coherencia de los datos.
Buscar definiciones de esquema de índice
Los índices de búsqueda se definen en una o más columnas TOKENLIST
de una tabla. Búsqueda
los índices tienen los siguientes componentes:
- Tabla base: Es la tabla de Spanner que necesita indexación.
- Columna
TOKENLIST
: Es una colección de columnas que definen los tokens. que necesitan indexación. El orden de estas columnas no es importante.
Por ejemplo, en la siguiente instrucción, la tabla base es Álbumes. Las columnas TOKENLIST
se crean en AlbumTitle
(AlbumTitle_Tokens
) y Rating
(Rating_Tokens
).
CREATE TABLE Albums (
AlbumId STRING(MAX) NOT NULL,
SingerId INT64 NOT NULL,
ReleaseTimestamp INT64 NOT NULL,
AlbumTitle STRING(MAX),
Rating FLOAT64,
AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
Rating_Tokens TOKENLIST AS (TOKENIZE_NUMBER(Rating)) HIDDEN
) PRIMARY KEY(AlbumId);
Usa la siguiente sentencia CREATE SEARCH INDEX
para crear un índice de búsqueda con los tokens de AlbumTitle
y Rating
:
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens, Rating_Tokens)
PARTITION BY SingerId
ORDER BY ReleaseTimestamp DESC
Los índices de búsqueda tienen las siguientes opciones:
- Particiones: Es un grupo opcional de columnas que dividen el índice de búsqueda. Consultar un índice particionado suele ser mucho más eficiente que consultar un índice no particionado. Para obtener más información, consulta Índices de búsqueda de particiones.
- Columna de orden de clasificación: Es una columna
INT64
opcional que establece el orden de recuperación del índice de búsqueda. Para ver más información, consulta Orden de clasificación del índice de búsqueda: - Intercalación: al igual que los índices secundarios, puedes intercalar los índices de búsqueda. Los índices de búsqueda intercalados usan menos recursos para escribir y unir tabla base. Para obtener más información, consulta Búsqueda intercalada. índices.
- Cláusula de opciones: una lista de pares clave-valor que anula el valor predeterminado del índice de búsqueda.
Para obtener más información, consulta la referencia CREATE SEARCH INDEX
.
Diseño interno de los índices de búsqueda
Un elemento importante de la representación interna de los índices de búsqueda es un docid, que sirve como una representación eficiente del almacenamiento de la clave primaria de la tabla base, que puede ser de longitud arbitraria. También es lo que crea el orden del diseño de datos interno según las columnas ORDER BY
proporcionadas por el usuario de la cláusula CREATE SEARCH INDEX
. Se representa como uno o dos números enteros de 64 bits.
Los índices de búsqueda se implementan internamente como una asignación de dos niveles:
- Tokens a docids
- Docids a las claves primarias de la tabla base
Este esquema genera ahorros significativos de almacenamiento, ya que Spanner no necesita almacenar la clave primaria completa de la tabla base para cada par <token, document>
.
Existen dos tipos de índices físicos que implementan los dos niveles de asignación:
- Un índice secundario que asigna claves de partición y un docid a la clave primaria de la tabla base. En el ejemplo de la sección anterior, esto asigna
{SingerId, ReleaseTimestamp, uid}
a{SingerId, AlbumId}
. El índice secundario también almacena todas las columnas especificadas en la cláusulaSTORING
deCREATE SEARCH INDEX
. - Índices de tokens que asignan tokens a docids, similares a los índices invertidos en la literatura de recuperación de información. Spanner mantiene un índice de tokens independiente para cada
TOKENLIST
del índice de búsqueda. Desde el punto de vista lógico, Los índices de tokens mantienen listas de docids para cada token en cada partición. (conocidas en la recuperación de información como listas de publicaciones). Las listas se ordenan según los tokens para una recuperación rápida y, dentro de las listas, se usa docid para el orden. Los índices de tokens individuales son un detalle de implementación que no se expone a través de las APIs de Spanner.
Spanner admite las siguientes cuatro opciones para docid.
Índice de la Búsqueda | Docid | Comportamiento |
---|---|---|
La cláusula ORDER BY se omite para el índice de búsqueda. |
{uid} |
Spanner agrega un valor único oculto (UID) para identificar cada fila. |
ORDER BY column |
{column, uid} |
Spanner agrega la columna de UID como un tiebreaker entre filas con los mismos valores de column dentro de una partición. |
ORDER BY column ... OPTIONS (disable_automatic_uid_column=true) |
{column} |
No se agregó la columna UID. Los valores de column deben ser únicos dentro de una partición. |
ORDER BY column1, column2 ... OPTIONS (disable_automatic_uid_column=true) |
{column1, column2} |
No se agregó la columna UID. La combinación de los valores column1 y column2 debe ser única dentro de una partición. |
Notas de uso:
- La columna UID interna no se expone a través de la API de Spanner.
- En los índices donde no se agrega el UID, las transacciones que agregan una fila con falla si ya existe (partición,orden de clasificación).
Por ejemplo, considera los siguientes datos:
AlbumId | SingerId | ReleaseTimestamp | SongTitle |
---|---|---|---|
a1 | 1 | 997 | Días hermosos |
a2 | 1 | 743 | Ojos hermosos |
Suponiendo que la columna de preordenamiento está en orden ascendente, el contenido del token
índice particionado por SingerId
particiona el contenido del índice de token en el
de la siguiente manera:
SingerId | _token | ReleaseTimestamp | uid |
---|---|---|---|
1 | Hermoso | 743 | uid1 |
1 | Hermoso | 997 | uid2 |
1 | días | 743 | uid1 |
1 | ojos | 997 | uid2 |
Fragmentación del índice de la búsqueda
Cuando Spanner divide una tabla, distribuye los datos del índice de búsqueda para que todos los tokens de una fila de tabla base en particular estén en la misma división. En otras palabras, el índice de búsqueda está fragmentado por documentos. Esta estrategia de fragmentación tiene implicaciones significativas en el rendimiento:
- La cantidad de servidores con los que se comunica cada transacción sigue
constante, sin importar la cantidad de tokens o de tokens indexados
TOKENLIST
columnas. - Las búsquedas que involucran varias expresiones condicionales se ejecutan independientemente en cada división, lo que evita la sobrecarga de rendimiento asociada con una unión distribuida.
Los índices de búsqueda tienen dos modos de distribución:
- Fragmentación uniforme (predeterminado). En el particionamiento uniforme, los datos indexados de cada fila de la tabla base se asignan de forma aleatoria a una división de índice de una partición.
- Fragmentación por orden. En el particionado por orden de clasificación, los datos de cada fila de la tabla base se asignan a una división de índice de una partición según las columnas
ORDER BY
. Por ejemplo, en el caso de un orden descendente, todas las filas con el los valores de orden más altos aparecen en la primera división de índice de una partición y el siguiente grupo más alto de valores de orden en la siguiente división.
Estos modos de fragmentación ofrecen una compensación entre los riesgos de hotspots y el costo de consulta:
- Los índices de búsqueda divididos por orden de clasificación son propensos a generar hotspots cuando el índice se ordena por una marca de tiempo. Para obtener más información, consulta Elige una clave primaria para prevenir hotspots. Por otro lado, cuando la carga de escritura aumenta en un rango de documentos, La fragmentación uniforme garantiza que el aumento se distribuya de manera uniforme entre fragmentos.
- La división basada en cargas estándar crea divisiones adicionales que proporcionan la protección adecuada contra la generación de hotspots. La desventaja del particionamiento uniforme es que puede usar más recursos para algunos tipos de consultas.
El modo de fragmentación de un índice de búsqueda se configura con la cláusula OPTIONS
:
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens, Rating_Tokens)
PARTITION BY SingerId
ORDER BY ReleaseTimestamp DESC
OPTIONS (sort_order_sharding = true);
Cuando se establece sort_order_sharding=false
o no se especifica, el índice de búsqueda se crea con el particionamiento uniforme.
Índices de búsqueda intercalados
Al igual que los índices secundarios, puedes intercalar índices de búsqueda en una tabla superior de la tabla base. El motivo principal para usar la búsqueda intercalada es ubicar los datos de la tabla base con los datos del índice para particiones pequeñas. Esta colocación oportunista tiene las siguientes ventajas:
- Las operaciones de escritura no necesitan realizar una confirmación en dos fases.
- Las uniones inversas del índice de búsqueda con la tabla base no se distribuyen.
Los índices de búsqueda intercalados tienen las siguientes restricciones:
- Solo ordenadas fragmentadas índices se pueden intercalar.
- Los índices de búsqueda solo se pueden intercalar en tablas de nivel superior (no en tablas secundarias).
- Al igual que las tablas intercaladas y los índices secundarios, haz que la clave del
tabla superior, un prefijo de las columnas
PARTITION BY
en la tabla índice de búsqueda.
Cómo definir un índice de búsqueda intercalado
En el siguiente ejemplo, se muestra cómo definir un índice de búsqueda intercalado:
CREATE TABLE Singers (
SingerId INT64 NOT NULL
) PRIMARY KEY(SingerId);
CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId STRING(MAX) NOT NULL,
AlbumTitle STRING(MAX),
AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN
) PRIMARY KEY(SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE;
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens)
PARTITION BY SingerId,
INTERLEAVE IN Singers
OPTIONS (sort_order_sharding = true);
Orden de clasificación del índice de búsqueda
Los requisitos para la definición del orden de clasificación del índice de búsqueda son diferentes de los índices secundarios.
Por ejemplo, considera la siguiente tabla:
CREATE TABLE Albums (
AlbumId STRING(MAX) NOT NULL,
ReleaseTimestamp INT64 NOT NULL,
AlbumName STRING(MAX),
AlbumName_Token TOKENLIST AS (TOKEN(AlbumName)) HIDDEN
) PRIMARY KEY(AlbumId);
La aplicación puede definir un índice secundario para buscar información con el
AlbumName
ordenado por ReleaseTimestamp
:
CREATE INDEX AlbumsSecondaryIndex ON Albums(AlbumName, ReleaseTimestamp DESC);
El índice de búsqueda equivalente se ve de la siguiente manera (usa la tokenización de concordancia exacta, ya que los índices secundarios no admiten búsquedas de texto completo):
CREATE SEARCH INDEX AlbumsSearchIndex
ON Albums(AlbumName_Token)
ORDER BY ReleaseTimestamp DESC;
El orden de clasificación del índice de búsqueda debe cumplir con los siguientes requisitos:
- Usa solo columnas
INT64
para el orden de clasificación de un índice de búsqueda. Las columnas con tamaños arbitrarios utilizan demasiados recursos en el índice de búsqueda porque Spanner necesita almacenar un docid junto a cada token. Específicamente, la columna de orden de clasificación no puede usar el tipoTIMESTAMP
porqueTIMESTAMP
usa una precisión de nanosegundos que no se ajusta a un número entero de 64 bits. Las columnas de orden de clasificación no deben ser
NULL
. Existen dos maneras de cumplir con esto requisito:- Declara la columna de orden de clasificación como
NOT NULL
. - Configura el índice para excluir valores NULL.
- Declara la columna de orden de clasificación como
A menudo, se usa una marca de tiempo para determinar el orden de clasificación. Una práctica común es usar microsegundos desde la época de Unix para esas marcas de tiempo.
Las aplicaciones generalmente recuperan los datos más recientes primero mediante un índice de búsqueda que en orden descendente.
Índices de búsqueda filtrados por NULL
Los índices de búsqueda pueden usar la sintaxis WHERE column IS NOT NULL
para
excluir las filas de la tabla base. Se puede aplicar el filtrado NULL a claves de partición, ordena
columnas de orden y columnas almacenadas. El filtrado NULL en las columnas
de array almacenadas no es
por lo que está permitido.
Ejemplo
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens)
STORING Genre
WHERE Genre IS NOT NULL
La consulta debe especificar la condición de filtrado NULL (Genre IS NOT NULL
para
este ejemplo) en la cláusula WHERE
. De lo contrario, el Optimizador de consultas no podrá
para usar el índice de búsqueda. Para obtener más información, consulta
Requisitos de las consultas en SQL.
Usa el filtrado NULL en una columna generada para excluir filas basadas en cualquiera criterios arbitrarios. Para obtener más información, consulta Crea un índice parcial con un columna generada.
¿Qué sigue?
- Obtén más información sobre la asignación de tokens y los generadores de tokens de Spanner.
- Obtén más información sobre los índices numéricos.
- Obtén más información sobre la partición de índices.