Rechercher dans les index

Cette page explique comment ajouter des index de recherche aux tables. Les index de recherche sont nécessaires pour créer la structure d'une table pour autoriser la recherche en texte intégral Spanner.

Index de recherche en texte intégral

Vous pouvez créer un index de recherche sur toutes les colonnes à inclure pour les recherches en texte intégral. Pour créer un index de recherche, utilisez la méthode CREATE SEARCH INDEX Une instruction LDD ou mise à jour à l'aide de la Instruction LDD ALTER SEARCH INDEX. Spanner crée et maintient automatiquement l'index de recherche, y compris en ajoutant et en en mettant à jour les données dans l'index de recherche dès qu'elles changent dans la base de données.

Un index de recherche peut être partitionné ou non partitionné, selon le type de requêtes à accélérer.

  • Par exemple, un index partitionné constitue le meilleur choix lorsque le interroge une boîte aux lettres électronique. Chaque requête est limitée à un champ d'application boîte aux lettres.

  • Par exemple, une requête non partitionnée est le meilleur choix : une requête concerne toutes les catégories de produits d'un catalogue de produits.

Outre la recherche en texte intégral, les index de recherche Spanner sont suivantes:

  • les recherches de sous-chaîne, c'est-à-dire un type de requête qui recherche une requête (la sous-chaîne) dans un corps de texte plus grand.
  • Accélération des requêtes contenant des expressions numériques et des correspondances exactes.
  • Combiner des conditions sur n'importe quel sous-ensemble de données indexées en une seule analyse d'index

Les index de recherche prennent en charge l'utilisation de données non textuelles, telles que des chiffres et à mots clés exacts, le cas d'utilisation le plus courant d'un index de recherche consiste à indexer du texte dans un document.

Pour montrer les fonctionnalités des index de recherche, supposons qu'un tableau stocke des informations sur les albums musicaux:

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

Spanner comporte plusieurs fonctions de tokenisation qui créent de jetons. Pour modifier le tableau précédent afin de permettre aux utilisateurs d'effectuer une recherche en texte intégral pour trouver d'albums, utilisez la fonction TOKENIZE_FULLTEXT pour créer des jetons à partir de des titres d'albums. Créez ensuite une colonne qui utilise le type de données TOKENLIST. pour contenir la sortie de tokenisation de TOKENIZE_FULLTEXT. Pour cet exemple, nous créons la colonne AlbumTitle_Tokens.

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

L'instruction suivante utilise le LDD CREATE SEARCH INDEX pour créer un index de recherche (AlbumsIndex) sur les AlbumTitle jetons (AlbumTitle_Tokens):

CREATE SEARCH INDEX AlbumsIndex
  ON Albums(AlbumTitle_Tokens);

Après avoir ajouté l'index de recherche, utilisez des requêtes SQL pour trouver les albums correspondant les critères de recherche. Exemple :

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

Cohérence des données

Lorsqu'un index est créé, Spanner utilise des processus automatisés pour : pour remplir les données pour garantir la cohérence. Lorsque les écritures sont validées, les index sont mis à jour dans la même transaction. Spanner automatiquement effectue des vérifications de cohérence des données.

Définitions de schémas d'index de recherche

Les index de recherche sont définis sur une ou plusieurs colonnes TOKENLIST d'une table. Réseau de Recherche Les index se composent des éléments suivants:

  • Table de base: table Spanner à indexer.
  • Colonne TOKENLIST: ensemble de colonnes définissant les jetons devant être indexées. L'ordre de ces colonnes n'est pas important.

Par exemple, dans l'instruction suivante, la table de base est Albums. TOKENLIST colonnes créées sur AlbumTitle (AlbumTitle_Tokens) et 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);

Utilisez l'instruction CREATE SEARCH INDEX suivante pour créer un index de recherche à l'aide des jetons pour AlbumTitle et Rating:

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

