Citymobil - manual untuk meningkatkan ketersediaan di tengah pertumbuhan bisnis untuk startup. Bagian 4



Ini adalah artikel selanjutnya dari seri yang menggambarkan bagaimana kami meningkatkan ketersediaan layanan kami di Citymobil (Anda dapat membaca bagian sebelumnya di sini: bagian 1 , bagian 2 , bagian 3 ). Di bagian selanjutnya, saya akan berbicara tentang kecelakaan dan pemadaman secara rinci.

1. Rilis buruk: kelebihan database


Mari saya mulai dengan contoh spesifik dari jenis pemadaman ini. Kami menerapkan optimasi: menambahkan USE INDEX dalam kueri SQL; selama pengujian serta dalam produksi, itu mempercepat pertanyaan pendek, tetapi yang panjang - melambat. Perlambatan permintaan panjang hanya diperhatikan dalam produksi. Akibatnya, banyak permintaan paralel yang panjang menyebabkan database menjadi turun selama satu jam. Kami mempelajari dengan seksama cara kerja USE INDEX; kami menggambarkannya dalam file Do dan Dont dan memperingatkan para insinyur terhadap penggunaan yang salah. Kami juga menganalisis kueri dan menyadari bahwa sebagian besar data historis diambil dan, karenanya, dapat dijalankan pada replika terpisah untuk permintaan historis. Bahkan jika replika ini turun karena kelebihan beban, bisnis akan tetap berjalan.

Kami terus menemukan masalah yang sama sesudahnya, dan pada titik tertentu, kami memutuskan untuk mengatasi masalah ini. Kami telah mempelajari kode melalui dan memindahkan semua pertanyaan yang kami bisa tanpa mengorbankan layanan kami ke replika. Replika itu sendiri dibagi tergantung pada tingkat kekritisannya, sehingga tidak ada yang bisa gagal dan menghentikan layanan. Hasilnya, kami membuat arsitektur dengan basis data berikut:

  • master database - untuk operasi penulisan dan kueri yang super sensitif terhadap kesegaran data;
  • replika produksi - untuk pertanyaan pendek yang kurang sensitif terhadap kesegaran data;
  • replika untuk koefisien lonjakan harga. Replika ini bisa 30-60 detik di belakang; itu tidak penting karena koefisien tidak sering berubah dan jika replika ini turun, layanan tidak akan berhenti bekerja; harga tidak akan sepenuhnya cocok dengan keseimbangan penawaran dan permintaan;
  • replika untuk pengaturan operasional dan pusat panggilan. Jika macet, bisnis akan tetap berjalan tetapi tanpa dukungan pelanggan dan driver, dan kami tidak akan dapat mengubah sementara beberapa pengaturan;
  • banyak replika untuk analisis dan dasbor ad hoc;
  • Basis data MPP (pemrosesan paralel masif) untuk beberapa analitik besar dengan irisan lengkap pada data historis.

Arsitektur ini memberi kami ruang luas untuk pertumbuhan dan mengurangi sejumlah kerusakan karena kueri SQL yang tidak optimal. Tapi itu masih jauh dari sempurna. Kami berencana untuk mengimplementasikan sharding, sehingga kami dapat meningkatkan dan menghapus pembaruan serta sangat sensitif terhadap kueri kesegaran data. Margin keamanan MySQL tidak terbatas. Kita akan membutuhkan beberapa artileri berat dalam bentuk beberapa basis data dalam memori (mis. Tarantool) segera. Saya pasti akan membicarakannya di artikel saya berikutnya.

Sementara kami berhadapan dengan kode dan kueri yang tidak optimal, kami memahami hal-hal berikut: segala yang tidak optimal harus dihilangkan sebelum dirilis dan bukan setelahnya. Ini mengurangi risiko pemadaman dan upaya tim rekayasa untuk optimisasi. Jika kode sudah dikerahkan dengan beberapa rilis baru di atasnya, itu jauh lebih sulit untuk mengoptimalkannya. Sebagai hasilnya, kami memperkenalkan tinjauan kode wajib untuk optimisasi. Ini dilakukan oleh para insinyur kami yang paling berpengalaman, kekuatan elit kami.

