Mengoptimalkan Indeks

Halaman ini menjelaskan konsep yang perlu dipertimbangkan saat memilih Firestore dalam indeks mode Datastore untuk aplikasi Anda.

Firestore dalam mode Datastore menghasilkan performa kueri yang tinggi dengan menggunakan indeks untuk semua kueri. Performa sebagian besar kueri bergantung pada ukuran kumpulan hasil, bukan ukuran total database.

Firestore dalam mode Datastore menentukan indeks bawaan untuk setiap properti dalam suatu entity. Indeks properti tunggal ini mendukung banyak kueri sederhana. Firestore dalam mode Datastore mendukung fitur penggabungan indeks yang memungkinkan database menggabungkan indeks bawaan untuk mendukung kueri tambahan. Untuk kueri yang lebih kompleks, Anda harus menentukan indeks komposit terlebih dahulu.

Halaman ini berfokus pada fitur penggabungan indeks karena memengaruhi dua peluang pengoptimalan indeks yang penting:

  • Mempercepat kueri
  • Mengurangi jumlah indeks komposit

Contoh berikut menunjukkan fitur penggabungan indeks.

Memfilter Photo entity

Pertimbangkan database mode Datastore dengan entity jenis Photo:

Foto
Properti Jenis nilai Deskripsi
owner_id String ID pengguna
tag Array string Kata kunci berupa token
size Bilangan bulat Enumerasi:
  • 1 icon
  • 2 medium
  • 3 large
coloration Bilangan bulat Enumerasi:
  • 1 black & white
  • 2 color

Bayangkan Anda memerlukan fitur aplikasi yang memungkinkan pengguna membuat kueri entity Photo berdasarkan AND logis dari hal berikut:

  • Maksimal tiga filter berdasarkan properti:

    • owner_id
    • size
    • coloration
  • String penelusuran tag. Aplikasi membuat token string penelusuran menjadi tag dan menambahkan filter untuk setiap tag.

    Misalnya, aplikasi mengubah string penelusuran outside, family menjadi filter kueri tag=outside dan tag=family.

Dengan menggunakan indeks bawaan dan Firestore dalam fitur penggabungan indeks mode Datastore, Anda dapat memenuhi persyaratan indeks fitur filter Photo ini tanpa menambahkan indeks komposit tambahan.

Indeks bawaan untuk entity Photo mendukung kueri filter tunggal seperti:

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_owner_id = client.query(kind="Photo", filters=[("owner_id", "=", "user1234")])

query_size = client.query(kind="Photo", filters=[("size", "=", 2)])

query_coloration = client.query(kind="Photo", filters=[("coloration", "=", 2)])

Fitur filter Photo juga memerlukan kueri yang menggabungkan beberapa filter kesetaraan dengan AND yang logis:

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_all_properties = client.query(
    kind="Photo",
    filters=[
        ("owner_id", "=", "user1234"),
        ("size", "=", 2),
        ("coloration", "=", 2),
        ("tag", "=", "family"),
    ],
)

Firestore dalam mode Datastore dapat mendukung kueri ini dengan menggabungkan indeks bawaan.

Penggabungan indeks

Firestore dalam mode Datastore dapat menggunakan penggabungan indeks saat kueri dan indeks memenuhi semua batasan berikut:

  • Kueri hanya menggunakan filter kesetaraan (=)
  • Tidak ada indeks komposit yang sangat cocok dengan filter dan pengurutan kueri
  • Setiap filter kesetaraan cocok dengan setidaknya satu indeks yang ada dengan urutan yang sama seperti kueri

Dalam situasi ini, Firestore dalam mode Datastore dapat menggunakan indeks yang ada untuk mendukung kueri, bukan mengharuskan Anda mengonfigurasi indeks komposit tambahan.

Jika dua indeks atau lebih diurutkan berdasarkan kriteria yang sama, Firestore dalam mode Datastore dapat menggabungkan hasil dari beberapa pemindaian indeks untuk menemukan hasil yang sama untuk semua indeks tersebut. Firestore dalam mode Datastore dapat menggabungkan indeks bawaan, karena semua nilai diurutkan berdasarkan kunci entity.

Dengan menggabungkan indeks bawaan, Firestore dalam mode Datastore mendukung kueri dengan filter kesetaraan di beberapa properti:

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_all_properties = client.query(
    kind="Photo",
    filters=[
        ("owner_id", "=", "user1234"),
        ("size", "=", 2),
        ("coloration", "=", 2),
        ("tag", "=", "family"),
    ],
)