Les index de recherche disposent des options suivantes:

  • Partitions : groupe facultatif de colonnes qui divise l'index de recherche. Interroger un index partitionné est souvent beaucoup plus efficace en interrogeant un index non partitionné. Pour en savoir plus, consultez Partitionner les index de recherche
  • Colonne d'ordre de tri: colonne facultative INT64 qui détermine l'ordre de récupération à partir de l'index de recherche. Pour plus pour en savoir plus, consultez Ordre de tri de l'index de recherche :
  • Entrelacement: comme pour les index secondaires, vous pouvez entrelacer les index de recherche. Les index de recherche intercalés utilisent moins de ressources pour écrire et joindre la table de base. Pour en savoir plus, consultez la section Recherche entrelacée index.
  • Clause Options: liste de paires clé/valeur qui remplace la valeur par défaut de l'index de recherche.

Pour en savoir plus, consultez la référence CREATE SEARCH INDEX.

Mise en page interne des index de recherche

Un élément important de la représentation interne des index de recherche docid, qui sert de représentation économe en stockage de la clé primaire ; de la table de base, qui peut être arbitrairement longue. C'est également ce qui crée l'ordre de la mise en page des données internes en fonction des colonnes ORDER BY fournies par l'utilisateur de la clause CREATE SEARCH INDEX. Il est représenté comme un ou deux entiers de 64 bits.

Les index de recherche sont implémentés en interne sous la forme d'un mappage à deux niveaux:

  1. Jetons vers des docids
  2. Docid pour baser les clés primaires de table

Ce schéma permet de réaliser d'importantes économies de stockage, car Spanner n'a pas besoin de stocker l'intégralité de la clé primaire de la table de base pour chaque Paire <token, document>.

Il existe deux types d'index physiques qui implémentent les deux niveaux de mappage:

  1. Un indice secondaire qui mappe les clés de partition et un docid sur la clé primaire de la table de base. Dans l'exemple de la section précédente, {SingerId, ReleaseTimestamp, uid} est mappé sur {SingerId, AlbumId}. L'index secondaire aussi stocke toutes les colonnes spécifiées dans la clause STORING de CREATE SEARCH INDEX.
  2. Les index de jetons mappent les jetons à des docids, de la même manière que les index inversés dans sur la recherche d'informations. Spanner gère un index de jetons distinct pour chaque TOKENLIST de l'index de recherche. Logiquement, Les index de jetons conservent des listes de docids pour chaque jeton dans chaque partition. (connues dans la récupération d'informations sous le nom de listes de publications). Les listes sont ordonnées par jetons pour une récupération rapide. Dans les listes, Docid est utilisé pour le tri. Les index de jetons individuels sont des détails d'implémentation qui ne sont pas exposés via API Spanner.

Spanner est compatible avec les quatre options de docid suivantes.

index de recherche Docid Comportement
La clause ORDER BY est omise pour l'index de recherche { uid } Spanner ajoute une valeur unique cachée (UID) pour identifier chaque ligne.
ORDER BY column { column, uid } Spanner ajoute la colonne UID pour départager les lignes ayant les mêmes valeurs column au sein d'une partition.
ORDER BY column ... OPTIONS (disable_automatic_uid_column=true) { column } La colonne UID n'est pas ajoutée. Les valeurs column doivent être uniques au sein d'une partition.
ORDER BY column1, column2 ... OPTIONS (disable_automatic_uid_column=true) { column1, column2 } La colonne UID n'est pas ajoutée. La combinaison des valeurs column1 et column2 doit être unique dans une partition.

Consignes d'utilisation :

  • La colonne "UID interne" n'est pas exposée via Spanner. API.
  • Dans les index où l'UID n'est pas ajouté, toute transaction échouera si elle produit deux lignes ayant la même valeur (partition,ordre de tri).

Prenons l'exemple des données suivantes:

ID de l'album SingerId ReleaseTimestamp Titre de l'album
a1 1 997 Grand chien
a2 1 743 Grand félin

En supposant que la colonne de prétri est dans l’ordre croissant, le contenu du jeton l'index partitionné par SingerId partitionne le contenu de l'index de jetons dans comme suit:

SingerId _token ReleaseTimestamp uid
1 grand 743 uid1
1 grand 997 uid2
1 cat 743 uid1
1 chien 997 uid2

Segmentation de l'index de recherche

Lorsque Spanner divise une table Distribue les données d'index de recherche de sorte que tous les jetons d'une ligne de table de base particulière dans la même division. En d'autres termes, l'index de recherche est segmenté en documents. Cette stratégie de segmentation a un impact important sur les performances:

  1. Le nombre de serveurs avec lesquels chaque transaction communique reste constante, quel que soit le nombre de jetons ou le nombre TOKENLIST colonnes.
  2. Les requêtes de recherche impliquant plusieurs expressions conditionnelles sont exécutées indépendamment sur chaque fractionnement, ce qui évite les coûts liés aux performances associés à une jointure distribuée.

Les index de recherche ont deux modes de distribution:

  • Segmentation uniforme (par défaut). Dans le cas de la segmentation uniforme, les données indexées pour chaque ligne de la table de base est attribuée de manière aléatoire à une division d'index d'une partition.
  • Segmentation par ordre de tri. Avec la segmentation par ordre de tri, les données de chaque ligne de la table de base est affecté à une division d'index d'une partition en fonction de l'ORDER BY. colonnes. Par exemple, dans le cas d'un ordre de tri décroissant, toutes les lignes dont les valeurs d'ordre de tri sont les plus élevées apparaissent dans la première division d'index d'une partition, et le groupe de valeurs d'ordre de tri le plus élevé suivant dans la division suivante.

Ces modes de segmentation s'accompagnent d'un compromis entre les risques de hotspotting et Coût de la requête:

  • Les index de recherche segmentés par ordre de tri sont sujets au hotspotting lorsque l'index est triées par horodatage. Pour en savoir plus, consultez la section Choisir une clé primaire pour empêcher points d'accès. D'un autre côté, lorsque la charge d'écriture augmente sur une plage de documents, la segmentation uniforme garantit que l'augmentation est répartie de façon uniforme entre segments.
  • La division standard basée sur la charge crée des divisions supplémentaires qui fournissent une protection efficace contre le hotspotting. L'inconvénient de la segmentation uniforme est qu'il peut utiliser plus de ressources pour certains types de requêtes.

Le mode de segmentation d'un index de recherche est configuré à l'aide de la clause OPTIONS:

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens, Rating_Tokens)
PARTITION BY SingerId
ORDER BY ReleaseTimestamp DESC
OPTIONS (sort_order_sharding = true);

