Praktik Terbaik

Anda dapat menggunakan praktik terbaik yang tercantum di sini sebagai referensi cepat tentang hal-hal yang perlu diingat saat membangun aplikasi yang menggunakan Firestore dalam mode Datastore. Jika Anda baru mulai menggunakan mode Datastore, halaman ini mungkin bukan tempat terbaik untuk memulai, karena tidak mengajarkan dasar-dasar cara menggunakan mode Datastore. Jika Anda adalah pengguna baru, sebaiknya mulai dengan Memulai Firestore dalam mode Datastore.

Umum

  • Selalu gunakan karakter UTF-8 untuk nama namespace, nama jenis, nama properti, dan nama kunci kustom. Karakter non-UTF-8 yang digunakan dalam nama ini dapat mengganggu fungsi mode Datastore. Misalnya, karakter non-UTF-8 dalam nama properti dapat mencegah pembuatan indeks yang menggunakan properti tersebut.
  • Jangan gunakan garis miring (/) dalam nama jenis atau nama kunci kustom. Garis miring depan dalam nama ini dapat mengganggu fungsi di masa mendatang.
  • Hindari menyimpan informasi sensitif di Cloud Project ID. Project ID Cloud mungkin dipertahankan di luar masa berlaku project Anda.

Panggilan API

  • Gunakan operasi batch untuk operasi baca, tulis, dan hapus, bukan operasi tunggal. Operasi batch lebih efisien karena melakukan beberapa operasi dengan overhead yang sama seperti operasi tunggal.
  • Jika transaksi gagal, pastikan Anda mencoba melakukan rollback transaksi. Rollback akan meminimalkan latensi percobaan ulang untuk permintaan lain yang bersaing untuk resource yang sama dalam transaksi. Perhatikan bahwa rollback itu sendiri mungkin gagal, sehingga rollback hanya boleh berupa upaya terbaik.
  • Gunakan panggilan asinkron jika bisa, bukan panggilan sinkron. Panggilan asinkron meminimalkan dampak latensi. Misalnya, pertimbangkan aplikasi yang memerlukan hasil lookup() sinkron dan hasil kueri sebelum dapat merender respons. Jika lookup() dan kueri tidak memiliki dependensi data, tidak perlu menunggu secara sinkron hingga lookup() selesai sebelum memulai kueri.

Entity

  • Jangan sertakan entitas yang sama (menurut kunci) beberapa kali dalam commit yang sama. Menyertakan entitas yang sama beberapa kali dalam commit yang sama dapat memengaruhi latensi.

  • Lihat bagian tentang pembaruan pada entity.

Kunci

  • Nama kunci dibuat secara otomatis jika tidak diberikan saat pembuatan entity. Kunci tersebut dialokasikan agar didistribusikan secara merata di ruang kunci.
  • Untuk kunci yang menggunakan nama kustom, selalu gunakan karakter UTF-8 kecuali garis miring (/). Karakter non-UTF-8 mengganggu berbagai proses seperti mengimpor file ekspor mode Datastore ke BigQuery. Garis miring depan dapat mengganggu fungsi di masa mendatang.
  • Untuk kunci yang menggunakan ID numerik:
    • Jangan gunakan angka negatif untuk ID. ID negatif dapat mengganggu pengurutan.
    • Jangan gunakan nilai 0(nol) untuk ID. Jika melakukannya, Anda akan mendapatkan ID yang dialokasikan secara otomatis.
    • Jika Anda ingin menetapkan ID numerik Anda sendiri secara manual ke entitas yang Anda buat, minta aplikasi Anda untuk mendapatkan blok ID dengan metode allocateIds(). Tindakan ini akan mencegah mode Datastore menetapkan salah satu ID numerik manual Anda ke entity lain.
  • Jika Anda menetapkan ID numerik manual atau nama kustom Anda sendiri ke entity yang Anda buat, jangan gunakan nilai yang meningkat secara monoton seperti:

    1, 2, 3, ,
    "Customer1", "Customer2", "Customer3", .
    "Product 1", "Product 2", "Product 3", .
    

    Jika aplikasi menghasilkan traffic yang besar, penomoran berurutan tersebut dapat menyebabkan hotspot yang memengaruhi latensi mode Datastore. Untuk menghindari masalah ID numerik berurutan, dapatkan ID numerik dari metode allocateIds(). Metode allocateIds() menghasilkan urutan ID numerik yang didistribusikan dengan baik.

  • Dengan menentukan kunci atau menyimpan nama yang dihasilkan, Anda nantinya dapat melakukan lookup() pada entity tersebut tanpa perlu mengeluarkan kueri untuk menemukan entity.

