Halaman ini menjelaskan cara menambahkan dan menggunakan indeks penelusuran. Penelusuran teks lengkap dijalankan terhadap entri dalam indeks penelusuran.
Cara menggunakan indeks penelusuran
Anda dapat membuat indeks penelusuran di kolom mana pun yang ingin Anda sediakan untuk penelusuran teks lengkap. Untuk membuat indeks penelusuran, gunakan
pernyataan DDL
CREATE SEARCH INDEX
. Untuk memperbarui indeks, gunakan pernyataan DDL
ALTER SEARCH INDEX
. Spanner secara otomatis membuat dan mengelola indeks penelusuran, termasuk menambahkan dan memperbarui data dalam indeks penelusuran segera setelah data tersebut berubah dalam database.
Partisi indeks penelusuran
Indeks penelusuran dapat dipartisi atau tidak dipartisi, bergantung pada jenis kueri yang ingin Anda percepat.
Contoh saat indeks berpartisi adalah pilihan terbaik adalah saat aplikasi mengkueri kotak surat email. Setiap kueri dibatasi untuk kotak surat tertentu.
Contoh saat kueri yang tidak dipartisi adalah pilihan terbaik adalah saat ada kueri di semua kategori produk dalam katalog produk.
Kasus penggunaan indeks penelusuran
Selain penelusuran teks lengkap, indeks penelusuran Spanner mendukung hal-hal berikut:
- Penelusuran JSON, yang merupakan cara efisien untuk mengindeks dan membuat kueri dokumen JSON dan JSONB.
- Penelusuran substring, yang merupakan jenis kueri yang mencari string yang lebih pendek (substring) dalam kumpulan teks yang lebih besar.
- Menggabungkan kondisi pada subset data terindeks apa pun, termasuk kecocokan persis dan numerik, ke dalam satu pemindaian indeks.
Untuk mengetahui informasi selengkapnya tentang kasus penggunaan, lihat Penelusuran versus indeks sekunder.
Contoh indeks penelusuran
Untuk menunjukkan kemampuan indeks penelusuran, misalkan ada tabel yang menyimpan informasi tentang album musik:
GoogleSQL
CREATE TABLE Albums (
AlbumId STRING(MAX) NOT NULL,
AlbumTitle STRING(MAX)
) PRIMARY KEY(AlbumId);
PostgreSQL
CREATE TABLE albums (
albumid character varying NOT NULL,
albumtitle character varying,
PRIMARY KEY(albumid));
Spanner memiliki beberapa fungsi tokenisasi yang membuat token. Untuk mengubah tabel sebelumnya agar pengguna dapat menjalankan penelusuran teks lengkap untuk menemukan judul album, gunakan fungsi TOKENIZE_FULLTEXT
untuk membuat token dari judul album. Kemudian, buat kolom yang menggunakan jenis data
TOKENLIST
untuk menyimpan output tokenisasi dari TOKENIZE_FULLTEXT
.
Untuk contoh ini, kita membuat kolom AlbumTitle_Tokens
.
GoogleSQL
ALTER TABLE Albums
ADD COLUMN AlbumTitle_Tokens TOKENLIST
AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN;
PostgreSQL
ALTER TABLE albums
ADD COLUMN albumtitle_tokens spanner.tokenlist
GENERATED ALWAYS AS (spanner.tokenize_fulltext(albumtitle)) VIRTUAL HIDDEN;
Contoh berikut menggunakan DDL
CREATE SEARCH INDEX
untuk membuat indeks penelusuran
(AlbumsIndex
) pada token AlbumTitle
(AlbumTitle_Tokens
):
GoogleSQL
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens);
PostgreSQL
Contoh ini menggunakan
CREATE SEARCH INDEX
.
CREATE SEARCH INDEX albumsindex ON albums(albumtitle_tokens);
Setelah menambahkan indeks penelusuran, gunakan kueri SQL untuk menemukan album yang cocok dengan kriteria penelusuran. Contoh:
GoogleSQL
SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
PostgreSQL
SELECT albumid
FROM albums
WHERE spanner.search(albumtitle_tokens, 'fifth symphony')
Konsistensi data
Saat indeks dibuat, Spanner menggunakan proses otomatis untuk mengisi ulang data guna memastikan konsistensi. Saat penulisan di-commit, indeks diperbarui dalam transaksi yang sama. Spanner secara otomatis melakukan pemeriksaan konsistensi data.
Definisi skema indeks penelusuran
Indeks penelusuran ditentukan pada satu atau beberapa kolom TOKENLIST
dari sebuah tabel. Indeks penelusuran memiliki komponen berikut:
- Tabel dasar: tabel Spanner yang memerlukan pengindeksan.
- Kolom
TOKENLIST
: kumpulan kolom yang menentukan token yang perlu diindeks. Urutan kolom ini tidak penting.
Misalnya, dalam pernyataan berikut, tabel dasarnya adalah Album. Kolom TOKENLIST
dibuat di AlbumTitle
(AlbumTitle_Tokens
) dan Rating
(Rating_Tokens
).
GoogleSQL
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);
PostgreSQL
CREATE TABLE albums (
albumid character varying NOT NULL,
singerid bigint NOT NULL,
releasetimestamp bigint NOT NULL,
albumtitle character varying,
rating double precision,
albumtitle_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(albumtitle)) VIRTUAL HIDDEN,
rating_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(rating)) VIRTUAL HIDDEN,
PRIMARY KEY(AlbumId));
Gunakan pernyataan CREATE SEARCH INDEX
berikut untuk membuat indeks penelusuran menggunakan token untuk AlbumTitle
dan Rating
:
GoogleSQL
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens, Rating_Tokens)
PARTITION BY SingerId
ORDER BY ReleaseTimestamp DESC
PostgreSQL
CREATE SEARCH INDEX albumsindex
ON albums(albumtitle_tokens, rating_tokens)
PARTITION BY singerid
ORDER BY releasetimestamp DESC
Indeks penelusuran memiliki opsi berikut:
- Partisi: grup kolom opsional yang membagi indeks penelusuran. Membuat kueri indeks yang dipartisi sering kali jauh lebih efisien daripada membuat kueri indeks yang tidak dipartisi. Untuk mengetahui informasi selengkapnya, lihat Mengelompokkan indeks penelusuran.
- Kolom urutan pengurutan: kolom
INT64
opsional yang menetapkan urutan pengambilan dari indeks penelusuran. Untuk informasi selengkapnya, lihat Urutan pengurutan indeks penelusuran. - Penyisipan: seperti indeks sekunder, Anda dapat menyisipkan indeks penelusuran. Indeks penelusuran yang disisipkan menggunakan lebih sedikit resource untuk menulis dan menggabungkan dengan tabel dasar. Untuk mengetahui informasi selengkapnya, lihat Indeks penelusuran yang disisipkan.
- Klausul opsi: daftar pasangan nilai kunci yang menggantikan setelan default indeks penelusuran.
Tata letak internal indeks penelusuran
Elemen penting dari representasi internal indeks penelusuran adalah
docid, yang berfungsi sebagai representasi kunci utama yang efisien untuk penyimpanan
dari tabel dasar yang dapat memiliki panjang yang tidak terbatas. Hal ini juga yang membuat
urutan tata letak data internal sesuai dengan ORDER BY
kolom pernyataan CREATE SEARCH INDEX
yang diberikan pengguna. ID ini direpresentasikan sebagai satu atau dua
bilangan bulat 64-bit.
Indeks penelusuran diimplementasikan secara internal sebagai pemetaan dua tingkat:
- Token ke docid
- Docid ke kunci utama tabel dasar
Skema ini menghasilkan penghematan penyimpanan yang signifikan karena Spanner tidak perlu menyimpan kunci utama tabel dasar lengkap untuk setiap pasangan <token, document>
.
Ada dua jenis indeks fisik yang menerapkan dua tingkat pemetaan:
- Indeks sekunder yang memetakan kunci partisi dan docid ke kunci utama tabel dasar. Dalam contoh di bagian sebelumnya, ini memetakan
{SingerId, ReleaseTimestamp, uid}
ke{AlbumId}
. Indeks sekunder juga menyimpan semua kolom yang ditentukan dalam klausaSTORING
dariCREATE SEARCH INDEX
. - Indeks token yang memetakan token ke docid, mirip dengan indeks terbalik dalam
literatur penarikan informasi. Spanner mempertahankan
indeks token terpisah untuk setiap
TOKENLIST
indeks penelusuran. Secara logis, indeks token menyimpan daftar docid untuk setiap token dalam setiap partisi (dikenal dalam penelusuran informasi sebagai daftar postingan). Daftar diurutkan berdasarkan token untuk pengambilan cepat, dan dalam daftar, docid digunakan untuk pengurutan. Indeks token individual adalah detail implementasi yang tidak diekspos melalui API Spanner.
Spanner mendukung empat opsi berikut untuk docid.
Indeks penelusuran | Docid | Perilaku |
---|---|---|
Klausul ORDER BY tidak ada untuk indeks penelusuran |
{uid} |
Spanner menambahkan nilai unik tersembunyi (UID) untuk mengidentifikasi setiap baris. |
ORDER BY column |
{column, uid} |
Spanner menambahkan kolom UID sebagai pemutus hubungan antara baris dengan nilai column yang sama dalam partisi. |
Catatan penggunaan:
- Kolom UID internal tidak diekspos melalui Spanner API.
- Dalam indeks tempat UID tidak ditambahkan, transaksi yang menambahkan baris dengan pasangan (partisi,urutan pengurutan) yang sudah ada akan gagal.
Misalnya, pertimbangkan data berikut:
AlbumId | SingerId | ReleaseTimestamp | SongTitle |
---|---|---|---|
a1 | 1 | 997 | Hari-hari yang indah |
a2 | 1 | 743 | Mata yang indah |
Dengan asumsi kolom pra-pengurutan dalam urutan menaik, konten indeks token yang dipartisi menurut SingerId
memartisi konten indeks token dengan cara berikut:
SingerId | _token | ReleaseTimestamp | uid |
---|---|---|---|
1 | indah | 743 | uid1 |
1 | indah | 997 | uid2 |
1 | hari | 743 | uid1 |
1 | mata | 997 | uid2 |
Sharding indeks penelusuran
Saat membagi tabel, Spanner akan mendistribusikan data indeks penelusuran sehingga semua token dalam baris tabel dasar tertentu berada dalam pembagian yang sama. Dengan kata lain, indeks penelusuran di-shard berdasarkan dokumen. Strategi sharding ini memiliki implikasi performa yang signifikan:
- Jumlah server yang berkomunikasi dengan setiap transaksi tetap
konstan, terlepas dari jumlah token atau jumlah kolom
TOKENLIST
yang diindeks. - Kueri penelusuran yang melibatkan beberapa ekspresi kondisional dieksekusi secara independen pada setiap pemisahan, sehingga menghindari overhead performa yang terkait dengan gabungan terdistribusi.
Indeks penelusuran memiliki dua mode distribusi:
- Sharding seragam (default). Dalam sharding seragam, data yang diindeks untuk setiap baris tabel dasar ditetapkan secara acak ke pemisahan indeks partisi.
- Sharding urutan pengurutan. Dalam sharding urutan pengurutan, data untuk setiap baris tabel dasar
ditetapkan ke pemisahan indeks partisi berdasarkan kolom
ORDER BY
(yaitu, kolom pra-pengurutan). Misalnya, dalam kasus urutan pengurutan menurun, semua baris dengan nilai urutan pengurutan terbesar muncul di pemisahan indeks pertama partisi, dan grup nilai urutan pengurutan terbesar berikutnya di pemisahan berikutnya.
Mode sharding ini memiliki keseimbangan antara risiko hotspot dan biaya kueri:
- Indeks penelusuran yang di-shard seragam direkomendasikan jika pola baca atau tulis ke indeks penelusuran dapat menyebabkan hotspot. Sharding seragam mengurangi hotspot dengan mendistribusikan beban baca dan tulis secara merata di seluruh bagian, tetapi hal ini dapat meningkatkan penggunaan resource selama eksekusi kueri sebagai gantinya. Dalam indeks penelusuran yang di-shard seragam, kueri harus membaca semua pemisahan dalam partisi, karena data didistribusikan secara acak. Saat mengakses indeks yang di-shard secara seragam, Spanner membaca semua split secara paralel untuk mengurangi latensi kueri secara keseluruhan.
- Indeks penelusuran yang di-shard dengan urutan pengurutan lebih disukai jika pola baca atau tulis cenderung tidak menyebabkan hotspot. Pendekatan ini dapat mengurangi biaya kueri
yang
ORDER BY
-nya cocok denganORDER BY
indeks, dan menentukanLIMIT
yang relatif rendah. Saat menjalankan kueri tersebut, Spanner membaca mulai dari pemisahan pertama partisi secara inkremental, dan kueri dapat selesai tanpa membaca semua pemisahan saatLIMIT
dapat dipenuhi lebih awal. Mode sharding indeks penelusuran dikonfigurasi menggunakan klausa
OPTIONS
.
GoogleSQL
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens, Rating_Tokens)
PARTITION BY SingerId
ORDER BY ReleaseTimestamp DESC
OPTIONS (sort_order_sharding = true);
PostgreSQL
Mode sharding indeks penelusuran dikonfigurasi menggunakan klausa
WITH
.
CREATE SEARCH INDEX albumsindex
ON albums(albumtitle_tokens, rating_tokens)
PARTITION BY singerid
ORDER BY releasetimestamp DESC
WITH (sort_order_sharding = true);
Jika sort_order_sharding=false
disetel atau tidak ditentukan, indeks penelusuran akan
dibuat menggunakan sharding seragam.
Indeks penelusuran yang diselingi
Seperti indeks sekunder, Anda dapat menyisipkan indeks penelusuran dalam tabel induk dari tabel dasar. Alasan utama menggunakan indeks penelusuran yang disisipkan adalah untuk menempatkan data tabel dasar dengan data indeks untuk partisi kecil. Kolokasi oportunistik ini memiliki keunggulan berikut:
- Operasi tulis tidak perlu melakukan commit dua fase.
- Gabungan kembali indeks penelusuran dengan tabel dasar tidak didistribusikan.
Indeks penelusuran berselang-seling memiliki batasan berikut:
- Hanya indeks yang di-shard menurut urutan pengurutan yang dapat disisipkan.
- Indeks penelusuran hanya dapat disisipkan dalam tabel tingkat teratas (bukan dalam tabel turunan).
- Seperti tabel yang di-interleave dan indeks sekunder, jadikan kunci tabel induk sebagai awalan kolom
PARTITION BY
dalam indeks penelusuran yang di-interleave.
Menentukan indeks penelusuran berselang-seling
Contoh berikut menunjukkan cara menentukan indeks penelusuran yang disisipkan:
GoogleSQL
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);
PostgreSQL
CREATE TABLE singers(
singerid bigint NOT NULL
PRIMARY KEY(singerid));
CREATE TABLE albums(
singerid bigint NOT NULL,
albumid character varying NOT NULL,
albumtitle character varying,
albumtitle_tokens spanner.tokenlist
GENERATED ALWAYS
AS (
spanner.tokenize_fulltext(albumtitle)
) VIRTUAL 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 WITH(sort_order_sharding = true);
Urutan pengurutan indeks penelusuran
Persyaratan untuk definisi urutan pengurutan indeks penelusuran berbeda dengan indeks sekunder.
Misalnya, perhatikan tabel berikut:
GoogleSQL
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);
PostgreSQL
CREATE TABLE albums (
albumid character varying NOT NULL,
releasetimestamp bigint NOT NULL,
albumname character varying,
albumname_token spanner.tokenlist
GENERATED ALWAYS AS(spanner.token(albumname)) VIRTUAL HIDDEN,
PRIMARY KEY(albumid));
Aplikasi dapat menentukan indeks sekunder untuk mencari informasi menggunakan
AlbumName
yang diurutkan berdasarkan ReleaseTimestamp
:
CREATE INDEX AlbumsSecondaryIndex ON Albums(AlbumName, ReleaseTimestamp DESC);
Indeks penelusuran yang setara akan terlihat seperti berikut (menggunakan tokenisasi pencocokan persis, karena indeks sekunder tidak mendukung penelusuran teks lengkap):
CREATE SEARCH INDEX AlbumsSearchIndex
ON Albums(AlbumName_Token)
ORDER BY ReleaseTimestamp DESC;
Urutan pengurutan indeks penelusuran harus sesuai dengan persyaratan berikut:
- Hanya gunakan kolom
INT64
untuk urutan pengurutan indeks penelusuran. Kolom yang memiliki ukuran arbitrer menggunakan terlalu banyak resource dalam indeks penelusuran karena Spanner perlu menyimpan docid di samping setiap token. Secara khusus, kolom urutan pengurutan tidak dapat menggunakan jenisTIMESTAMP
karenaTIMESTAMP
menggunakan presisi nanodetik yang tidak sesuai dengan bilangan bulat 64-bit. Kolom urutan pengurutan tidak boleh
NULL
. Ada dua cara untuk memenuhi persyaratan ini:- Nyatakan kolom urutan penyortiran sebagai
NOT NULL
. - Konfigurasi indeks untuk mengecualikan nilai NULL.
- Nyatakan kolom urutan penyortiran sebagai
Stempel waktu sering digunakan untuk menentukan urutan pengurutan. Praktik yang umum adalah menggunakan mikrodetik sejak epoch Unix untuk stempel waktu tersebut.
Aplikasi biasanya mengambil data terbaru terlebih dahulu menggunakan indeks penelusuran yang diurutkan dalam urutan menurun.
Indeks penelusuran yang difilter dengan NULL
Indeks penelusuran dapat menggunakan sintaksis WHERE column_name IS NOT NULL
untuk mengecualikan baris tabel dasar. Pemfilteran NULL dapat diterapkan ke kunci partisi, kolom urutan pengurutan, dan kolom tersimpan. Pemfilteran NULL pada kolom array yang disimpan tidak diizinkan.
Contoh
GoogleSQL
CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens)
STORING (Genre)
WHERE Genre IS NOT NULL
PostgreSQL
CREATE SEARCH INDEX albumsindex
ON albums(albumtitle_tokens)
INCLUDE (genre)
WHERE genre IS NOT NULL
Kueri harus menentukan kondisi pemfilteran NULL (Genre IS NOT NULL
untuk
contoh ini) dalam klausa WHERE
. Jika tidak, Pengoptimal Kueri tidak dapat menggunakan indeks penelusuran. Untuk mengetahui informasi selengkapnya, lihat
persyaratan kueri SQL.
Gunakan pemfilteran NULL pada kolom yang dihasilkan untuk mengecualikan baris berdasarkan kriteria arbitrer. Untuk mengetahui informasi selengkapnya, lihat Membuat indeks parsial menggunakan kolom yang dihasilkan.
Langkah berikutnya
- Pelajari tokenisasi dan tokenisasi Spanner.
- Pelajari indeks numerik.
- Pelajari indeks JSON.
- Pelajari partisi indeks.