Firestore dalam mode Datastore juga dapat menggabungkan hasil indeks dari beberapa bagian dari indeks yang sama. Dengan menggabungkan berbagai bagian indeks bawaan untuk properti tag, Firestore dalam mode Datastore mendukung kueri yang menggabungkan beberapa filter tag dalam AND yang logis:

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_tag = client.query(
    kind="Photo",
    filters=[
        ("tag", "=", "family"),
        ("tag", "=", "outside"),
        ("tag", "=", "camping"),
    ],
)

query_owner_size_color_tags = client.query(
    kind="Photo",
    filters=[
        ("owner_id", "=", "user1234"),
        ("size", "=", 2),
        ("coloration", "=", 2),
        ("tag", "=", "family"),
        ("tag", "=", "outside"),
        ("tag", "=", "camping"),
    ],
)

Kueri yang didukung oleh indeks bawaan gabungan menyelesaikan kumpulan kueri yang diperlukan oleh fitur pemfilteran Photo. Perhatikan bahwa mendukung fitur pemfilteran Photo tidak memerlukan indeks komposit tambahan.

Ketika memilih indeks yang optimal untuk aplikasi, penting untuk memahami fitur penggabungan indeks. Penggabungan indeks memberi Firestore dalam mode Datastore fleksibilitas kueri yang lebih besar, tetapi dengan kemungkinan kompromi terhadap performa. Bagian berikutnya menjelaskan performa penggabungan indeks dan cara meningkatkan performa dengan menambahkan indeks komposit.

Menemukan indeks yang sempurna

Indeks diurutkan terlebih dahulu menurut ancestor, lalu menurut nilai properti, dalam urutan yang ditentukan dalam definisi indeks. Indeks komposit sempurna untuk kueri, yang memungkinkan kueri dijalankan dengan paling efisien, ditentukan di properti berikut, secara berurutan:

  1. Properti yang digunakan dalam filter kesetaraan
  2. Properti yang digunakan dalam tata urutan
  3. Properti yang digunakan di filter distinctOn
  4. Properti yang digunakan dalam filter rentang & ketidaksetaraan (yang belum disertakan dalam tata urutan)
  5. Properti yang digunakan dalam agregasi dan proyeksi (yang belum disertakan dalam tata urutan serta filter rentang & ketidaksetaraan)

Hal ini memastikan bahwa semua hasil untuk setiap kemungkinan eksekusi kueri diperhitungkan. Database Firestore dalam mode Datastore menjalankan kueri menggunakan indeks yang sempurna dengan mengikuti langkah-langkah berikut:

  1. Mengidentifikasi indeks yang sesuai dengan jenis kueri, properti filter, operator filter, dan tata urutan
  2. Memindai dari awal indeks ke entity pertama yang memenuhi semua atau sebagian kondisi filter kueri
  3. Terus memindai indeks, menampilkan setiap entity yang memenuhi semua kondisi filter, hingga
    • menemukan entity yang tidak memenuhi kondisi filter, atau
    • mencapai akhir indeks, atau
    • telah mengumpulkan jumlah hasil maksimum yang diminta oleh kueri

Misalnya, pertimbangkan kueri berikut:

SELECT * FROM Task
WHERE category = 'Personal'
  AND priority < 3
ORDER BY priority DESC

Indeks komposit yang sempurna untuk kueri ini adalah indeks kunci untuk entity jenis Task, dengan kolom untuk nilai properti category dan priority. Indeks diurutkan terlebih dahulu dalam urutan menaik menurut category, lalu dalam urutan menurun menurut priority:

indexes:
- kind: Task
  properties:
  - name: category
    direction: asc
  - name: priority
    direction: desc

Dua kueri dengan bentuk yang sama tetapi dengan nilai filter yang berbeda menggunakan indeks yang sama. Misalnya, kueri berikut menggunakan indeks yang sama dengan kueri sebelumnya:

SELECT * FROM Task
WHERE category = 'Work'
  AND priority < 5
ORDER BY priority DESC

Untuk indeks ini

indexes:
- kind: Task
  properties:
  - name: category
    direction: asc
  - name: priority
    direction: asc
  - name: created
    direction: asc

Indeks sebelumnya dapat memenuhi kedua kueri berikut:

SELECT * FROM Task
WHERE category = 'Personal'
  AND priority = 5
ORDER BY created ASC

dan

SELECT * FROM Task
WHERE category = 'Work'
ORDER BY priority ASC, created ASC

Mengoptimalkan pilihan indeks Anda

Bagian ini menjelaskan karakteristik performa penggabungan indeks dan dua peluang pengoptimalan yang terkait dengan penggabungan indeks:

  • Menambahkan indeks komposit untuk mempercepat kueri yang mengandalkan indeks gabungan
  • Mengurangi jumlah indeks komposit dengan memanfaatkan indeks gabungan

Performa penggabungan indeks