Kami juga mulai mengumpulkan metode optimisasi kode terbaik yang sesuai untuk realia kami di Do's dan Dont's. Mereka tercantum di bawah ini. Tolong, jangan menganggap praktik-praktik ini sebagai kebenaran yang tidak dapat disangkal dan jangan mencoba meniru mereka secara membabi buta. Setiap metode hanya masuk akal untuk beberapa situasi tertentu dan bisnis tertentu. Ini hanya contoh untuk memperjelas spesifik:

  • Jika kueri SQL tidak bergantung pada pengguna (misalnya, peta permintaan pengemudi dengan harga maksimum dan koefisien lonjakan - peta ini sama untuk setiap pengguna aplikasi driver), maka kueri ini harus dilakukan dalam skrip cron dengan beberapa frekuensi tertentu (satu menit sekali sudah cukup dalam kasus itu). Hasilnya harus ditulis dalam cache (Memcached atau Redis) yang harus digunakan dalam kode produksi.
  • Jika kueri SQL beroperasi dengan data yang keterlambatannya tidak penting untuk bisnis, maka hasilnya harus ditempatkan dalam cache dengan beberapa TTL (misalnya 30 detik) dan kemudian dibaca dari cache dengan kode produksi.
  • Jika dalam implementasi metode server tertentu (dalam PHP atau bahasa sisi-server lainnya) Anda memutuskan untuk membuat kueri SQL, Anda harus memastikan bahwa data yang Anda butuhkan belum tiba dengan beberapa kueri SQL lain atau akan tiba di kode Anda di bawah ini.
  • Sama seperti di atas berlaku untuk permintaan ke cache. Cache cepat tetapi masih merupakan database. Jadi bisa juga kelebihan beban. Kesalahan umum adalah Anda berpikir bahwa cache adalah semacam variabel dalam-memori normal dan menggunakannya sebagai variabel. Tetapi akses ke sana melibatkan perjalanan pulang-pergi jaringan dan menghasilkan beban kerja ke Redis atau Memcached. Karena itu, jika data telah tiba dari cache maka jangan hanya mengambil dari cache apa yang sudah diambil.
  • Jika selama pemrosesan permintaan web (lagi dalam PHP atau bahasa lain) Anda perlu memanggil suatu fungsi, Anda perlu memastikan tidak akan ada query SQL tambahan atau akses cache yang dibuat di dalamnya. Jika panggilan fungsi seperti itu tidak bisa dihindari, Anda harus memastikan bahwa itu tidak dapat dimodifikasi, atau logikanya tidak rusak untuk mencegah permintaan basis data / cache yang tidak perlu.
  • Jika perlu melakukan kueri SQL, Anda harus benar-benar yakin bahwa bidang yang Anda butuhkan tidak dapat ditambahkan ke kueri yang sudah ada dalam kode Anda di atas atau di bawah.

2. Operasi manual yang buruk


Contoh kecelakaan ini:

  • bad ALTER yang membebani database atau menyebabkan penundaan replika;
  • bad DROP (mis., kami menemukan bug di MySQL yang memblokir database sementara menjatuhkan tabel);
  • pertanyaan berat pada master, dibuat secara manual karena kesalahan;
  • server web sedang dikonfigurasi meskipun itu di bawah beban kerja nyata sedangkan kami pikir itu di luar operasi.

