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 berikut 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 pada nama-nama ini dapat mengganggu fungsi di masa mendatang.
  • Hindari menyimpan informasi sensitif di Cloud Project ID. ID Project 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 dengan satu operasi.
  • Jika transaksi gagal, pastikan Anda mencoba melakukan rollback transaksi. Rollback meminimalkan latensi percobaan ulang untuk permintaan berbeda yang bersaing untuk resource yang sama dalam suatu transaksi. Perhatikan bahwa rollback itu sendiri mungkin akan gagal, jadi sebaiknya rollback hanya merupakan 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, Anda tidak perlu menunggu secara sinkron sampai lookup() selesai sebelum memulai kueri.

Entity

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

  • Lihat bagian memperbarui entitas.

Kunci

  • Nama kunci dibuat secara otomatis jika tidak diberikan saat pembuatan entity. Keduanya dialokasikan agar didistribusikan secara merata di keyspace.
  • 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 dapat mengganggu fungsi di masa mendatang.
  • Untuk kunci yang menggunakan ID numerik:
    • Jangan menggunakan angka negatif untuk ID. ID negatif dapat mengganggu penyortiran.
    • Jangan gunakan nilai 0(nol) untuk ID. Jika melakukannya, Anda akan mendapatkan ID yang dialokasikan secara otomatis.
    • Jika ingin menetapkan ID numerik Anda sendiri secara manual ke entity 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 untuk 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 nanti dapat menjalankan lookup() pada entity tersebut tanpa perlu mengeluarkan kueri untuk menemukan entity.

Indeks

  • Jika properti tidak pernah diperlukan untuk kueri, kecualikan properti dari indeks. Pengindeksan properti yang tidak perlu dapat mengakibatkan peningkatan latensi dan peningkatan 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 seperti itu dapat menyebabkan hotspot yang memengaruhi latensi mode Datastore untuk aplikasi dengan kecepatan baca dan tulis yang tinggi. Untuk panduan lebih lanjut terkait cara menangani properti monoton, lihat Kecepatan baca/tulis yang tinggi untuk rentang kunci yang sempit di bawah.

Properti

  • Selalu gunakan karakter UTF-8 untuk properti jenis string. Karakter non-UTF-8 di properti string jenis dapat mengganggu kueri. Jika Anda perlu menyimpan data dengan karakter non-UTF-8, gunakan string byte.
  • Jangan gunakan titik pada nama properti. Titik pada nama properti mengganggu pengindeksan properti entitas yang disematkan.

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 suatu 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 cursors. Penggunaan offset hanya akan menghindari menampilkan entity yang dilewati ke aplikasi Anda, tetapi entity tersebut 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 menimbulkan masalah pertentangan.

Pembaruan pada entitas

Saat mendesain aplikasi, pertimbangkan seberapa cepat aplikasi mengupdate satu entity. Cara terbaik untuk menggambarkan performa beban kerja Anda adalah dengan melakukan pengujian beban. Tingkat maksimum persis yang dapat digunakan aplikasi untuk mengupdate 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 serta 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 tidak akan mengalami hotspotting pada operasi tulis jika membuat entity baru menggunakan alokasi ID entity otomatis.

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

  • Membuat entity baru dengan kecepatan tinggi untuk jenis tertentu dengan beberapa entity.

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

  • Menghapus entity dari suatu jenis dengan kecepatan tinggi.

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

Jika mengalami peningkatan kecepatan tulis secara tiba-tiba ke beberapa kunci, Anda bisa mendapatkan operasi tulis yang lambat karena hotspot. Mode Datastore pada akhirnya akan membagi ruang kunci untuk mendukung beban tinggi.

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

Hot spot dapat diterapkan pada 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 sejumlah kunci. Misalnya, hot key dapat dibaca atau ditulis selama startup instance, sehingga menyebabkan permintaan pemuatan gagal.

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

Demikian pula, jika Anda perlu membuat kueri pada properti yang meningkat (atau menurun) secara monoton menggunakan urutan atau filter, Anda dapat mengindeks properti baru, yang Anda gunakan sebagai awalan nilai monoton dengan nilai yang memiliki kardinalitas tinggi di seluruh set data, tetapi bersifat umum untuk semua entity dalam cakupan kueri yang ingin Anda lakukan. Misalnya, jika 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 tetap mengizinkan kueri dan mengurutkan hasil untuk pengguna tersebut, tetapi keberadaan ID pengguna akan memastikan indeks itu sendiri di-sharding dengan baik.