Dalam penggabungan indeks, Firestore dalam mode Datastore menggabungkan indeks secara efisien menggunakan algoritma penggabungan penggabungan zig-zag. Dengan algoritma ini, mode Datastore menggabungkan potensi kecocokan dari beberapa pemindaian indeks untuk menghasilkan kumpulan hasil yang cocok dengan sebuah kueri. Penggabungan indeks menggabungkan komponen filter pada waktu baca, bukan waktu tulis. Tidak seperti kebanyakan kueri dalam mode Datastore umumnya yang performanya hanya bergantung pada ukuran kumpulan hasil, performa untuk kueri penggabungan indeks bergantung pada filter dalam kueri dan jumlah potensi kecocokan yang dipertimbangkan database.

Performa kasus terbaik dari penggabungan indeks terjadi saat setiap potensi kecocokan dalam indeks memenuhi filter kueri. Dalam hal ini, performanya adalah O(R * I) dengan R adalah ukuran kumpulan hasil dan I adalah jumlah indeks yang dipindai.

Performa terburuk terjadi ketika database harus mempertimbangkan banyak potensi kecocokan, tetapi hanya sedikit yang memenuhi filter kueri. Dalam hal ini, performa adalah O(S) dengan S adalah ukuran kumpulan terkecil entity potensial dari satu pemindaian indeks.

Kinerja yang sebenarnya bergantung pada bentuk data. Jumlah rata-rata entity yang dipertimbangkan untuk setiap hasil yang ditampilkan adalah O(S/(R * I)). Kueri akan berperforma lebih buruk jika banyak entity cocok dengan setiap pemindaian indeks, tetapi hanya sedikit entity yang cocok dengan kueri secara keseluruhan. Artinya, R berukuran kecil dan S berukuran besar.

Empat hal yang memitigasi risiko ini:

  • Perencana kueri tidak mencari entity sampai mengetahui entity tersebut cocok dengan seluruh kueri.

  • Algoritma zig-zag tidak perlu menemukan semua hasil untuk menampilkan hasil berikutnya. Jika meminta 10 hasil pertama, Anda hanya membayar latensi untuk menemukan 10 hasil tersebut.

  • Algoritma zig-zag melewatkan sebagian besar hasil positif palsu. Performa kasus terburuk hanya terjadi jika hasil positif palsu (PP) terjalin dengan sempurna (dalam urutan) di antara pemindaian.

  • Latensi bergantung pada jumlah entity yang ditemukan di setiap pemindaian indeks, bukan jumlah entity yang cocok dengan setiap filter. Seperti yang ditunjukkan di bagian berikutnya, Anda dapat menambahkan indeks komposit untuk meningkatkan performa penggabungan indeks.

Mempercepat kueri penggabungan indeks

Saat Firestore dalam mode Datastore menggabungkan indeks, setiap pemindaian indeks sering kali dipetakan ke satu filter dalam kueri. Anda dapat meningkatkan performa kueri dengan menambahkan indeks komposit yang cocok dengan beberapa filter dalam kueri.

Pertimbangkan kueri ini:

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_owner_size_tag = client.query(
    kind="Photo",
    filters=[
        ("owner_id", "=", "username"),
        ("size", "=", 2),
        ("tag", "=", "family"),
    ],
)

Setiap filter dipetakan ke satu pemindaian indeks dalam indeks bawaan berikut:

Index(Photo, owner_id)
Index(Photo, size)
Index(Photo, tag)

Jika Anda menambahkan indeks komposit Index(Photo, owner_id, size), kueri akan dipetakan ke dua pemindaian indeks, bukan tiga pemindaian:

#  Satisfies both 'owner_id=username' and 'size=2'
Index(Photo, owner_id, size)
Index(Photo, tag)

Pertimbangkan skenario dengan banyak gambar besar, banyak gambar hitam putih, tetapi sedikit gambar panorama besar. Pemfilteran kueri untuk gambar panorama dan hitam-putih akan berjalan lambat jika menggabungkan indeks bawaan:

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_size_coloration = client.query(
    kind="Photo", filters=[("size", "=", 2), ("coloration", "=", 1)]
)

Untuk meningkatkan performa kueri, Anda dapat menurunkan nilai S (kumpulan entity terkecil dalam satu pemindaian indeks) di O(S/(R * I)) dengan menambahkan indeks komposit berikut:

Index(Photo, size, coloration)

Dibandingkan dengan penggunaan dua indeks bawaan, indeks komposit ini memberikan hasil potensial yang lebih sedikit untuk dua filter kueri yang sama. Pendekatan ini secara signifikan meningkatkan performa dengan mengorbankan satu indeks lagi.

Mengurangi jumlah indeks komposit dengan penggabungan indeks