Untuk meminimalkan pemadaman karena alasan ini, kami harus menyelidiki sifat kecelakaan setiap kali terjadi. Kami belum menemukan aturan umum. Sekali lagi, mari kita lihat beberapa contoh spesifik. Koefisien lonjakan (ongkos taksi pada waktu dan tempat permintaan tinggi dikalikan dengan mereka) berhenti bekerja di beberapa titik. Alasannya adalah bahwa ada skrip python yang bekerja pada server database replika di mana data untuk perhitungan koefisien diambil dan skrip menghabiskan semua memori dan replika turun. Script telah berjalan beberapa saat; itu beroperasi tepat di replika demi kenyamanan. Masalahnya dipecahkan dengan me-restart script. Kesimpulan berikut ini diambil: jangan menjalankan skrip asing di server database (itu ditulis dalam Do's and Don't's; jika tidak, itu akan menjadi bidikan kosong!), Pantau penggunaan memori pada server database dan beri tahu melalui SMS jika server itu hampir kehabisan memori.

Sangat penting untuk selalu menarik kesimpulan dan tidak merasa nyaman dalam situasi "melihat masalah, memperbaikinya, melupakannya". Layanan berkualitas hanya dapat ditawarkan jika orang pergi dengan kesimpulan. Selain itu, peringatan SMS sangat penting - mereka meningkatkan tingkat kualitas layanan, mereka tidak membiarkannya turun dan memungkinkan kami untuk meningkatkan keandalannya. Seperti pendaki gunung yang mencapai posisi stabil dan kemudian menarik dirinya ke posisi stabil lain, tetapi kali ini lebih tinggi.

Pemantauan dan peringatan tidak terlihat, tetapi mereka bertindak seperti kait besi yang memotong batu yang tidak diketahui mencegah kami jatuh di bawah perjanjian tingkat layanan kami yang terus kami tingkatkan.

3. Telur paskah


Apa yang kita sebut "telur Paskah" - adalah ranjau aksi tertunda yang belum pernah kita lakukan, meskipun sudah ada untuk sementara waktu. Di luar artikel ini, istilah ini digunakan untuk fitur tidak berdokumen yang dibuat dengan sengaja. Dalam kasus kami, ini bukan fitur sama sekali, melainkan bug yang bertindak seperti bom waktu dan muncul sebagai efek samping dari beberapa aktivitas yang bermaksud baik.

Sebagai contoh:

  • pengisian berlebih dari auto_increment 32-bit;
  • tidak optimalnya kode / konfigurasi, dipicu oleh beban kerja yang tinggi;
  • replika tertunda oleh kueri tidak optimal yang disebabkan oleh pola penggunaan baru atau oleh beban kerja yang lebih berat;
  • menunda replika oleh operasi UPDATE yang tidak optimal pada master yang disebabkan oleh pola beban kerja baru dan menunda replikasi.

Jenis lain dari telur Paskah yang populer adalah kode yang tidak optimal; menjadi lebih spesifik - query SQL tidak optimal. Tabel yang digunakan lebih kecil dan beban kerjanya lebih ringan - kueri bekerja dengan baik. Dengan pertumbuhan tabel waktu linier dan peningkatan beban kerja linier, konsumsi sumber daya oleh sistem manajemen basis data tumbuh secara kuadrat. Biasanya, itu mengarah ke efek negatif yang drastis: seperti semua dulu ok dan tiba-tiba - oops!

Skenario yang lebih jarang - kombinasi bug dan telur Paskah. Rilis dengan bug menyebabkan pembesaran tabel database dan meningkatkan jumlah baris tabel dari jenis tertentu sementara telur Paskah yang sudah ada menyebabkan kelebihan beban basis data karena permintaan yang lebih lambat ke tabel yang diperluas ini.

Kami dulu memiliki beberapa telur Paskah yang tidak berhubungan dengan beban kerja. Misalnya, bidang auto_increment 32-bit di MYSQL. Setelah lebih dari 2 milyar baris, sisipan gagal. Oleh karena itu, di dunia modern, kita harus menggunakan bidang auto_increment 64-bit saja. Kami belajar pelajaran itu dengan baik.

Bagaimana cara mengatasi telur Paskah? Jawabannya terdengar langsung: a) mencari telur yang lama, b) jangan biarkan yang baru muncul. Kami berusaha melakukan keduanya. Pencarian untuk telur lama berjalan seiring dengan optimasi kode berkelanjutan kami. Kami menunjuk dua insinyur paling berpengalaman untuk melakukan optimasi hampir penuh waktu. Mereka menemukan pertanyaan di slow.log yang paling banyak menggunakan sumber daya basis data; mereka mengoptimalkan kueri ini dan kode di sekitarnya. Kami menurunkan kemungkinan munculnya telur baru melalui pengujian setiap komit untuk optimalitas yang dilakukan oleh para insinyur sensei yang disebutkan di atas. Tugas mereka adalah menunjukkan kesalahan yang mempengaruhi kinerja, untuk menyarankan cara membuat segalanya lebih baik dan meneruskan pengetahuan ini ke insinyur lain.

