Lebih banyak pengembang harus mengetahui hal ini tentang database

Catatan. terjemahan: Jaana Dogan adalah seorang insinyur berpengalaman di Google yang saat ini mengerjakan observabilitas layanan produksi perusahaan yang ditulis dalam Go. Dalam artikel ini, yang mendapatkan popularitas besar di kalangan pembaca berbahasa Inggris, ia mengumpulkan 17 poin rincian teknis penting mengenai DBMS (dan terkadang sistem terdistribusi secara umum) yang berguna untuk dipertimbangkan bagi pengembang aplikasi besar/menuntut.

Lebih banyak pengembang harus mengetahui hal ini tentang database

Sebagian besar sistem komputer melacak statusnya dan, oleh karena itu, memerlukan semacam sistem penyimpanan data. Saya mengumpulkan pengetahuan tentang database dalam jangka waktu yang lama, serta membuat kesalahan desain yang menyebabkan kehilangan dan pemadaman data. Dalam sistem yang memproses informasi dalam jumlah besar, database terletak di jantung arsitektur sistem dan bertindak sebagai elemen kunci dalam memilih solusi optimal. Meskipun perhatian diberikan pada pekerjaan database, masalah yang coba diantisipasi oleh pengembang aplikasi seringkali hanya puncak gunung es. Dalam rangkaian artikel ini, saya membagikan beberapa ide yang akan berguna bagi pengembang yang tidak berspesialisasi dalam bidang ini.

  1. Anda beruntung jika 99,999% jaringan tidak menimbulkan masalah.
  2. ACID mempunyai arti yang berbeda-beda.
  3. Setiap database memiliki mekanismenya sendiri untuk memastikan konsistensi dan isolasi.
  4. Pemblokiran optimis datang untuk menyelamatkan ketika sulit mempertahankan pemblokiran biasa.
  5. Ada anomali lain selain pembacaan kotor dan kehilangan data.
  6. Basis data dan pengguna tidak selalu menyetujui tindakan yang diambil.
  7. Sharding tingkat aplikasi dapat dipindahkan ke luar aplikasi.
  8. Peningkatan otomatis bisa berbahaya.
  9. Data basi bisa berguna dan tidak perlu dikunci.
  10. Distorsi biasa terjadi pada sumber waktu mana pun.
  11. Keterlambatan mempunyai banyak arti.
  12. Persyaratan kinerja harus dievaluasi untuk transaksi tertentu.
  13. Transaksi bertumpuk bisa berbahaya.
  14. Transaksi tidak boleh dikaitkan dengan status aplikasi.
  15. Perencana kueri dapat memberi tahu Anda banyak hal tentang database.
  16. Migrasi online memang sulit, tetapi mungkin dilakukan.
  17. Peningkatan signifikan dalam database menyebabkan peningkatan ketidakpastian.

Saya ingin mengucapkan terima kasih kepada Emmanuel Odeke, Rein Henrichs, dan lainnya atas tanggapan mereka terhadap versi awal artikel ini.

Anda beruntung jika 99,999% jaringan tidak menimbulkan masalah.

Pertanyaan yang tersisa adalah seberapa andalnya teknologi jaringan modern dan seberapa sering sistem mati karena kegagalan jaringan. Informasi mengenai masalah ini masih langka dan penelitian sering kali didominasi oleh organisasi besar dengan jaringan, peralatan, dan personel khusus.

Dengan tingkat ketersediaan 99,999% untuk Spanner (database Google yang didistribusikan secara global), Google mengklaim bahwa hanya 7,6% masalah terkait dengan jaringan. Pada saat yang sama, perusahaan menyebut jaringan khususnya sebagai “pilar utama” ketersediaan tinggi. Belajar Bailis dan Kingsbury, yang dilakukan pada tahun 2014, menantang salah satu dari “kesalahpahaman tentang komputasi terdistribusi", yang dirumuskan Peter Deutsch pada tahun 1994. Apakah jaringan tersebut benar-benar dapat diandalkan?

Penelitian komprehensif di luar perusahaan raksasa, yang dilakukan untuk Internet yang lebih luas, tidak ada. Data dari para pemain utama juga tidak mencukupi mengenai berapa persentase masalah pelanggan mereka yang terkait dengan jaringan. Kami sangat menyadari pemadaman listrik di tumpukan jaringan penyedia cloud besar yang dapat mematikan sebagian besar Internet selama beberapa jam hanya karena pemadaman tersebut merupakan peristiwa penting yang berdampak pada banyak orang dan perusahaan. Pemadaman jaringan dapat menyebabkan masalah di lebih banyak kasus, meskipun tidak semua kasus tersebut menjadi sorotan. Klien layanan cloud juga tidak tahu apa-apa tentang penyebab masalahnya. Jika terjadi kegagalan, hampir tidak mungkin untuk mengaitkannya dengan kesalahan jaringan di pihak penyedia layanan. Bagi mereka, layanan pihak ketiga adalah kotak hitam. Tidak mungkin menilai dampaknya tanpa menjadi penyedia layanan yang besar.