Indeks

  • Jika properti tidak akan pernah diperlukan untuk kueri, kecualikan properti dari indeks. Mengindeks properti secara tidak perlu dapat meningkatkan latensi dan meningkatkan biaya penyimpanan entri indeks.
  • Hindari memiliki terlalu banyak indeks komposit. Penggunaan indeks komposit yang berlebihan dapat mengakibatkan peningkatan latensi tulis dan peningkatan biaya penyimpanan entri indeks. Jika Anda perlu menjalankan kueri ad hoc pada set data besar tanpa indeks yang ditentukan sebelumnya, gunakan BigQuery.
  • Jangan mengindeks properti dengan nilai yang meningkat secara monoton (seperti stempel waktu NOW()). Mempertahankan indeks tersebut dapat menyebabkan hotspot yang memengaruhi latensi mode Datastore untuk aplikasi dengan tingkat baca dan tulis yang tinggi. Untuk panduan lebih lanjut tentang cara menangani properti monoton, lihat Rasio operasi baca/tulis yang tinggi untuk rentang kunci yang sempit di bawah.

Properti

  • Selalu gunakan karakter UTF-8 untuk properti berjenis string. Karakter non-UTF-8 dalam properti jenis string dapat mengganggu kueri. Jika Anda perlu menyimpan data dengan karakter non-UTF-8, gunakan string byte.
  • Jangan gunakan titik dalam nama properti. Titik dalam nama properti mengganggu pengindeksan properti entity tersemat.

Kueri

  • Jika Anda hanya perlu mengakses kunci dari hasil kueri, gunakan kueri khusus kunci. Kueri khusus kunci menampilkan hasil dengan latensi dan biaya yang lebih rendah daripada mengambil seluruh entity.
  • Jika Anda hanya perlu mengakses properti tertentu dari entity, gunakan kueri proyeksi. Kueri proyeksi menampilkan hasil dengan latensi dan biaya yang lebih rendah daripada mengambil seluruh entity.
  • Demikian pula, jika Anda hanya perlu mengakses properti yang disertakan dalam filter kueri (misalnya, yang tercantum dalam klausa order by), gunakan kueri proyeksi.
  • Jangan gunakan offset. Sebagai gantinya, gunakan kursor. Penggunaan offset hanya akan menghindari ditampilkannya entity yang dilewati ke aplikasi Anda, tetapi entity ini masih diambil secara internal. Entity yang dilewati memengaruhi latensi kueri, dan aplikasi Anda akan dikenai biaya untuk operasi baca yang diperlukan untuk mengambilnya.

Mendesain untuk penskalaan

Praktik terbaik berikut menjelaskan cara menghindari situasi yang menciptakan masalah pertentangan.

Pembaruan pada entity

Saat mendesain aplikasi, pertimbangkan seberapa cepat aplikasi mengupdate satu entitas. Cara terbaik untuk menggambarkan performa beban kerja Anda adalah dengan melakukan pengujian beban. Tingkat maksimum persis yang dapat digunakan aplikasi untuk memperbarui satu entity sangat bergantung pada beban kerja. Faktornya meliputi kecepatan tulis, pertentangan di antara permintaan, dan jumlah indeks yang terpengaruh.

Operasi tulis entity memperbarui entity dan indeks terkait, dan Firestore dalam mode Datastore secara sinkron menerapkan operasi tulis di seluruh kuorum replika. Dengan kecepatan tulis yang cukup tinggi, database akan mulai mengalami pertentangan, latensi yang lebih tinggi, atau error lainnya.

Kecepatan baca/tulis yang tinggi untuk rentang kunci yang sempit

Hindari kecepatan baca atau tulis yang tinggi ke dokumen yang mirip secara leksikografis, atau aplikasi Anda akan mengalami error pertentangan. Masalah ini dikenal sebagai hotspotting dan aplikasi Anda dapat mengalami hotspotting jika melakukan salah satu hal berikut:

  • Membuat entity baru dengan kecepatan yang sangat tinggi dan mengalokasikan ID yang meningkat secara monoton miliknya sendiri.

    Mode Datastore mengalokasikan kunci menggunakan algoritma sebar. Anda seharusnya tidak mengalami hotspotting pada penulisan jika membuat entity baru menggunakan alokasi ID entity otomatis.

  • Membuat entitas baru dengan kecepatan yang sangat tinggi menggunakan kebijakan alokasi ID berurutan lama.

  • Membuat entitas baru dengan kecepatan tinggi untuk jenis dengan sedikit entitas.

  • Membuat entity baru dengan nilai properti yang diindeks dan meningkat secara monoton, seperti stempel waktu, dengan kecepatan yang sangat tinggi.

  • Menghapus entity dari jenis dengan kecepatan tinggi.

  • Menulis ke database dengan kecepatan yang sangat tinggi tanpa meningkatkan traffic secara bertahap.