Meningkatkan traffic

Tingkatkan traffic secara bertahap ke jenis atau bagian baru dari keyspace.

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

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

Masalah yang sama juga dapat terjadi jika Anda memigrasikan entity 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. Berikut adalah contoh strategi, yang disebut "Pembacaan Paralel". Anda perlu menentukan apakah strategi ini efektif untuk data Anda atau tidak. Pertimbangan penting adalah dampak biaya dari operasi paralel selama migrasi.

Baca dari entity atau kunci lama terlebih dahulu. Jika tidak ada, maka Anda dapat membaca dari kunci atau entity baru. Tingkat pembacaan yang tinggi pada entity yang tidak ada dapat menyebabkan hotspotting. Jadi, Anda harus memastikan untuk meningkatkan beban secara bertahap. Strategi yang lebih baik adalah menyalin entitas lama ke yang baru lalu menghapus yang lama. Tingkatkan pembacaan paralel secara bertahap untuk memastikan ruang kunci baru terbagi dengan baik.

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

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

Peningkatan dari strategi ini adalah memigrasikan sekelompok kecil pengguna sekaligus. Menambahkan kolom ke entity 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 batch pengguna tersebut. Pengguna yang sedang melakukan migrasi akan menggunakan operasi baca paralel.

Perlu diperhatikan bahwa Anda tidak dapat melakukan roll back dengan mudah, kecuali jika Anda melakukan penulisan ganda pada entity lama dan baru selama fase migrasi. Tindakan ini akan meningkatkan biaya mode Datastore yang timbul.

Penghapusan

Hindari menghapus entity dalam jumlah besar di berbagai kunci.

Firestore dalam mode Datastore secara berkala menulis ulang tabelnya untuk menghapus entri yang telah dihapus, serta untuk mengatur ulang data Anda agar operasi baca dan tulis menjadi lebih efisien. Proses ini dikenal sebagai pemadatan.

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

Penggunaan nilai stempel waktu untuk kolom terindeks adalah antipola untuk menunjukkan waktu habis masa berlaku entity. Untuk mengambil entity yang sudah tidak berlaku, Anda harus membuat kueri berdasarkan kolom terindeks ini, yang mungkin terletak di bagian keyspace yang tumpang-tindih dengan entri indeks untuk entity yang baru saja dihapus.

Anda dapat meningkatkan performa dengan "kueri shard", yang menambahkan string dengan panjang tetap ke stempel waktu 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 sebelum stempel waktu habis masa berlaku sehingga kueri diurutkan berdasarkan nomor pembuatan, lalu sharding, lalu stempel waktu. Penghapusan entity lama terjadi pada generasi sebelumnya. Nomor pembuatan entitas yang tidak dihapus akan bertambah. Setelah penghapusan selesai, Anda dapat beralih ke generasi berikutnya. Kueri terhadap generasi yang lebih lama akan berperforma buruk hingga pemadatan selesai. Anda mungkin perlu menunggu beberapa generasi selesai sebelum membuat kueri indeks untuk mendapatkan daftar entity yang akan dihapus, guna mengurangi risiko tidak adanya hasil karena konsistensi akhir.

Sharding dan replikasi

Menggunakan sharding atau replikasi untuk menangani hotspot.

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

Anda dapat menggunakan sharding jika perlu menulis ke sebagian rentang kunci dengan kecepatan yang lebih tinggi daripada yang diizinkan oleh Firestore dalam mode Datastore. {i>Sharding<i} memecah suatu entitas menjadi potongan-potongan yang lebih kecil.

Beberapa kesalahan umum saat sharding meliputi:

  • Sharding menggunakan awalan waktu. Jika waktu bergulir ke awalan berikutnya, bagian baru yang tidak terpisah akan menjadi hotspot. Sebagai gantinya, Anda harus secara bertahap me-roll sebagian penulisan ke awalan baru.

  • Melakukan sharding hanya entitas terpopuler. Jika Anda melakukan sharding sebagian kecil dari jumlah total entity, mungkin baris di antara entity panas tidak akan cukup untuk memastikan bahwa entity tersebut tetap berada di bagian yang berbeda.

Langkah selanjutnya