Mengingat apa yang dilaporkan oleh para pemain besar tentang sistem mereka, dapat dikatakan bahwa Anda beruntung jika kesulitan jaringan hanya menyebabkan sebagian kecil dari potensi masalah downtime. Komunikasi jaringan masih mengalami masalah-masalah biasa seperti kegagalan perangkat keras, perubahan topologi, perubahan konfigurasi administratif, dan pemadaman listrik. Baru-baru ini, saya terkejut mengetahui bahwa daftar kemungkinan masalah telah ditambahkan gigitan hiu (ya, Anda tidak salah dengar).

ACID mempunyai arti yang berbeda-beda

Akronim ACID adalah singkatan dari Atomicity, Consistency, Isolation, Reliability. Properti transaksi ini dimaksudkan untuk memastikan validitasnya jika terjadi kegagalan, kesalahan, kegagalan perangkat keras, dll. Tanpa ACID atau skema serupa, akan sulit bagi pengembang aplikasi untuk membedakan antara apa yang menjadi tanggung jawab mereka dan apa yang menjadi tanggung jawab database. Sebagian besar database transaksional relasional mencoba mematuhi ACID, namun pendekatan baru seperti NoSQL telah memunculkan banyak database tanpa transaksi ACID karena mahal untuk diterapkan.

Ketika saya pertama kali memasuki industri ini, pimpinan teknis kami berbicara tentang betapa relevannya konsep ACID. Agar adil, ACID dianggap sebagai gambaran kasar daripada standar implementasi yang ketat. Saat ini saya merasa hal ini sangat berguna karena mengangkat kategori masalah tertentu (dan menyarankan serangkaian kemungkinan solusi).

Tidak semua DBMS mematuhi ACID; Pada saat yang sama, implementasi database yang mendukung ACID memahami serangkaian persyaratan secara berbeda. Salah satu alasan mengapa implementasi ACID tidak merata adalah karena banyaknya trade-off yang harus dilakukan untuk mengimplementasikan persyaratan ACID. Pembuat konten mungkin menampilkan database mereka sesuai dengan ACID, namun interpretasi kasus edge mungkin sangat bervariasi, begitu pula mekanisme penanganan kejadian yang "tidak mungkin". Paling tidak, pengembang dapat memperoleh pemahaman tingkat tinggi tentang seluk-beluk implementasi dasar untuk mendapatkan pemahaman yang tepat tentang perilaku khusus dan trade-off desain mereka.

Perdebatan tentang apakah MongoDB mematuhi persyaratan ACID terus berlanjut bahkan setelah rilis versi 4. MongoDB sudah lama tidak didukung pencatatan, meskipun secara default data dimasukkan ke disk tidak lebih dari sekali setiap 60 detik. Bayangkan skenario berikut: sebuah aplikasi memposting dua tulisan (w1 dan w2). MongoDB berhasil menyimpan w1, tetapi w2 hilang karena kegagalan perangkat keras.

Lebih banyak pengembang harus mengetahui hal ini tentang database
Diagram yang mengilustrasikan skenario. MongoDB mogok sebelum dapat menulis data ke disk

Mengkomit ke disk adalah proses yang mahal. Dengan menghindari komitmen yang sering dilakukan, pengembang meningkatkan kinerja perekaman dengan mengorbankan keandalan. MongoDB saat ini mendukung logging, namun penulisan kotor masih dapat memengaruhi integritas data karena log diambil setiap 100 md secara default. Artinya, skenario serupa masih mungkin terjadi untuk log dan perubahan yang disajikan di dalamnya, meskipun risikonya jauh lebih rendah.

Setiap database memiliki konsistensi dan mekanisme isolasinya sendiri

Dari persyaratan ACID, konsistensi dan isolasi memiliki jumlah penerapan berbeda yang paling besar karena rentang trade-off yang lebih luas. Harus dikatakan bahwa konsistensi dan isolasi adalah fungsi yang cukup mahal. Mereka memerlukan koordinasi dan meningkatkan persaingan untuk konsistensi data. Kompleksitas masalah meningkat secara signifikan ketika diperlukan penskalaan database secara horizontal di beberapa pusat data (terutama jika pusat data tersebut berlokasi di wilayah geografis yang berbeda). Mencapai tingkat konsistensi yang tinggi sangatlah sulit, karena hal ini juga mengurangi ketersediaan dan meningkatkan segmentasi jaringan. Untuk penjelasan yang lebih umum tentang fenomena ini, saya menyarankan Anda untuk merujuk ke teorema CAP. Perlu juga dicatat bahwa aplikasi dapat menangani sejumlah kecil ketidakkonsistenan, dan pemrogram dapat memahami nuansa masalah dengan cukup baik untuk mengimplementasikan logika tambahan dalam aplikasi untuk menangani ketidakkonsistenan tanpa terlalu bergantung pada database untuk menanganinya.