Jika Anda mengalami peningkatan mendadak pada kecepatan tulis ke rentang kunci yang kecil, Anda bisa mendapatkan penulisan yang lambat karena hotspot. Mode Datastore pada akhirnya akan membagi ruang kunci untuk mendukung beban tinggi.

Batas untuk operasi baca biasanya jauh lebih tinggi daripada untuk operasi tulis, kecuali jika Anda membaca dari satu kunci dengan kecepatan tinggi.

Hot spot dapat diterapkan ke rentang kunci yang digunakan oleh kunci entity dan indeks.

Dalam beberapa kasus, hotspot dapat memiliki dampak yang lebih luas terhadap aplikasi daripada mencegah pembacaan atau penulisan ke rentang kunci yang kecil. Misalnya, tombol pintas mungkin dibaca atau ditulis selama startup instance, sehingga menyebabkan permintaan pemuatan gagal.

Jika Anda memiliki kunci atau properti yang diindeks yang akan meningkat secara monoton, Anda dapat menambahkan hash acak di awal untuk memastikan kunci di-shard ke beberapa tablet.

Demikian pula, jika Anda perlu membuat kueri pada properti yang meningkat (atau menurun) secara monoton menggunakan pengurutan atau filter, Anda dapat mengindeks pada properti baru, yang nilai monotonnya diawali dengan nilai yang memiliki kardinalitas tinggi di seluruh set data, tetapi umum untuk semua entity dalam cakupan kueri yang ingin Anda lakukan. Misalnya, jika Anda ingin membuat kueri entri berdasarkan stempel waktu, tetapi hanya perlu menampilkan hasil untuk satu pengguna dalam satu waktu, Anda dapat menambahkan awalan stempel waktu dengan ID pengguna dan mengindeks properti baru tersebut. Hal ini akan masih mengizinkan kueri dan hasil yang diurutkan untuk pengguna tersebut, tetapi keberadaan ID pengguna akan memastikan indeks itu sendiri di-shard dengan baik.

Meningkatkan traffic

Tingkatkan traffic secara bertahap ke jenis atau bagian ruang kunci baru.

Anda harus meningkatkan traffic ke jenis baru secara bertahap agar Firestore dalam mode Datastore memiliki cukup waktu untuk menyiapkan traffic yang meningkat. Sebaiknya gunakan maksimum 500 operasi per detik ke jenis baru, lalu tingkatkan traffic sebesar 50% setiap 5 menit. Secara teori, Anda dapat meningkatkan kapasitas hingga 740 ribu operasi per detik setelah 90 menit menggunakan jadwal peningkatan ini. Pastikan operasi tulis didistribusikan secara relatif merata di seluruh rentang kunci. SRE kami menyebutnya sebagai aturan "500/50/5".

Pola peningkatan bertahap ini sangat penting jika Anda mengubah kode untuk berhenti menggunakan jenis A dan menggunakan jenis B. Cara sederhana untuk menangani migrasi ini adalah dengan mengubah kode Anda untuk membaca jenis B, dan jika tidak ada, baca jenis A. Namun, hal ini dapat menyebabkan peningkatan traffic secara tiba-tiba ke jenis baru dengan sebagian ruang kunci yang sangat kecil.

Masalah yang sama juga dapat terjadi jika Anda memigrasikan entitas untuk menggunakan rentang kunci yang berbeda dalam jenis yang sama.

Strategi yang Anda gunakan untuk memigrasikan entity ke jenis atau kunci baru akan bergantung pada model data Anda. Di bawah ini adalah contoh strategi, yang dikenal sebagai "Parallel Reads". Anda perlu menentukan apakah strategi ini efektif untuk data Anda atau tidak. Pertimbangan penting adalah dampak biaya operasi paralel selama migrasi.

Baca dari entity atau kunci lama terlebih dahulu. Jika tidak ada, Anda dapat membaca dari entity atau kunci baru. Kecepatan baca yang tinggi untuk entity yang tidak ada dapat menyebabkan hotspotting, jadi Anda harus memastikan untuk secara bertahap meningkatkan beban. Strategi yang lebih baik adalah menyalin entity lama ke yang baru, lalu menghapus yang lama. Tingkatkan operasi baca paralel secara bertahap untuk memastikan bahwa ruang kunci baru dibagi dengan baik.

Strategi yang memungkinkan untuk secara bertahap meningkatkan operasi baca atau tulis pada jenis baru adalah dengan menggunakan hash deterministik ID pengguna untuk mendapatkan persentase acak pengguna yang menulis entity baru. Pastikan bahwa hasil hash ID pengguna tidak condong ke pengguna tertentu karena fungsi acak Anda atau perilaku pengguna.