Meskipun indeks komposit yang sama persis dengan filter dalam kueri memiliki performa terbaik, tidak selalu terbaik atau memungkinkan untuk menambahkan indeks komposit untuk setiap kombinasi filter. Anda harus menyeimbangkan indeks komposit dengan hal berikut:

  • Batas indeks komposit:

    Limit Jumlah
    Jumlah maksimum indeks komposit untuk database
    Jumlah maksimum ukuran entri indeks komposit entity 2 MiB
    Jumlah maksimum hal berikut untuk satu entitas:
    • jumlah nilai properti yang diindeks
    • jumlah entri indeks komposit
    20.000
  • Biaya penyimpanan setiap indeks tambahan.
  • Efek pada latensi tulis.

Masalah pengindeksan sering muncul pada kolom multinilai seperti properti tag dari entity Photo.

Misalnya, bayangkan bahwa fitur pemfilteran Photo sekarang harus mendukung klausa pengurutan yang menurun berdasarkan empat properti tambahan:

Foto
Properti Jenis nilai Deskripsi
date_added Bilangan bulat Tanggal/waktu
rating Float Rating pengguna gabungan
comment_count Bilangan bulat Jumlah komentar
download_count Bilangan bulat Jumlah download

Jika mengabaikan kolom tag, Anda dapat memilih indeks komposit yang cocok dengan setiap kombinasi filter Photo:

Index(Photo, owner_id, -date_added)
Index(Photo, owner_id, -comments)
Index(Photo, size, -date_added)
Index(Photo, size, -comments)
...
Index(Photo, owner_id, size, -date_added)
Index(Photo, owner_id, size, -comments)
...
Index(Photo, owner_id, size, coloration, -date_added)
Index(Photo, owner_id, size, coloration, -comments)

Jumlah total indeks komposit adalah:

2^(number of filters) * (number of different orders) = 2 ^ 3 * 4 = 32 composite indexes

Jika Anda mencoba mendukung hingga 3 filter tag, jumlah total indeks komposit adalah:

2 ^ (3 + 3 tag filters) * 4 = 256 indexes.

Indeks yang mencakup properti multinilai seperti tag juga menyebabkan masalah indeks yang meledak yang meningkatkan biaya penyimpanan dan latensi tulis.

Guna mendukung filter di kolom tag untuk fitur ini, Anda dapat mengurangi jumlah total indeks dengan mengandalkan indeks gabungan. Kumpulan indeks komposit berikut adalah jumlah minimum yang diperlukan untuk mendukung fitur pemfilteran Photo dengan pengurutan:

Index(Photo, owner_id, -date_added)
Index(Photo, owner_id, -rating)
Index(Photo, owner_id, -comments)
Index(Photo, owner_id, -downloads)
Index(Photo, size, -date_added)
Index(Photo, size, -rating)
Index(Photo, size, -comments)
Index(Photo, size, -downloads)
...
Index(Photo, tag, -date_added)
Index(Photo, tag, -rating)
Index(Photo, tag, -comments)
Index(Photo, tag, -downloads)

Jumlah indeks komposit yang ditentukan adalah:

(number of filters + 1) * (number of orders) = 7 * 4 = 28

Penggabungan indeks juga memberikan manfaat berikut:

  • Memungkinkan entity Photo mendukung hingga 1.000 tag tanpa batasan jumlah filter tag per kueri.
  • Mengurangi jumlah total indeks yang mengurangi biaya penyimpanan dan latensi tulis.

Memilih indeks untuk aplikasi

Anda dapat memilih indeks optimal untuk database mode Datastore Anda dengan menggunakan dua pendekatan:

  • Menggunakan penggabungan indeks untuk mendukung kueri tambahan

    • Memerlukan lebih sedikit indeks komposit
    • Mengurangi biaya penyimpanan per entitas
    • Meningkatkan latensi tulis
    • Menghindari ledakan indeks
    • Performa bergantung pada bentuk data
  • Menentukan indeks komposit yang cocok dengan beberapa filter dalam kueri

    • Meningkatkan performa kueri
    • Performa kueri yang konsisten yang tidak bergantung pada bentuk data
    • Harus tetap di bawah batas indeks komposit
    • Peningkatan biaya penyimpanan per entitas
    • Peningkatan latensi tulis

Saat mencari tahu indeks yang optimal untuk aplikasi Anda, jawabannya dapat berubah seiring perubahan bentuk data Anda. Mengambil sampel performa kueri memberi Anda gambaran yang baik tentang kueri umum aplikasi dan kuerinya yang lambat. Dengan informasi ini, Anda dapat menambahkan indeks untuk meningkatkan performa kueri yang umum dan lambat.