Índices da Pesquisa

Esta página descreve como adicionar índices de pesquisa. A pesquisa de texto completo é executada em entradas no índice de pesquisa.

Como usar índices de pesquisa

É possível criar um índice de pesquisa em qualquer coluna que você queira disponibilizar para pesquisas de texto completo. Para criar um índice de pesquisa, use o Instrução DDL CREATE SEARCH INDEX. Para atualizar um índice use a instrução DDL ALTER SEARCH INDEX. O Spanner cria e mantém o índice de pesquisa automaticamente, incluindo a adição e atualização de dados no índice de pesquisa assim que eles mudam no banco de dados.

Pesquisar partições de índices

Um índice de pesquisa pode ser particionado ou não particionado, dependendo do tipo de consultas que você quer acelerar.

  • Um exemplo de quando um índice particionado é a melhor escolha é quando o aplicativo consulta uma caixa de correio de e-mail. Cada consulta é restrita a um caixa de e-mails.

  • Um exemplo de quando uma consulta não particionada é a melhor escolha é quando há uma consulta em todas as categorias dos produtos em um catálogo.

Casos de uso do índice de pesquisa

Além da pesquisa de texto completo, os índices de pesquisa do Spanner oferecem suporte a:

  • Pesquisas de substring, que é um tipo de consulta que procura uma string mais curta (a substring) em uma um corpo de texto maior.
  • Combinar condições em qualquer subconjunto de dados indexados em uma única verificação de índice.

Embora os índices de pesquisa ofereçam suporte à indexação de dados não textuais, como números e strings de correspondência exata, o caso de uso mais comum para um índice de pesquisa é indexar texto em um documento.

Exemplo de índice de pesquisa

Para mostrar os recursos dos índices de pesquisa, suponha que há uma tabela que armazena informações sobre álbuns de música:

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  AlbumTitle STRING(MAX)
) PRIMARY KEY(AlbumId);

O Spanner tem várias funções de tokenização que criam tokens. Para modificar a tabela anterior e permitir que os usuários façam uma pesquisa de texto completo para encontrar títulos de álbuns, use a função TOKENIZE_FULLTEXT para criar tokens a partir de títulos de álbuns. Em seguida, crie uma coluna que use o tipo de dados TOKENLIST. para manter a saída da tokenização de TOKENIZE_FULLTEXT. Para este exemplo, criamos a coluna AlbumTitle_Tokens.

ALTER TABLE Albums
  ADD COLUMN AlbumTitle_Tokens TOKENLIST
  AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN;

O exemplo a seguir usa a DDL CREATE SEARCH INDEX para criar um índice de pesquisa (AlbumsIndex) nos tokens AlbumTitle (AlbumTitle_Tokens):

CREATE SEARCH INDEX AlbumsIndex
  ON Albums(AlbumTitle_Tokens);

Depois de adicionar o índice de pesquisa, use consultas SQL para encontrar álbuns que correspondam aos critérios de pesquisa. Exemplo:

SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")

Consistência de dados

Quando um índice é criado, o Spanner usa processos automatizados para preencher os dados e garantir a consistência. Quando as gravações são confirmadas, os índices são atualizados na mesma transação. O Spanner executa automaticamente verificações de consistência de dados.

Definições de esquema do índice de pesquisa

Os índices de pesquisa são definidos em uma ou mais colunas TOKENLIST de uma tabela. Pesquisa os índices têm os seguintes componentes:

  • Tabela base: a tabela do Spanner que precisa ser indexada.
  • Coluna TOKENLIST: uma coleção de colunas que define os tokens que precisam ser indexados. A ordem dessas colunas não é importante.

Por exemplo, na instrução a seguir, a tabela base é Álbuns. TOKENLIST colunas são criadas em AlbumTitle (AlbumTitle_Tokens) e 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);

Use a seguinte instrução CREATE SEARCH INDEX para criar um índice de pesquisa usando os tokens de AlbumTitle e Rating:

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens, Rating_Tokens)
PARTITION BY SingerId
ORDER BY ReleaseTimestamp DESC

Os índices de pesquisa têm as seguintes opções:

  • Partições: um grupo opcional de colunas que divide o índice de pesquisa. Consultar um índice particionado geralmente é muito mais eficiente do que consultando um índice não particionado. Para mais informações, consulte Índices de pesquisa de partição.
  • Coluna de ordem de classificação: uma coluna INT64 opcional que estabelece a ordem de recuperação do índice de pesquisa. Para mais informações, consulte Ordem de classificação do índice de pesquisa.
  • Intercalação: assim como os índices secundários, é possível intercalar índices de pesquisa. Os índices de pesquisa intercalados usam menos recursos para gravar e mesclar com o tabela base. Para mais informações, consulte Índices de pesquisa intercalados.
  • Cláusula de opções: uma lista de pares de chave-valor que modifica o padrão. do índice de pesquisa.