Sementara itu, jalankan tugas Dataflow untuk menyalin semua data Anda dari entitas atau kunci lama ke entitas atau kunci baru. Tugas batch Anda harus menghindari penulisan ke kunci berurutan untuk mencegah hotspot. Setelah tugas batch selesai, Anda hanya dapat membaca dari lokasi baru.

Perbaikan strategi ini dapat berupa migrasi pengguna dalam batch kecil sekaligus. Tambahkan kolom ke entitas pengguna yang melacak status migrasi pengguna tersebut. Pilih sekelompok pengguna yang akan dimigrasikan berdasarkan hash ID pengguna. Tugas Mapreduce atau Dataflow akan memigrasikan kunci untuk kumpulan pengguna tersebut. Pengguna yang memiliki migrasi yang sedang berlangsung akan menggunakan pembacaan paralel.

Perhatikan bahwa Anda tidak dapat melakukan roll back dengan mudah, kecuali Anda melakukan operasi tulis ganda pada entity lama dan baru selama fase migrasi. Tindakan ini akan meningkatkan biaya mode Datastore yang dikeluarkan.

Penghapusan

Hindari menghapus entity dalam jumlah besar di rentang kunci yang kecil.

Firestore dalam mode Datastore secara berkala menulis ulang tabelnya untuk menghapus entri yang dihapus, dan mengatur ulang data Anda sehingga pembacaan dan penulisan menjadi lebih efisien. Proses ini dikenal sebagai pemadatan.

Jika Anda menghapus sejumlah besar entity mode Datastore di seluruh rentang kunci yang kecil, kueri di bagian indeks ini akan lebih lambat hingga pemadatan selesai. Dalam kasus ekstrem, kueri Anda mungkin kehabisan waktu tunggu sebelum menampilkan hasil.

Menggunakan nilai stempel waktu untuk kolom yang diindeks untuk mewakili waktu habis masa berlaku entitas adalah anti-pola. Untuk mengambil entity yang sudah tidak berlaku, Anda harus membuat kueri terhadap kolom yang diindeks ini, yang kemungkinan terletak di bagian ruang kunci yang tumpang-tindih dengan entri indeks untuk entity yang baru saja dihapus.

Anda dapat meningkatkan performa dengan "kueri sharding", yang menambahkan string dengan panjang tetap ke stempel waktu habis masa berlaku. Indeks diurutkan pada string lengkap, sehingga entity pada stempel waktu yang sama akan berada di seluruh rentang kunci indeks. Anda menjalankan beberapa kueri secara paralel untuk mengambil hasil dari setiap shard.

Solusi yang lebih lengkap untuk masalah stempel waktu habis masa berlaku adalah menggunakan "nomor generasi" yang merupakan penghitung global yang diperbarui secara berkala. Nomor pembuatan ditambahkan ke stempel waktu masa berlaku sehingga kueri diurutkan berdasarkan nomor pembuatan, lalu shard, lalu stempel waktu. Penghapusan entity lama terjadi pada generasi sebelumnya. Setiap entitas yang tidak dihapus harus memiliki nomor generasi yang ditingkatkan. Setelah penghapusan selesai, Anda akan melanjutkan ke generasi berikutnya. Kueri terhadap generasi lama akan berperforma buruk hingga pemadatan selesai. Anda mungkin perlu menunggu beberapa generasi selesai sebelum membuat kueri indeks untuk mendapatkan daftar entitas yang akan dihapus, guna mengurangi risiko hasil yang hilang karena konsistensi akhir.

Sharding dan replikasi

Gunakan sharding atau replikasi untuk menangani hotspot.

Anda dapat menggunakan replikasi jika perlu membaca sebagian rentang kunci dengan kecepatan yang lebih tinggi daripada yang diizinkan Firestore dalam mode Datastore. Dengan menggunakan strategi ini, Anda akan menyimpan N salinan entity yang sama sehingga memungkinkan kecepatan pembacaan yang lebih tinggi N kali lipat daripada yang didukung oleh satu entity.

Anda dapat menggunakan sharding jika perlu menulis ke sebagian rentang kunci dengan kecepatan yang lebih tinggi dari yang diizinkan Firestore dalam mode Datastore. Sharding membagi entitas menjadi bagian-bagian yang lebih kecil.

Beberapa kesalahan umum saat melakukan sharding meliputi:

  • Sharding menggunakan awalan waktu. Saat waktu beralih ke awalan berikutnya, bagian yang tidak terpecah baru akan menjadi hotspot. Sebagai gantinya, Anda harus secara bertahap melakukan rollover sebagian operasi tulis ke awalan baru.

  • Hanya melakukan sharding pada entitas terpanas. Jika Anda melakukan sharding pada sebagian kecil dari jumlah total entity, mungkin tidak ada baris yang memadai di antara entity hot untuk memastikan bahwa entity tersebut tetap berada di bagian yang berbeda.

Langkah selanjutnya