DBMS sering kali menyediakan tingkat isolasi yang berbeda. Pengembang aplikasi dapat memilih yang paling efektif berdasarkan preferensi mereka. Isolasi rendah memungkinkan peningkatan kecepatan, namun juga meningkatkan risiko perlombaan data. Insulasi yang tinggi mengurangi kemungkinan ini, namun memperlambat pekerjaan dan dapat menyebabkan persaingan, yang akan menyebabkan rem pada bagian dasar sehingga kegagalan akan dimulai.

Lebih banyak pengembang harus mengetahui hal ini tentang database
Tinjauan model konkurensi yang ada dan hubungan di antara mereka

Standar SQL hanya mendefinisikan empat tingkat isolasi, meskipun secara teori dan praktik masih banyak lagi. Jepson.io menawarkan gambaran yang sangat baik tentang model konkurensi yang ada. Misalnya, Google Spanner menjamin kemampuan serialisasi eksternal dengan sinkronisasi jam, dan meskipun ini adalah lapisan isolasi yang lebih ketat, lapisan ini tidak ditentukan dalam lapisan isolasi standar.

Standar SQL menyebutkan tingkat isolasi berikut:

  • Serializable (paling ketat dan mahal): Eksekusi serial memiliki efek yang sama dengan beberapa eksekusi transaksi berurutan. Eksekusi berurutan berarti bahwa setiap transaksi berikutnya dimulai hanya setelah transaksi sebelumnya selesai. Perlu dicatat bahwa levelnya Serializable sering diimplementasikan sebagai apa yang disebut isolasi snapshot (misalnya, di Oracle) karena perbedaan interpretasi, meskipun isolasi snapshot itu sendiri tidak terwakili dalam standar SQL.
  • Bacaan berulang: Catatan yang tidak terikat pada transaksi saat ini tersedia untuk transaksi saat ini, namun perubahan dilakukan oleh transaksi lain (seperti baris baru) tidak terlihat.
  • Baca berkomitmen: Data yang tidak terikat tidak tersedia untuk transaksi. Dalam hal ini, transaksi hanya dapat melihat data yang dikomit, dan pembacaan bayangan mungkin terjadi. Jika suatu transaksi memasukkan dan melakukan baris baru, transaksi saat ini akan dapat melihatnya ketika ditanya.
  • Baca tanpa komitmen (tingkat paling tidak ketat dan mahal): Pembacaan kotor diperbolehkan, transaksi dapat melihat perubahan yang tidak dikomit yang dilakukan oleh transaksi lain. Dalam praktiknya, level ini mungkin berguna untuk perkiraan kasar, seperti kueri COUNT(*) di atas meja.

Уровень Serializable meminimalkan risiko perlombaan data, sekaligus paling mahal untuk diterapkan dan menghasilkan beban kompetitif tertinggi pada sistem. Tingkat isolasi lainnya lebih mudah diterapkan, tetapi meningkatkan kemungkinan terjadinya data race. Beberapa DBMS memungkinkan Anda mengatur tingkat isolasi khusus, yang lain memiliki preferensi yang kuat dan tidak semua tingkat didukung.

Dukungan untuk tingkat isolasi sering kali diiklankan dalam DBMS tertentu, namun hanya studi yang cermat terhadap perilakunya yang dapat mengungkapkan apa yang sebenarnya terjadi.

Lebih banyak pengembang harus mengetahui hal ini tentang database
Tinjauan anomali konkurensi pada tingkat isolasi berbeda untuk DBMS berbeda

Martin Kleppmann dalam proyeknya pertapaan Membandingkan tingkat isolasi yang berbeda, membicarakan anomali konkurensi, dan apakah database mampu mematuhi tingkat isolasi tertentu. Penelitian Kleppmann menunjukkan betapa berbedanya pemikiran pengembang database tentang tingkat isolasi.

Pemblokiran optimis datang untuk menyelamatkan ketika sulit mempertahankan pemblokiran biasa.

Pemblokiran bisa sangat mahal, bukan hanya karena meningkatkan persaingan dalam database, namun juga karena memerlukan server aplikasi untuk terus-menerus terhubung ke database. Segmentasi jaringan dapat memperburuk situasi penguncian eksklusif dan menyebabkan kebuntuan yang sulit diidentifikasi dan diselesaikan. Jika penguncian eksklusif tidak cocok, penguncian optimis akan membantu.

Kunci optimis adalah metode yang saat membaca sebuah string, versinya, checksum, atau waktu modifikasi terakhirnya diperhitungkan. Hal ini memungkinkan Anda untuk memastikan bahwa tidak ada perubahan versi atom sebelum mengubah entri:

UPDATE products
SET name = 'Telegraph receiver', version = 2
WHERE id = 1 AND version = 1

Dalam hal ini, memperbarui tabel products tidak akan dilakukan jika operasi lain sebelumnya melakukan perubahan pada baris ini. Jika tidak ada operasi lain yang dilakukan pada baris ini, perubahan untuk satu baris akan terjadi dan kita dapat mengatakan bahwa pembaruan berhasil.

Ada anomali lain selain pembacaan kotor dan kehilangan data

Terkait konsistensi data, fokusnya adalah pada potensi kondisi balapan yang dapat menyebabkan pembacaan kotor dan kehilangan data. Namun anomali data tidak berhenti sampai di situ.

Salah satu contoh anomali tersebut adalah distorsi rekaman (tulis miring). Distorsi sulit dideteksi karena biasanya tidak dicari secara aktif. Hal ini bukan disebabkan oleh pembacaan yang kotor atau kehilangan data, namun karena pelanggaran batasan logis yang ditempatkan pada data.

Sebagai contoh, mari kita pertimbangkan aplikasi pemantauan yang mengharuskan satu operator selalu siap dihubungi setiap saat:

BEGIN tx1;                      BEGIN tx2;
SELECT COUNT(*)
FROM operators
WHERE oncall = true;
0                               SELECT COUNT(*)
                                FROM operators
                                WHERE oncall = TRUE;
                                0
UPDATE operators                UPDATE operators
SET oncall = TRUE               SET oncall = TRUE
WHERE userId = 4;               WHERE userId = 2;
COMMIT tx1;                     COMMIT tx2;

Dalam situasi di atas, kerusakan catatan akan terjadi jika kedua transaksi berhasil dilakukan. Meskipun tidak ada pembacaan kotor atau kehilangan data, integritas data telah dikompromikan: sekarang dua orang dianggap sedang menelepon pada saat yang sama.

Isolasi serial, desain skema, atau batasan database dapat membantu menghilangkan kerusakan penulisan. Pengembang harus dapat mengidentifikasi anomali tersebut selama pengembangan untuk menghindarinya dalam produksi. Pada saat yang sama, distorsi rekaman sangat sulit dicari dalam basis kode. Terutama dalam sistem besar, ketika tim pengembangan yang berbeda bertanggung jawab untuk mengimplementasikan fungsi berdasarkan tabel yang sama dan tidak menyetujui spesifikasi akses data.

Basis data dan pengguna tidak selalu sepakat tentang apa yang harus dilakukan

Salah satu fitur utama database adalah jaminan perintah eksekusi, namun perintah ini sendiri mungkin tidak transparan bagi pengembang perangkat lunak. Basis data mengeksekusi transaksi sesuai urutan penerimaannya, bukan sesuai urutan yang diinginkan pemrogram. Urutan transaksi sulit diprediksi, terutama dalam sistem paralel dengan muatan tinggi.

Selama pengembangan, terutama ketika bekerja dengan perpustakaan non-pemblokiran, gaya yang buruk dan keterbacaan yang rendah dapat menyebabkan pengguna percaya bahwa transaksi dieksekusi secara berurutan, padahal sebenarnya mereka dapat masuk ke database dalam urutan apa pun.

Sekilas pada program di bawah ini T1 dan T2 dipanggil secara berurutan, namun jika fungsi tersebut non-blocking dan langsung mengembalikan hasilnya dalam bentuk janji, maka urutan panggilan akan ditentukan oleh saat panggilan tersebut masuk ke database:

result1 = T1() // hasil nyata adalah janji
hasil2 = T2()

Jika atomisitas diperlukan (yaitu, semua operasi harus diselesaikan atau dibatalkan) dan urutannya penting, maka operasi T1 dan T2 harus dilakukan dalam satu transaksi.

Sharding tingkat aplikasi dapat dipindahkan ke luar aplikasi

Sharding adalah metode mempartisi database secara horizontal. Beberapa database dapat secara otomatis membagi data secara horizontal, sementara database lainnya tidak bisa, atau tidak begitu baik dalam hal itu. Ketika arsitek/pengembang data dapat memprediksi dengan tepat bagaimana data akan diakses, mereka dapat membuat partisi horizontal di ruang pengguna alih-alih mendelegasikan pekerjaan ini ke database. Proses ini disebut "sharding tingkat aplikasi" (sharding tingkat aplikasi).

Sayangnya, nama ini sering menimbulkan kesalahpahaman bahwa sharding ada di layanan aplikasi. Faktanya, ini bisa diimplementasikan sebagai lapisan terpisah di depan database. Bergantung pada pertumbuhan data dan iterasi skema, persyaratan sharding bisa menjadi sangat rumit. Beberapa strategi mungkin mendapat manfaat dari kemampuan untuk melakukan iterasi tanpa harus memindahkan server aplikasi.