Pada titik tertentu, tepat setelah menemukan telur Paskah yang lain, kami menyadari bahwa mencari pertanyaan yang lambat adalah hal yang baik, tetapi kami juga harus mencari pertanyaan yang tampaknya lambat tetapi bekerja dengan cepat. Ini adalah pesaing berikutnya untuk menabrak segala sesuatu dalam kasus pertumbuhan meja ledakan lainnya. Contoh bodoh tapi jelas di sini adalah kueri yang penuh memindai tabel 10 baris tanpa menggunakan indeks sama sekali. Ini akan bekerja cepat untuk saat ini. Namun, ketika tabel cukup besar, maka kueri akan menurunkan basis data. Itu telur paskah.

4. Penyebab eksternal


Ini adalah penyebab yang tampaknya tidak dapat kita kontrol dengan baik. Dengan kata lain, itu adalah penyebab yang hanya bisa dikurangi tetapi tidak dihilangkan. Sebagai contoh:

  • Melambatkan permintaan kami oleh penyedia layanan peta. Ini dapat dikurangi melalui kontrol penggunaan layanan, kepatuhan pada tingkat beban kerja tertentu, perencanaan peningkatan beban kerja sebelumnya dan pembelian perluasan layanan. Namun, kami tidak dapat melakukannya tanpa peta.
  • Kegagalan jaringan di pusat data. Ini dapat dikurangi dengan menempatkan salinan layanan di pusat data cadangan. Namun, kami tidak dapat melakukannya tanpa pusat data baik fisik maupun cloud.
  • Layanan pembayaran turun. Ini dapat dikurangi dengan cadangan layanan pembayaran. Namun, kami tidak dapat melakukannya tanpa pembayaran.
  • Pemblokiran lalu lintas yang salah oleh layanan perlindungan DDoS. Itu dapat dikurangi dengan mematikan layanan perlindungan DDoS secara default dengan menyalakannya hanya dalam kasus serangan DDoS. Namun, kami tidak dapat melakukannya tanpa perlindungan DDoS.

Karena bahkan mitigasi penyebab eksternal adalah upaya yang panjang dan mahal, kami mulai mengumpulkan statistik untuk kecelakaan yang disebabkan oleh alasan eksternal dan menunggu akumulasi massa kritis. Kami tidak memiliki resep untuk mendefinisikan massa kritis. Ini semua tentang intuisi belaka. Sebagai contoh, jika kita benar-benar turun lima kali karena, katakanlah, masalah layanan perlindungan DDoS, maka dengan setiap downtime berikutnya, kebutuhan akan alternatif akan menjadi semakin dan semakin akut.

Di sisi lain, jika kita bisa membuat semuanya berfungsi dengan layanan eksternal yang tidak tersedia, kita pasti melakukannya. Analisis post-mortem dari setiap pemadaman membantu kita di sini. Harus selalu ada kesimpulan. Yang berarti suka atau tidak suka - kita selalu menemukan solusi.



Pada bagian terakhir, saya akan berbicara tentang satu lagi jenis pemadaman dan kesimpulan yang kami buat tentang mereka, bagaimana kami memodifikasi proses pengembangan, otomatisasi apa yang kami perkenalkan. Tetap disini!

Source: https://habr.com/ru/post/id450678/


All Articles