Lorsque sort_order_sharding=false est défini ou n'est pas spécifié, l'index de recherche est créés à l'aide de la segmentation uniforme.

Index de recherche entrelacés

Comme pour les index secondaires, vous pouvez entrelacer les index de recherche dans une table parente de la table de base. Principale raison d'utiliser la recherche entrelacée est de colocaliser les données de la table de base avec les données d'index pour les petites partitions. Cette colocation opportuniste présente les avantages suivants:

  • Les écritures n'ont pas besoin d'effectuer un commit en deux phases.
  • Les jointures arrière de l'index de recherche avec la table de base ne sont pas distribuées.

Les index de recherche intercalés sont soumis aux restrictions suivantes :

  1. Seuls les index ordonnés par sharding peuvent être entrelacés.
  2. Les index de recherche ne peuvent être entrelacés que dans les tables de niveau supérieur (et non dans les tables enfants) tableaux).
  3. Comme pour les tables entrelacées et les index secondaires, définissez la clé du la table parente un préfixe des colonnes PARTITION BY dans la table entrelacée ; dans l'index de recherche Google.

L'exemple suivant montre comment définir un index de recherche entrelacé:

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

Index de recherche filtrés par la valeur NULL

Les index de recherche peuvent utiliser la syntaxe WHERE column IS NOT NULL pour : exclure les lignes de la table de base. Le filtrage NULL peut s'appliquer aux clés de partitionnement, trier des colonnes d'ordre et des colonnes stockées. Le filtrage NULL sur les colonnes d'un tableau stocké n'est pas autorisé.

Exemple

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

La requête doit spécifier la condition de filtrage NULL (Genre IS NOT NULL pour cet exemple) dans la clause WHERE. Sinon, l'optimiseur de requêtes ne pourra pas pour utiliser l'index de recherche. Pour en savoir plus, consultez Exigences concernant les requêtes SQL

Utilisez le filtrage NULL sur une colonne générée pour exclure des lignes en fonction de critères arbitraires. Pour en savoir plus, consultez la section Créer un index partiel à l'aide d'une propriété générée.

Étape suivante