Lebih banyak pengembang harus mengetahui hal ini tentang database
Contoh arsitektur di mana server aplikasi dipisahkan dari layanan sharding

Memindahkan sharding ke layanan terpisah memperluas kemampuan untuk menggunakan strategi sharding yang berbeda tanpa perlu menerapkan ulang aplikasi. Vites adalah contoh sistem sharding di tingkat aplikasi. Vitess menyediakan sharding horizontal untuk MySQL dan memungkinkan klien untuk terhubung melalui protokol MySQL. Sistem mengelompokkan data ke dalam node MySQL berbeda yang tidak mengetahui apa pun tentang satu sama lain.

Peningkatan otomatis bisa berbahaya

AUTOINCREMENT adalah cara umum untuk menghasilkan kunci utama. Seringkali ada kasus ketika database digunakan sebagai pembuat ID, dan database berisi tabel yang dirancang untuk menghasilkan pengidentifikasi. Ada beberapa alasan mengapa pembuatan kunci utama menggunakan penambahan otomatis masih jauh dari ideal:

  • Dalam database terdistribusi, penambahan otomatis adalah masalah serius. Untuk menghasilkan ID, diperlukan kunci global. Sebagai gantinya, Anda dapat membuat UUID: ini tidak memerlukan interaksi antara node database yang berbeda. Peningkatan otomatis dengan kunci dapat menimbulkan pertentangan dan secara signifikan mengurangi kinerja sisipan dalam situasi terdistribusi. Beberapa DBMS (misalnya MySQL) mungkin memerlukan konfigurasi khusus dan perhatian yang lebih cermat untuk mengatur replikasi master-master dengan benar. Dan mudah untuk membuat kesalahan saat mengonfigurasi, yang akan menyebabkan kegagalan perekaman.
  • Beberapa database memiliki algoritma partisi berdasarkan kunci utama. ID yang berurutan dapat menyebabkan hot spot yang tidak dapat diprediksi dan meningkatkan beban pada beberapa partisi sementara partisi lainnya tetap menganggur.
  • Kunci utama adalah cara tercepat untuk mengakses baris dalam database. Dengan cara yang lebih baik untuk mengidentifikasi catatan, ID berurutan dapat mengubah kolom terpenting dalam tabel menjadi kolom tidak berguna yang diisi dengan nilai tidak berarti. Oleh karena itu, bila memungkinkan, silakan pilih kunci utama yang unik dan alami secara global (misalnya nama pengguna).

Sebelum memutuskan suatu pendekatan, pertimbangkan dampak penambahan ID dan UUID secara otomatis pada pengindeksan, partisi, dan sharding.

Data basi dapat berguna dan tidak perlu dikunci

Kontrol Konkurensi Multiversi (MVCC) mengimplementasikan banyak persyaratan konsistensi yang telah dibahas secara singkat di atas. Beberapa database (misalnya, Postgres, Spanner) menggunakan MVCC untuk “mengumpankan” transaksi dengan snapshot—versi database yang lebih lama. Transaksi snapshot juga dapat diserialkan untuk memastikan konsistensi. Saat membaca dari snapshot lama, data usang dibaca.

Membaca data yang sedikit usang dapat berguna, misalnya, saat membuat analisis dari data atau menghitung perkiraan nilai agregat.

Keuntungan pertama bekerja dengan data lama adalah latensi yang rendah (terutama jika database didistribusikan di berbagai wilayah geografis). Yang kedua adalah transaksi read-only bebas kunci. Ini merupakan keuntungan yang signifikan untuk aplikasi yang banyak membaca, asalkan dapat menangani data lama.

Lebih banyak pengembang harus mengetahui hal ini tentang database
Server aplikasi membaca data dari replika lokal yang kedaluwarsa 5 detik, meskipun versi terbaru tersedia di seberang Samudra Pasifik

DBMS secara otomatis membersihkan versi lama dan, dalam beberapa kasus, memungkinkan Anda melakukan ini berdasarkan permintaan. Misalnya, Postgres memungkinkan pengguna melakukan hal tersebut VACUUM berdasarkan permintaan, dan juga secara berkala melakukan operasi ini secara otomatis. Spanner menjalankan pengumpul sampah untuk membuang snapshot yang lebih lama dari satu jam.

Sumber waktu mana pun dapat mengalami distorsi

Rahasia terbaik dalam ilmu komputer adalah bahwa semua API pengatur waktu berbohong. Faktanya, mesin kami tidak mengetahui waktu saat ini secara pasti. Komputer mengandung kristal kuarsa yang menghasilkan getaran yang digunakan untuk menjaga waktu. Namun, waktu tersebut tidak cukup akurat dan mungkin mendahului/tertinggal dari waktu yang tepat. Pergeserannya bisa mencapai 20 detik per hari. Oleh karena itu, waktu di komputer kita harus disinkronkan secara berkala dengan waktu di jaringan.