Para mais informações, consulte a referência de CREATE SEARCH INDEX.

Layout interno dos índices de pesquisa

Um elemento importante da representação interna dos índices de pesquisa é um docid, que serve como uma representação eficiente de armazenamento da chave primária da tabela base, que pode ser arbitrariamente longa. É também o que cria ordem do layout de dados internos de acordo com o ORDER BY fornecido pelo usuário as colunas do cláusula CREATE SEARCH INDEX. Ele é representado como um ou dois números inteiros de 64 bits.

Os índices de pesquisa são implementados internamente como um mapeamento de dois níveis:

  1. Tokens para docids
  2. Docids para chaves primárias da tabela de base

Esse esquema resulta em uma economia de armazenamento significativa, já que o Spanner não precisa armazenar a chave primária da tabela base completa para cada Par de <token, document>.

Há dois tipos de índices físicos que implementam os dois níveis de mapeamento:

  1. Um índice secundário que mapeia chaves de partição e um docid para a chave primária da tabela de base. No exemplo da seção anterior, {SingerId, ReleaseTimestamp, uid} é mapeado para {SingerId, AlbumId}. O índice secundário também armazena todas as colunas especificadas na cláusula STORING de CREATE SEARCH INDEX.
  2. Índices de token que mapeiam tokens para docids, semelhantes aos índices invertidos na literatura de recuperação de informações. O Spanner mantém um índice de token separado para cada TOKENLIST do índice de pesquisa. Logicamente, os índices de token mantêm listas de docids para cada token em cada partição (conhecidas na recuperação de informações como listas de postagens). As listas são ordenadas por tokens para recuperação rápida e, nas listas, o docid é usado para ordenação. Os índices de token individuais são um detalhe de implementação não exposto pelas APIs do Spanner.

O Spanner oferece suporte às quatro opções a seguir para docid.

Índice da Pesquisa DocID Comportamento
A cláusula ORDER BY é omitida para o índice de pesquisa. {uid} O Spanner adiciona um valor exclusivo oculto (UID, na sigla em inglês) para identificar cada linha.
ORDER BY column {column, uid} O Spanner adiciona a coluna UID como um desempatador entre linhas com os mesmos valores column em uma partição.
ORDER BY column ... OPTIONS (disable_automatic_uid_column=true) {column} A coluna UID não foi adicionada. Os valores column precisam ser exclusivos dentro de uma partição.
ORDER BY column1, column2 ... OPTIONS (disable_automatic_uid_column=true) {column1, column2} A coluna UID não é adicionada. A combinação de valores column1 e column2 precisa ser exclusiva em uma partição.

Observações sobre o uso:

  • A coluna interna do UID não é exposta pelo Spanner API.
  • Em índices em que o UID não é adicionado, as transações que adicionam uma linha com uma já existente (partição, ordem de classificação) falham.

Por exemplo, considere os seguintes dados:

AlbumId SingerId ReleaseTimestamp SongTitle
a1 1 997 Dias lindos
a2 1 743 Belos olhos

Supondo que a coluna de pré-classificação esteja em ordem crescente, o conteúdo do índice de token particionado por SingerId particiona o conteúdo do índice de token da seguinte maneira:

SingerId _token ReleaseTimestamp uid
1 lindas 743 uid1
1 linda 997 uid2
1 dias 743 uid1
1 nos olhos 997 uid2

Fragmentação do índice de pesquisa

Quando o Spanner divide uma tabela, ele distribui dados de índice de pesquisa para que todos os tokens em uma linha de tabela base específica estejam na mesma divisão. Em outras palavras, o índice de pesquisa é fragmentado por documento. Essa estratégia de fragmentação tem implicações significativas no desempenho:

  1. O número de servidores com que cada transação se comunica permanece constante, independentemente do número de tokens ou de colunas TOKENLIST indexadas.
  2. As consultas de pesquisa que envolvem várias expressões condicionais são executadas de forma independente em cada divisão, evitando a sobrecarga de desempenho associada a uma mesclagem distribuída.

Os índices de pesquisa têm dois modos de distribuição:

  • Fragmentação uniforme (padrão). Na fragmentação uniforme, os dados indexados para cada linha da tabela base é atribuída aleatoriamente a uma divisão de índice de uma partição.
  • Fragmentação de ordem de classificação. Na fragmentação de ordem de classificação, os dados de cada linha da tabela base é atribuído a uma divisão de índice de uma partição com base no ORDER BY colunas. Por exemplo, no caso de uma ordem de classificação decrescente, todas as linhas com o valores de ordem de classificação mais altos aparecem na primeira divisão de índice de uma partição, e o próximo grupo mais alto de valores de ordem de classificação na próxima divisão.

