Questa pagina descrive come aggiungere gli indici di ricerca. La La ricerca a testo intero viene eseguita sulle voci nell'indice di ricerca.
Come utilizzare gli indici di ricerca
Puoi creare un indice di ricerca su qualsiasi colonna che vuoi creare
disponibile per le ricerche di testo completo. Per creare un indice di ricerca, utilizza il metodo
CREATE SEARCH INDEX
Istruzione DDL. Per aggiornare un indice, utilizza l'istruzione DDL ALTER SEARCH INDEX
. Spanner
crea e gestisce automaticamente l'indice di ricerca, inclusa l'aggiunta e
l'aggiornamento dei dati nell'indice di ricerca non appena vengono modificati nel database.
Partizioni degli indici di ricerca
Un indice di ricerca può essere partizionato o non partizionato, a seconda del tipo di query che vuoi accelerare.
Un esempio di quando un indice partizionato è la scelta migliore è quando l'applicazione esegue query su una casella di posta email. Ogni query è limitata a una cassetta postale specifica.
Un esempio di quando una query non partizionata è la scelta migliore è quando viene eseguita una query su tutte le categorie di prodotti in un catalogo dei prodotti.
Casi d'uso dell'indice della Ricerca
Oltre alla ricerca a testo intero, gli indici di ricerca di Spanner supportano quanto segue:
- Ricerche di sottostringhe, che è un tipo di query che cerca una stringa più breve (la sottostringa) all'interno di un corpo del testo più grande.
- Combinazione di condizioni su qualsiasi sottoinsieme di dati indicizzati in un'unica scansione dell'indice.
Mentre gli indici di ricerca supportano l'indicizzazione di dati non testuali, come numeri e stringhe con corrispondenza esatta, il caso d'uso più comune per un indice di ricerca è indicizzare testo in un documento.
Esempio di indice della Ricerca
Per mostrare le funzionalità degli indici di ricerca, supponiamo che esista una tabella che immagazzina informazioni sugli album musicali:
CREATE TABLE Albums (
AlbumId STRING(MAX) NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY(AlbumId);
Spanner ha diverse funzioni di tokenizzazione che creano
di token. Per modificare la tabella precedente in modo che gli utenti possano eseguire una ricerca full-text per trovare i titoli degli album, utilizza la funzione TOKENIZE_FULLTEXT
per creare token dai titoli degli album. Poi crea una colonna che utilizzi il tipo di dati TOKENLIST
per contenere l'output di tokenizzazione da TOKENIZE_FULLTEXT
. Per questo esempio,
abbiamo creato la colonna AlbumTitle_Tokens
.
ALTER TABLE Albums
ADD COLUMN AlbumTitle_Tokens TOKENLIST
AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN;
Il seguente esempio utilizza il DDL CREATE SEARCH INDEX
per creare un indice di ricerca
(AlbumsIndex
) sui token AlbumTitle
(AlbumTitle_Tokens
):
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens);
Dopo aver aggiunto l'indice di ricerca, utilizza le query SQL per trovare gli album corrispondenti ai criteri di ricerca. Ad esempio:
SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
Coerenza dei dati
Quando viene creato un indice, Spanner utilizza processi automatici per eseguire il backfill dei dati al fine di garantire la coerenza. Quando viene eseguito il commit delle scritture, gli indici vengono aggiornati nella stessa transazione. Spanner esegue automaticamente esegue controlli di coerenza dei dati.
Cerca definizioni dello schema di indice
Gli indici di ricerca sono definiti su una o più colonne TOKENLIST
di una tabella. Gli indici di ricerca hanno i seguenti componenti:
- Tabella di base: la tabella Spanner che deve essere indicizzata.
- Colonna
TOKENLIST
: una raccolta di colonne che definiscono i token che richiedono l'indicizzazione. L'ordine di queste colonne non è importante.
Ad esempio, nell'istruzione seguente, la tabella di base è Album. TOKENLIST
le colonne vengono create in 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);
Utilizza la seguente dichiarazione CREATE SEARCH INDEX
per creare un indice di ricerca
utilizzando i token per AlbumTitle
e Rating
:
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens, Rating_Tokens)
PARTITION BY SingerId
ORDER BY ReleaseTimestamp DESC
Gli indici di ricerca hanno le seguenti opzioni:
- Partizioni: un gruppo facoltativo di colonne che suddivide l'indice di ricerca. L'esecuzione di query su un indice partizionato è spesso molto più efficiente eseguire una query su un indice non partizionato. Per ulteriori informazioni, consulta Indici di ricerca delle partizioni.
- Colonna di ordinamento: una colonna
INT64
facoltativa che stabilisce l'ordine di recupero dall'indice di ricerca. Per maggiori informazioni le informazioni, vedi Ordinamento dell'indice di ricerca. - Interfoliazione: come accade per gli indici secondari, puoi interfoliare gli indici di ricerca. Gli indici di ricerca con interleaving utilizzano meno risorse per la scrittura e l'unione con il metodo e la tabella di base. Per ulteriori informazioni, consulta la sezione Indici di ricerca interlacciati.
- Clausola opzioni: un elenco di coppie chiave-valore che eseguono l'override di quella predefinita. impostazioni dell'indice di ricerca.
Per ulteriori informazioni, consulta la documentazione di riferimento di CREATE SEARCH INDEX
.
Layout interno degli indici di ricerca
Un elemento importante della rappresentazione interna degli indici di ricerca è un docid, che funge da rappresentazione efficiente in termini di spazio di archiviazione della chiave primaria della tabella di base e può essere di lunghezza arbitraria. Inoltre, crea l'ordine per il layout dei dati interni in base alle colonne ORDER BY
fornite dall'utente della clausola CREATE SEARCH INDEX
. Sono rappresentati come uno o due numeri interi a 64 bit.
Gli indici di ricerca sono implementati internamente come una mappatura a due livelli:
- Token a docid
- Docid alle chiavi primarie della tabella di base
Questo schema comporta risparmi significativi di spazio di archiviazione come Spanner
non deve archiviare la chiave primaria completa
della tabella di base per ogni
Coppia di <token, document>
.
Esistono due tipi di indici fisici che implementano i due livelli di mappatura:
- Un indice secondario che mappa le chiavi di partizione e un docid alla tabella di base.
e la chiave primaria. Nell'esempio nella sezione precedente, viene mappato
{SingerId, ReleaseTimestamp, uid}
a{AlbumId}
. Anche l'indice secondario Archivia tutte le colonne specificate nella clausolaSTORING
diCREATE SEARCH INDEX
. - Indici di token che mappano i token ai documenti, simili agli indici invertiti nelle
letteratura sul recupero delle informazioni. Spanner gestisce un
indice dei token separato per ogni
TOKENLIST
dell'indice di ricerca. A livello logico, Gli indici di token mantengono elenchi di documenti per ogni token all'interno di ciascuna partizione (noto nel recupero delle informazioni come elenchi di post). Gli elenchi vengono ordinati da token per un recupero rapido, e all'interno degli elenchi, docid viene utilizzato per ordinare. Gli indici dei singoli token sono un dettaglio di implementazione non visibile le API Spanner.
Spanner supporta le seguenti quattro opzioni per docid.
Indice della Ricerca | Docid | Comportamento |
---|---|---|
La clausola ORDER BY viene omessa per l'indice di ricerca |
{uid} |
Spanner aggiunge un valore univoco nascosto (UID) per identificare ogni riga. |
ORDER BY column |
{column, uid} |
Spanner aggiunge la colonna UID come criterio di parità tra le righe con gli stessi valori column all'interno di una partizione. |
ORDER BY column ... OPTIONS (disable_automatic_uid_column=true) |
{column} |
La colonna dell'UID non è stata aggiunta. I valori column devono essere univoci all'interno di una partizione. |
ORDER BY column1, column2 ... OPTIONS (disable_automatic_uid_column=true) |
{column1, column2} |
La colonna UID non è stata aggiunta. La combinazione di valori column1 e column2 deve essere univoca all'interno di una partizione. |
Note sull'utilizzo:
- La colonna UID interna non è esposta tramite l'API Spanner.
- Negli indici in cui l'UID non viene aggiunto, le transazioni che aggiungono una riga con un valore già esistente (partizione, ordine di ordinamento) non vanno a buon fine.
Ad esempio, considera i seguenti dati:
AlbumId | SingerId | ReleaseTimestamp | SongTitle |
---|---|---|---|
a1 | 1 | 997 | Giornate meravigliose |
a2 | 1 | 743 | Occhi splendidi |
Supponendo che la colonna di preordinamento sia in ordine crescente, il contenuto del token
partizionato per SingerId
partiziona il contenuto dell'indice del token nella
nel seguente modo:
SingerId | _token | ReleaseTimestamp | uid |
---|---|---|---|
1 | bello | 743 | uid1 |
1 | bello | 997 | uid2 |
1 | giorni | 743 | uid1 |
1 | occhi | 997 | uid2 |
Sharding dell'indice della Ricerca
Quando Spanner suddivide una tabella, distribuisce i dati dell'indice di ricerca in modo che tutti i token di una determinata riga della tabella di base si trovino nella stessa suddivisione. In altre parole, l'indice di ricerca è segmentato sui documenti. Questa strategia di suddivisione ha implicazioni significative sul rendimento:
- Il numero di server con cui comunica ogni transazione rimane
costante, indipendentemente dal numero di token o dal numero di
TOKENLIST
colonne. - Le query di ricerca che coinvolgono più espressioni condizionali vengono eseguite in modo indipendente su ogni suddivisione, evitando il sovraccarico delle prestazioni associato a un join distribuito.
Gli indici di ricerca hanno due modalità di distribuzione:
- Spartizionamento uniforme (impostazione predefinita). Nello sharding uniforme, i dati indicizzati per ogni la riga della tabella di base viene assegnata in modo casuale a una suddivisione dell'indice di una partizione.
- sharding dell'ordinamento. Nel partitioning in base all'ordinamento, i dati di ogni riga della tabella di base vengono assegnati a una suddivisione dell'indice di una partizione in base alle colonne
ORDER BY
. Ad esempio, nel caso di un ordinamento decrescente, tutte le righe con i valori di ordinamento più alti appaiono sulla prima suddivisione dell'indice di una partizione e il gruppo successivo di valori di ordinamento nella suddivisione successiva.
Queste modalità di sharding prevedono un compromesso tra i rischi di hotspot e costo delle query:
- Gli indici di ricerca suddivisi per ordinamento sono soggetti a hotspot quando l'indice è ordinato in base a un timestamp. Per ulteriori informazioni, consulta Scegliere una chiave primaria per prevenire gli hotspot. D'altra parte, quando il carico di scrittura aumenta in un intervallo di documenti, il partitioning uniforme garantisce che l'aumento venga distribuito uniformemente tra gli shard.
- La suddivisione standard basata sul carico crea ulteriori suddivisioni che forniscono un'adeguata protezione contro l'hotspotting. Lo svantaggio dello sharding uniforme è che può utilizzare più risorse per alcuni tipi di query.
La modalità dello sharding di un indice di ricerca viene configurata utilizzando la clausola OPTIONS
:
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens, Rating_Tokens)
PARTITION BY SingerId
ORDER BY ReleaseTimestamp DESC
OPTIONS (sort_order_sharding = true);
Se sort_order_sharding=false
è impostato o non specificato, l'indice di ricerca viene creato utilizzando la suddivisione uniforme.
Indici di ricerca interlacciati
Come per gli indici secondari, puoi interfoliare gli indici di ricerca in una tabella principale della tabella di base. Il motivo principale per utilizzare la ricerca con interleaving consiste nel posizionare i dati delle tabelle di base con i dati degli indici per partizioni di piccole dimensioni. Questa co-locazione opportunistica presenta i seguenti vantaggi:
- Le scritture non richiedono un commit in due fasi.
- I back-join dell'indice di ricerca con la tabella di base non vengono distribuiti.
Gli indici di ricerca interlacciati presentano le seguenti limitazioni:
- Solo gli indici con suddivisione in base all'ordine di ordinamento possono essere interlacciati.
- Gli indici di ricerca possono essere interlacciati solo nelle tabelle di primo livello (non nelle tabelle figlie).
- Come per le tabelle interlacciate e gli indici secondari, imposta la chiave della tabella principale come prefisso delle colonne
PARTITION BY
nell'indice di ricerca interlacciato.
Definisci un indice di ricerca con interleaving
L'esempio seguente mostra come definire un indice di ricerca interlacciato:
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);
Ordinamento dell'indice della Ricerca
I requisiti per la definizione dell'ordinamento dell'indice di ricerca sono diversi da quelli degli indici secondari.
Ad esempio, considera la seguente tabella:
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);
L'applicazione potrebbe definire un indice secondario per cercare informazioni utilizzando AlbumName
ordinato per ReleaseTimestamp
:
CREATE INDEX AlbumsSecondaryIndex ON Albums(AlbumName, ReleaseTimestamp DESC);
L'indice di ricerca equivalente è il seguente (utilizza la tokenizzazione con corrispondenza esatta, poiché gli indici secondari non supportano le ricerche full-text):
CREATE SEARCH INDEX AlbumsSearchIndex
ON Albums(AlbumName_Token)
ORDER BY ReleaseTimestamp DESC;
L'ordinamento dell'indice di ricerca deve essere conforme ai seguenti requisiti:
- Utilizza solo
INT64
colonne per l'ordinamento dell'ordine di un indice di ricerca. Le colonne con dimensioni arbitrarie ne utilizzano troppe risorse nell'indice di ricerca perché Spanner deve archiviare un docid accanto a ogni token. In particolare, l'ordinamento non è possibile usare il tipoTIMESTAMP
perchéTIMESTAMP
utilizza di precisione in nanosecondi, che non rientra in un numero intero a 64 bit. Le colonne dell'ordinamento non devono essere
NULL
. Esistono due modi per soddisfare questo requisito:- Dichiara la colonna dell'ordinamento come
NOT NULL
. - Configura l'indice per escludere i valori NULL.
- Dichiara la colonna dell'ordinamento come
Spesso viene utilizzato un timestamp per determinare l'ordinamento. Una pratica comune è usano microsecondi dall'epoca di Unix per questi timestamp.
In genere le applicazioni recuperano per primi i dati più recenti utilizzando un indice di ricerca in ordine decrescente.
Indici di ricerca con filtri NULL
Gli indici di ricerca possono utilizzare la sintassi WHERE column IS NOT NULL
per
escludi le righe della tabella di base. Il filtro NULL può essere applicato alle chiavi di partizionamento,
e colonne archiviate. Il filtro NULL sulle colonne di array archiviate non è consentito.
Esempio
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens)
STORING Genre
WHERE Genre IS NOT NULL
La query deve specificare la condizione di filtro NULL (Genre IS NOT NULL
per questo esempio) nella clausola WHERE
. In caso contrario, Query Optimizer non è in grado
per utilizzare l'indice di ricerca. Per ulteriori informazioni, consulta i requisiti delle query SQL.
Usa il filtro NULL su una colonna generata per escludere le righe in base a qualsiasi in base a criteri arbitrari. Per ulteriori informazioni, consulta Creare un indice parziale utilizzando una colonna generata.
Passaggi successivi
- Scopri di più sulla tokenizzazione e sui tokenizzatori Spanner.
- Ulteriori informazioni sugli indici numerici.
- Scopri di più sul partizionamento degli indici.