Server NTP digunakan untuk sinkronisasi, namun proses sinkronisasi itu sendiri dapat mengalami penundaan jaringan. Bahkan sinkronisasi dengan server NTP di pusat data yang sama memerlukan waktu. Jelas bahwa bekerja dengan server NTP publik dapat menyebabkan distorsi yang lebih besar.

Jam atom dan GPS-nya lebih baik dalam menentukan waktu saat ini, tetapi harganya mahal dan memerlukan pengaturan yang rumit, sehingga tidak dapat dipasang di setiap mobil. Oleh karena itu, pusat data menggunakan pendekatan berjenjang. Jam atom dan/atau GPS menunjukkan waktu yang tepat, setelah itu disiarkan ke mesin lain melalui server sekunder. Artinya setiap mesin akan mengalami offset tertentu dari waktu yang tepat.

Situasi ini diperburuk oleh kenyataan bahwa aplikasi dan database sering kali berlokasi di mesin yang berbeda (jika tidak di pusat data yang berbeda). Dengan demikian, waktunya akan berbeda tidak hanya pada node DB yang didistribusikan ke berbagai mesin. Ini juga akan berbeda di server aplikasi.

Google TrueTime mengambil pendekatan yang sangat berbeda. Kebanyakan orang percaya bahwa kemajuan Google dalam arah ini disebabkan oleh transisi dangkal ke jam atom dan GPS, tetapi ini hanya sebagian dari gambaran besarnya. Berikut cara kerja TrueTime:

  • TrueTime menggunakan dua sumber berbeda: GPS dan jam atom. Jam-jam ini memiliki mode kegagalan yang tidak berkorelasi. [lihat halaman 5 untuk rinciannya di sini - kira-kira. terjemahkan), sehingga penggunaan gabungannya meningkatkan keandalan.
  • TrueTime memiliki API yang tidak biasa. Ia mengembalikan waktu sebagai interval dengan kesalahan pengukuran dan ketidakpastian yang ada di dalamnya. Momen waktu sebenarnya berada di antara batas atas dan batas bawah interval. Spanner, database terdistribusi Google, cukup menunggu hingga waktu saat ini dipastikan berada di luar jangkauan. Metode ini menimbulkan beberapa latensi ke dalam sistem, terutama jika ketidakpastian pada master tinggi, namun memastikan kebenaran bahkan dalam situasi terdistribusi secara global.

Lebih banyak pengembang harus mengetahui hal ini tentang database
Komponen Spanner menggunakan TrueTime, di mana TT.now() mengembalikan interval, sehingga Spanner hanya tidur hingga titik di mana ia dapat yakin bahwa waktu saat ini telah melewati titik tertentu

Berkurangnya akurasi dalam menentukan waktu saat ini berarti peningkatan durasi operasi Spanner dan penurunan kinerja. Inilah sebabnya mengapa penting untuk menjaga akurasi setinggi mungkin meskipun tidak mungkin mendapatkan jam tangan yang benar-benar akurat.

Keterlambatan mempunyai banyak arti

Jika Anda bertanya kepada belasan ahli tentang apa itu penundaan, Anda mungkin akan mendapatkan jawaban berbeda. Dalam DBMS, latensi sering disebut "latensi basis data" dan berbeda dengan apa yang dirasakan oleh klien. Faktanya adalah klien mengamati jumlah penundaan jaringan dan penundaan basis data. Kemampuan untuk mengisolasi jenis latensi sangat penting saat melakukan debug pada masalah yang berkembang. Saat mengumpulkan dan menampilkan metrik, usahakan selalu memperhatikan kedua jenis tersebut.

Persyaratan kinerja harus dievaluasi untuk transaksi tertentu

Terkadang karakteristik kinerja DBMS dan batasannya ditentukan dalam hal throughput tulis/baca dan latensi. Hal ini memberikan gambaran umum tentang parameter sistem utama, namun ketika mengevaluasi kinerja DBMS baru, pendekatan yang jauh lebih komprehensif adalah dengan mengevaluasi operasi penting secara terpisah (untuk setiap kueri dan/atau transaksi). Contoh:

  • Tulis throughput dan latensi saat menyisipkan baris baru ke dalam tabel X (dengan 50 juta baris) dengan batasan tertentu dan bantalan baris di tabel terkait.
  • Keterlambatan dalam menampilkan teman dari teman pengguna tertentu ketika jumlah rata-rata teman adalah 500.
  • Latensi dalam mengambil 100 entri teratas dari riwayat pengguna ketika pengguna mengikuti 500 pengguna lain dengan X entri per jam.