Esses modos de fragmentação apresentam um equilíbrio entre os riscos de uso excessivo do ponto de acesso e as Custo da consulta:

  • Os índices de pesquisa divididos por ordem de classificação tendem a ter pontos de acesso quando o índice é classificado por um carimbo de data/hora. Para mais informações, consulte Escolher uma chave primária para evitar pontos de acesso. Por outro lado, quando a carga de gravação aumenta em uma série de documentos, o sharding uniforme garante que o aumento seja distribuído uniformemente entre os fragmentos.
  • A divisão padrão baseada em carga cria divisões adicionais que oferecem proteção adequada contra hotspots. A desvantagem do sharding uniforme é que ele pode usar mais recursos para alguns tipos de consultas.

O modo de fragmentação de um índice de pesquisa é configurado usando a 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);

Quando sort_order_sharding=false é definido ou deixado sem especificação, o índice de pesquisa é criado usando o sharding uniforme.

Índices de pesquisa intercalados

Assim como os índices secundários, é possível intercalar índices de pesquisa em uma tabela mãe da tabela base. O principal motivo para usar a pesquisa intercalada índices é colocar dados da tabela base com dados de índice para partições pequenas. Essa colocalização oportunista tem as seguintes vantagens:

  • As gravações não precisam fazer uma confirmação de duas fases.
  • As junções de retorno do índice de pesquisa com a tabela base não são distribuídas.

Os índices de pesquisa intercalados têm as seguintes restrições:

  1. Somente índices fragmentados de ordem de classificação podem ser intercalados.
  2. Os índices de pesquisa só podem ser intercalados em tabelas de nível superior (não em tabelas filhas) tabelas).
  3. Como tabelas intercaladas e índices secundários, torne a chave do um prefixo das colunas PARTITION BY na tabela mãe índice de pesquisa.

Definir um índice de pesquisa intercalado

O exemplo a seguir demonstra como definir um índice de pesquisa 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);

Pesquisar ordem de classificação do índice

Os requisitos para a definição da ordem de classificação do índice de pesquisa são diferentes dos índices secundários.

Por exemplo, considere a seguinte tabela:

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);

O aplicativo pode definir um índice secundário para procurar informações usando o AlbumName classificado por ReleaseTimestamp:

CREATE INDEX AlbumsSecondaryIndex ON Albums(AlbumName, ReleaseTimestamp DESC);

O índice de pesquisa equivalente tem a seguinte aparência (usa correspondência exata tokenização, já que os índices secundários não são compatíveis com pesquisas de texto completo):

CREATE SEARCH INDEX AlbumsSearchIndex
ON Albums(AlbumName_Token)
ORDER BY ReleaseTimestamp DESC;

A ordem de classificação do índice de pesquisa precisa estar em conformidade com os seguintes requisitos:

  1. Use apenas colunas INT64 para a classificação da ordem de um índice de pesquisa. Colunas com tamanhos arbitrários usam muitos recursos no índice de pesquisa porque o Spanner precisa armazenar um docid ao lado de cada token. Especificamente, a classificação A coluna de pedidos não pode usar o tipo TIMESTAMP porque TIMESTAMP usa precisão de nanossegundos, que não cabe em um número inteiro de 64 bits.
  2. As colunas de ordem de classificação não podem ser NULL. Há duas maneiras de fazer isso requisito:

    1. Declare a coluna de ordem de classificação como NOT NULL.
    2. Configure o índice para excluir valores NULL.

Um carimbo de data/hora é frequentemente usado para determinar a ordem de classificação. Uma prática comum é usar microssegundos desde a época do Unix para esses carimbos de data/hora.

Os aplicativos geralmente recuperam os dados mais recentes primeiro usando um índice de pesquisa em ordem decrescente.

Índices de pesquisa filtrados por NULL

Os índices de pesquisa podem usar a sintaxe WHERE column IS NOT NULL para excluir linhas da tabela base. A filtragem de NULL pode ser aplicada a chaves de particionamento, classificar ordenar colunas e colunas armazenadas. A filtragem NULL em colunas de matriz armazenadas não é permitida.

Exemplo

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens)
STORING Genre
WHERE Genre IS NOT NULL

A consulta precisa especificar a condição de filtragem NULL (Genre IS NOT NULL para neste exemplo) na cláusula WHERE. Caso contrário, o otimizador de consultas não poderá usar o índice de pesquisa. Para mais informações, consulte Requisitos da consulta SQL.

Use a filtragem NULL em uma coluna gerada para excluir linhas com base em qualquer critério arbitrário. Para mais informações, consulte Criar um índice parcial usando uma coluna gerada.

A seguir