Praktik terbaik Cloud Datastore

Anda dapat menggunakan praktik terbaik yang tercantum di sini sebagai referensi cepat tentang hal yang perlu diingat saat membangun aplikasi yang menggunakan Datastore. Jika Anda baru mulai menggunakan Datastore, halaman ini mungkin bukan tempat terbaik untuk memulai, karena tidak mengajarkan dasar-dasar cara menggunakan Datastore. Jika Anda adalah pengguna baru, sebaiknya mulai dengan Memulai 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 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.
  • Sebagai praktik terbaik kepatuhan data, sebaiknya jangan simpan informasi sensitif dalam nama entity Datastore atau nama properti entity.

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

  • Mengelompokkan data yang sangat terkait di grup entity. Grup entity mengaktifkan kueri ancestor, yang akan menampilkan hasil yang sangat konsisten. Kueri ancestor juga dengan cepat memindai grup entity dengan I/O minimal karena entity dalam grup entity disimpan di tempat yang dekat secara fisik di server Datastore.
  • Hindari penulisan ke grup entity lebih dari sekali per detik. Menulis pada kecepatan yang berkelanjutan di atas batas tersebut membuat pembacaan yang konsisten pada akhirnya menjadi lebih akhir, menyebabkan waktu tunggu untuk pembacaan yang sangat konsisten, dan mengakibatkan performa keseluruhan aplikasi Anda yang lebih lambat. Penulisan batch atau transaksional ke grup entity hanya dihitung sebagai satu penulisan terhadap batas ini.
  • 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 Datastore.

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 cadangan Datastore ke Google 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 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 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() yang konsisten 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 diperlukan dapat mengakibatkan peningkatan latensi untuk mencapai konsistensi, dan peningkatan biaya penyimpanan entri indeks.
  • Hindari memiliki terlalu banyak indeks komposit. Penggunaan indeks komposit yang berlebihan dapat mengakibatkan peningkatan latensi untuk mencapai konsistensi, dan peningkatan biaya penyimpanan entri indeks. Jika Anda perlu menjalankan kueri ad hoc pada set data besar tanpa indeks yang ditentukan sebelumnya, gunakan Google BigQuery.
  • Jangan mengindeks properti dengan nilai yang meningkat secara monoton (seperti stempel waktu NOW()). Mempertahankan indeks seperti itu dapat menyebabkan hotspot yang memengaruhi latensi 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.
  • Jika Anda membutuhkan konsistensi kuat untuk kueri, gunakan kueri ancestor. (Untuk menggunakan kueri ancestor, pertama-tama Anda harus membuat struktur data untuk konsistensi yang kuat.) Kueri ancestor memberikan hasil yang sangat konsisten. Perhatikan bahwa kueri keys-only non-ancestor yang diikuti lookup() tidak menampilkan hasil yang kuat karena kueri yang bukan hanya kunci ancestor dapat memperoleh hasil dari indeks yang tidak konsisten pada saat kueri.

Mendesain untuk penskalaan

Pembaruan pada satu grup entitas

Satu entity group di Datastore tidak boleh diperbarui terlalu cepat.

Jika Anda menggunakan Datastore, Google merekomendasikan agar Anda mendesain aplikasi sehingga tidak perlu mengupdate grup entity lebih dari sekali per detik. Ingat bahwa entity tanpa induk dan tanpa turunan adalah grup entity-nya sendiri. Jika Anda mengupdate grup entity terlalu cepat, penulisan Datastore Anda akan memiliki latensi, waktu tunggu, dan jenis error lainnya yang lebih tinggi. Hal ini dikenal sebagai pertentangan.

Kecepatan penulisan Datastore ke satu grup entity terkadang dapat melebihi batas satu per detik, sehingga pengujian beban mungkin tidak menunjukkan masalah ini.

Kecepatan baca/tulis yang tinggi untuk rentang kunci yang sempit

Hindari kecepatan baca atau tulis yang tinggi ke kunci Datastore yang mirip secara leksikografis.

Datastore dibuat di atas database NoSQL Google, Bigtable, dan tunduk pada karakteristik performa Bigtable. Bigtable diskalakan dengan melakukan sharding baris ke tablet terpisah, dan baris ini diurutkan secara leksikografis berdasarkan kunci.

Jika menggunakan Datastore, Anda dapat mengalami operasi tulis yang lambat karena tablet panas jika terjadi peningkatan kecepatan tulis secara tiba-tiba pada sejumlah kunci yang melebihi kapasitas satu server tablet. Bigtable 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. Bigtable tidak dapat membagi satu tombol ke lebih dari satu tablet.

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

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

Secara default, Datastore mengalokasikan kunci menggunakan algoritma yang tersebar. Dengan demikian, Anda biasanya tidak akan mengalami hotspotting pada penulisan Datastore jika Anda membuat entity baru pada kecepatan tulis yang tinggi menggunakan kebijakan alokasi ID default. Ada beberapa kasus sudut di mana Anda dapat mengalami masalah ini:

  • Jika Anda membuat entity baru dengan kecepatan yang sangat tinggi menggunakan kebijakan alokasi ID berurutan yang lama.

  • Jika Anda membuat entity baru dengan kecepatan yang sangat tinggi dan mengalokasikan ID Anda sendiri yang meningkat secara monoton.

  • Jika Anda membuat entity baru dengan kecepatan yang sangat tinggi untuk jenis yang sebelumnya memiliki sangat sedikit entity yang ada. Bigtable akan dimulai dengan semua entity di server tablet yang sama dan akan memerlukan waktu untuk membagi rentang kunci ke server tablet terpisah.

  • Anda juga akan melihat masalah ini jika membuat entity baru dengan kecepatan tinggi menggunakan properti terindeks yang meningkat secara monoton, seperti stempel waktu, karena properti tersebut adalah kunci untuk baris dalam tabel indeks di Bigtable.

  • Datastore menambahkan namespace dan jenis grup root entity ke kunci baris Bigtable. Anda dapat mencapai hotspot jika mulai menulis ke namespace atau jenis baru tanpa meningkatkan traffic secara bertahap.

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 pengurutan atau filter, Anda dapat mengindeks properti baru yang akan memberi 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 mengawali stempel waktu dengan ID pengguna dan mengindeks properti baru tersebut. Hal ini masih akan mengizinkan kueri dan mengurutkan hasil untuk pengguna tersebut, tetapi keberadaan ID pengguna akan memastikan indeks itu sendiri di-sharding dengan baik.

Untuk penjelasan lebih detail tentang masalah ini, lihat postingan blog Ikai Lan tentang penyimpanan nilai yang meningkat secara monoton di Datastore.

Meningkatkan traffic

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

Anda harus meningkatkan traffic ke jenis Datastore baru secara bertahap guna memberi Bigtable waktu yang cukup untuk membagi tablet seiring meningkatnya traffic. Sebaiknya gunakan maksimum 500 operasi per detik ke jenis Datastore 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. Bigtable mungkin tidak dapat membagi tablet secara efisien jika keyspace sparse.

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 Bigtable. 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. Hal ini akan meningkatkan biaya Datastore yang timbul.

Penghapusan

Hindari menghapus sejumlah besar entity Datastore di sejumlah kecil kunci.

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

Jika Anda menghapus sejumlah besar entity 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

Gunakan sharding atau replikasi untuk hot Datastore key.

Anda bisa menggunakan replikasi jika perlu membaca sebagian rentang kunci pada kecepatan yang lebih tinggi dari yang diizinkan Bigtable. 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 dari yang diizinkan Bigtable. {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