Evaluasi dan eksperimen dapat mencakup kasus-kasus kritis sampai Anda yakin bahwa database memenuhi persyaratan kinerja. Aturan praktis serupa juga mempertimbangkan pengelompokan ini saat mengumpulkan metrik latensi dan menentukan SLO.

Waspadai kardinalitas tinggi saat mengumpulkan metrik untuk setiap operasi. Gunakan log, pengumpulan peristiwa, atau penelusuran terdistribusi untuk mendapatkan data debug berdaya tinggi. Di dalam artikel "Ingin Men-debug Latensi?» Anda dapat membiasakan diri dengan metodologi debugging penundaan.

Transaksi bertumpuk bisa berbahaya

Tidak semua DBMS mendukung transaksi bersarang, namun jika mendukung, transaksi tersebut dapat mengakibatkan kesalahan tak terduga yang tidak selalu mudah dideteksi (yaitu, jelas bahwa ada semacam anomali).

Anda dapat menghindari penggunaan transaksi bertumpuk menggunakan pustaka klien yang dapat mendeteksi dan melewatinya. Jika transaksi yang disarangkan tidak dapat ditinggalkan, berhati-hatilah dalam penerapannya untuk menghindari situasi yang tidak terduga di mana transaksi yang sudah selesai secara tidak sengaja dibatalkan karena transaksi yang disarangkan.

Mengenkapsulasi transaksi dalam lapisan yang berbeda dapat menyebabkan transaksi bersarang yang tidak terduga, dan dari sudut pandang keterbacaan kode, hal ini dapat mempersulit pemahaman maksud pembuatnya. Perhatikan program berikut ini:

with newTransaction():
   Accounts.create("609-543-222")
   with newTransaction():
       Accounts.create("775-988-322")
       throw Rollback();

Apa keluaran dari kode di atas? Apakah ini akan mengembalikan kedua transaksi, atau hanya transaksi internal saja? Apa yang terjadi jika kita mengandalkan perpustakaan berlapis-lapis yang merangkum pembuatan transaksi untuk kita? Akankah kita dapat mengidentifikasi dan memperbaiki kasus-kasus seperti itu?

Bayangkan sebuah lapisan data dengan banyak operasi (mis. newAccount) sudah diterapkan dalam transaksinya sendiri. Apa yang terjadi jika Anda menjalankannya sebagai bagian dari logika bisnis tingkat tinggi yang dijalankan dalam transaksinya sendiri? Bagaimana isolasi dan konsistensi dalam kasus ini?

function newAccount(id string) {
  with newTransaction():
      Accounts.create(id)
}

Daripada mencari jawaban atas pertanyaan yang tak ada habisnya, lebih baik hindari transaksi bertumpuk. Lagi pula, lapisan data Anda dapat dengan mudah melakukan operasi tingkat tinggi tanpa membuat transaksinya sendiri. Selain itu, logika bisnis itu sendiri mampu memulai suatu transaksi, melakukan operasi padanya, melakukan atau membatalkan suatu transaksi.

function newAccount(id string) {
   Accounts.create(id)
}
// In main application:
with newTransaction():
   // Read some data from database for configuration.
   // Generate an ID from the ID service.
   Accounts.create(id)
   Uploads.create(id) // create upload queue for the user.

Transaksi tidak boleh dikaitkan dengan status aplikasi

Terkadang tergoda untuk menggunakan status aplikasi dalam transaksi untuk mengubah nilai tertentu atau mengubah parameter kueri. Nuansa penting yang perlu dipertimbangkan adalah cakupan penerapan yang benar. Klien sering memulai kembali transaksi ketika ada masalah jaringan. Jika transaksi kemudian bergantung pada keadaan yang diubah oleh beberapa proses lain, transaksi mungkin memilih nilai yang salah tergantung pada kemungkinan terjadinya data race. Transaksi harus mempertimbangkan risiko kondisi data race pada aplikasi.

var seq int64
with newTransaction():
    newSeq := atomic.Increment(&seq)
    Entries.query(newSeq)
    // Other operations...

Transaksi di atas akan menambah nomor urut setiap kali dieksekusi, apa pun hasil akhirnya. Jika penerapan gagal karena masalah jaringan, permintaan akan dieksekusi dengan nomor urut yang berbeda saat Anda mencoba lagi.

Perencana kueri dapat memberi tahu Anda banyak hal tentang database

Perencana kueri menentukan bagaimana kueri akan dieksekusi dalam database. Mereka juga menganalisis permintaan dan mengoptimalkannya sebelum mengirimkannya. Para perencana hanya dapat memberikan beberapa kemungkinan perkiraan berdasarkan sinyal-sinyal yang mereka miliki. Misalnya, apa metode pencarian terbaik untuk kueri berikut?

SELECT * FROM articles where author = "rakyll" order by title;

Hasilnya dapat diambil dengan dua cara:

  • Pemindaian tabel lengkap: Anda dapat melihat setiap entri dalam tabel dan mengembalikan artikel dengan nama penulis yang cocok, lalu mengurutkannya.
  • Pemindaian indeks: Anda dapat menggunakan indeks untuk menemukan ID yang cocok, mendapatkan baris tersebut, lalu mengurutkannya.

Tugas perencana kueri adalah menentukan strategi mana yang terbaik. Perlu dipertimbangkan bahwa perencana kueri hanya memiliki kemampuan prediktif yang terbatas. Hal ini dapat menyebabkan keputusan yang buruk. DBA atau pengembang dapat menggunakannya untuk mendiagnosis dan menyempurnakan kueri yang berkinerja buruk. Versi baru DBMS dapat mengonfigurasi perencana kueri, dan diagnosis mandiri dapat membantu saat memperbarui database jika versi baru menyebabkan masalah kinerja. Log kueri yang lambat, laporan masalah latensi, atau statistik waktu eksekusi dapat membantu mengidentifikasi kueri yang memerlukan pengoptimalan.

Beberapa metrik yang disajikan oleh perencana kueri mungkin mengalami gangguan (terutama saat memperkirakan latensi atau waktu CPU). Tambahan yang bagus untuk penjadwal adalah alat untuk melacak dan melacak jalur eksekusi. Mereka memungkinkan Anda untuk mendiagnosis masalah tersebut (sayangnya, tidak semua DBMS menyediakan alat seperti itu).

Migrasi online sulit dilakukan, tetapi mungkin dilakukan

Migrasi online, migrasi langsung, atau migrasi waktu nyata berarti berpindah dari satu database ke database lainnya tanpa waktu henti atau kerusakan data. Migrasi langsung lebih mudah dilakukan jika transisi terjadi dalam DBMS/mesin yang sama. Situasi menjadi lebih rumit ketika perlu berpindah ke DBMS baru dengan kinerja dan persyaratan skema yang berbeda.

Ada berbagai model migrasi online. Ini salah satunya:

  • Aktifkan entri ganda di kedua database. Database baru pada tahap ini belum memiliki semua data, namun hanya menerima data terbaru. Setelah Anda yakin akan hal ini, Anda dapat melanjutkan ke langkah berikutnya.
  • Aktifkan pembacaan dari kedua database.
  • Konfigurasikan sistem sehingga pembacaan dan penulisan dilakukan terutama pada database baru.
  • Berhenti menulis ke database lama sambil terus membaca data darinya. Pada tahap ini, database baru masih kekurangan beberapa data. Mereka harus disalin dari database lama.
  • Basis data lama bersifat hanya baca. Salin data yang hilang dari database lama ke database baru. Setelah migrasi selesai, alihkan jalur ke database baru, hentikan database lama, lalu hapus database tersebut dari sistem.

Untuk informasi tambahan, saya sarankan menghubungi Artikel, yang merinci strategi migrasi Stripe berdasarkan model ini.

Peningkatan signifikan dalam database menyebabkan peningkatan ketidakpastian

Pertumbuhan database menyebabkan masalah yang tidak dapat diprediksi terkait dengan skalanya. Semakin banyak yang kita ketahui tentang struktur internal database, semakin baik kita dapat memprediksi skalanya. Namun, beberapa momen masih sulit diramalkan.
Seiring bertambahnya basis, asumsi dan ekspektasi sebelumnya mengenai volume data dan kebutuhan bandwidth jaringan mungkin menjadi ketinggalan jaman. Hal ini terjadi ketika muncul pertanyaan tentang perombakan desain besar-besaran, perbaikan operasional skala besar, memikirkan ulang penerapan, atau migrasi ke DBMS lain untuk menghindari potensi masalah.

Namun jangan berpikir bahwa pengetahuan yang baik tentang struktur internal database yang ada adalah satu-satunya hal yang diperlukan. Skala baru akan membawa serta hal-hal baru yang tidak diketahui. Masalah yang tidak dapat diprediksi, distribusi data yang tidak merata, masalah bandwidth dan perangkat keras yang tidak terduga, lalu lintas yang terus meningkat, dan segmen jaringan baru akan memaksa Anda memikirkan kembali pendekatan database, model data, model penerapan, dan ukuran database.

...

Saat saya mulai berpikir untuk menerbitkan artikel ini, sudah ada lima item lagi di daftar asli saya. Lalu datanglah sejumlah besar orang ide baru tentang apa lagi yang bisa dibahas. Oleh karena itu, artikel ini menyentuh masalah-masalah paling tidak jelas yang memerlukan perhatian maksimal. Namun, ini tidak berarti bahwa topik tersebut telah habis dan saya tidak akan kembali lagi ke topik tersebut di materi mendatang dan tidak akan membuat perubahan pada materi saat ini.

PS

Baca juga di blog kami:

Sumber: www.habr.com

Tambah komentar