SQL Baru = NoSQL+ACID

SQL Baru = NoSQL+ACID
Hingga saat ini, Odnoklassniki menyimpan sekitar 50 TB data yang diproses secara real-time di SQL Server. Untuk volume sebesar itu, hampir tidak mungkin untuk menyediakan akses yang cepat dan andal, dan bahkan toleran terhadap kegagalan pusat data menggunakan DBMS SQL. Biasanya, dalam kasus seperti itu, salah satu penyimpanan NoSQL digunakan, tetapi tidak semuanya dapat ditransfer ke NoSQL: beberapa entitas memerlukan jaminan transaksi ACID.

Hal ini mengarahkan kami pada penggunaan penyimpanan NewSQL, yaitu DBMS yang memberikan toleransi kesalahan, skalabilitas, dan kinerja sistem NoSQL, namun pada saat yang sama mempertahankan jaminan ACID yang familiar dengan sistem klasik. Hanya ada sedikit sistem industri kelas baru ini yang berfungsi, jadi kami sendiri yang menerapkan sistem tersebut dan menjalankannya secara komersial.

Cara kerjanya dan apa yang terjadi - baca di bawah.

Saat ini, pemirsa bulanan Odnoklassniki berjumlah lebih dari 70 juta pengunjung unik. Kami Kami berada di lima besar jejaring sosial terbesar di dunia, dan di antara dua puluh situs tempat pengguna menghabiskan waktu paling banyak. Infrastruktur OK menangani beban yang sangat tinggi: lebih dari satu juta permintaan HTTP/detik per front. Bagian dari armada server yang berjumlah lebih dari 8000 buah terletak berdekatan satu sama lain - di empat pusat data Moskow, yang memungkinkan untuk memastikan latensi jaringan kurang dari 1 ms di antara keduanya.

Kami telah menggunakan Cassandra sejak 2010, dimulai dengan versi 0.6. Saat ini ada beberapa lusin cluster yang beroperasi. Cluster tercepat memproses lebih dari 4 juta operasi per detik, dan cluster terbesar menyimpan 260 TB.

Namun, ini semua adalah cluster NoSQL biasa yang digunakan untuk penyimpanan terkoordinasi dengan lemah data. Kami ingin mengganti penyimpanan utama yang konsisten, Microsoft SQL Server, yang telah digunakan sejak berdirinya Odnoklassniki. Penyimpanannya terdiri dari lebih dari 300 mesin SQL Server Standard Edition, yang berisi 50 TB data - entitas bisnis. Data ini dimodifikasi sebagai bagian dari transaksi ACID dan memerlukan konsistensi tinggi.

Untuk mendistribusikan data ke seluruh node SQL Server, kami menggunakan vertikal dan horizontal partisi (pecahan). Secara historis, kami menggunakan skema sharding data sederhana: setiap entitas dikaitkan dengan token - fungsi dari ID entitas. Entitas dengan token yang sama ditempatkan di server SQL yang sama. Hubungan master-detail diterapkan sehingga token dari catatan utama dan anak selalu cocok dan ditempatkan di server yang sama. Di jejaring sosial, hampir semua catatan dibuat atas nama pengguna - yang berarti bahwa semua data pengguna dalam satu subsistem fungsional disimpan di satu server. Artinya, transaksi bisnis hampir selalu melibatkan tabel dari satu server SQL, yang memungkinkan untuk memastikan konsistensi data menggunakan transaksi ACID lokal, tanpa perlu menggunakan lambat dan tidak dapat diandalkan transaksi ACID terdistribusi.

Berkat sharding dan untuk mempercepat SQL:

  • Kami tidak menggunakan batasan kunci Asing, karena saat sharding, ID entitas mungkin berada di server lain.
  • Kami tidak menggunakan prosedur tersimpan dan pemicu karena beban tambahan pada CPU DBMS.
  • Kami tidak menggunakan GABUNG karena semua hal di atas dan banyak pembacaan acak dari disk.
  • Di luar transaksi, kami menggunakan tingkat isolasi Read Uncommit untuk mengurangi kebuntuan.
  • Kami hanya melakukan transaksi singkat (rata-rata lebih pendek dari 100 ms).
  • Kami tidak menggunakan UPDATE dan DELETE multi-baris karena banyaknya kebuntuan - kami hanya memperbarui satu catatan dalam satu waktu.
  • Kami selalu melakukan kueri hanya pada indeks - kueri dengan rencana pemindaian tabel lengkap bagi kami berarti membebani database secara berlebihan dan menyebabkannya gagal.

Langkah-langkah ini memungkinkan kami memaksimalkan kinerja server SQL. Namun, permasalahan yang ada semakin banyak. Mari kita lihat mereka.

Masalah dengan SQL

  • Karena kami menggunakan sharding yang ditulis sendiri, penambahan shard baru dilakukan secara manual oleh administrator. Selama ini, replika data yang dapat diskalakan tidak melayani permintaan.
  • Seiring bertambahnya jumlah catatan dalam tabel, kecepatan penyisipan dan modifikasi menurun; saat menambahkan indeks ke tabel yang ada, kecepatannya turun satu faktor; pembuatan dan pembuatan ulang indeks terjadi dengan waktu henti.
  • Memiliki sejumlah kecil Windows untuk SQL Server dalam produksi membuat manajemen infrastruktur menjadi sulit

Tapi masalah utamanya adalah

toleransi kesalahan

Server SQL klasik memiliki toleransi kesalahan yang buruk. Katakanlah Anda hanya memiliki satu server database, dan server tersebut gagal setiap tiga tahun sekali. Selama waktu ini, situs tidak aktif selama 20 menit, dan hal ini dapat diterima. Jika Anda memiliki 64 server, maka situs tersebut tidak aktif setiap tiga minggu sekali. Dan jika Anda memiliki 200 server, maka situs tersebut tidak berfungsi setiap minggunya. Ini masalah.

Apa yang bisa dilakukan untuk meningkatkan toleransi kesalahan server SQL? Wikipedia mengundang kita untuk membangun cluster yang sangat tersedia: dimana jika terjadi kegagalan pada salah satu komponen maka terdapat cadangannya.

Hal ini memerlukan armada peralatan yang mahal: banyak duplikasi, serat optik, penyimpanan bersama, dan penyertaan cadangan tidak dapat diandalkan: sekitar 10% peralihan berakhir dengan kegagalan node cadangan seperti kereta di belakang node utama.

Namun kelemahan utama dari cluster dengan ketersediaan tinggi ini adalah tidak adanya ketersediaan jika pusat data di mana cluster tersebut berada mengalami kegagalan. Odnoklassniki memiliki empat pusat data, dan kami perlu memastikan pengoperasian jika terjadi kegagalan total pada salah satu pusat data tersebut.

Untuk ini kita bisa menggunakan Multi-Master replikasi dibangun ke dalam SQL Server. Solusi ini jauh lebih mahal karena biaya perangkat lunak dan mengalami masalah replikasi yang umum - penundaan transaksi yang tidak dapat diprediksi dengan replikasi sinkron dan penundaan dalam penerapan replikasi (dan, akibatnya, kehilangan modifikasi) dengan replikasi asinkron. Yang tersirat penyelesaian konflik secara manual membuat opsi ini sama sekali tidak dapat diterapkan bagi kami.

Semua masalah ini memerlukan solusi radikal, dan kami mulai menganalisisnya secara mendetail. Di sini kita perlu mengenal apa yang terutama dilakukan SQL Server - transaksi.

Transaksi sederhana

Mari kita pertimbangkan transaksi paling sederhana, dari sudut pandang programmer SQL terapan: menambahkan foto ke album. Album dan foto disimpan di piring berbeda. Album ini memiliki penghitung foto publik. Kemudian transaksi tersebut dibagi menjadi beberapa langkah berikut:

  1. Kami mengunci album dengan kunci.
  2. Buat entri di tabel foto.
  3. Jika foto tersebut berstatus publik, tambahkan penghitung foto publik ke album, perbarui catatan, dan lakukan transaksi.

Atau dalam kodesemu:

TX.start("Albums", id);
Album album = albums.lock(id);
Photo photo = photos.create(…);

if (photo.status == PUBLIC ) {
    album.incPublicPhotosCount();
}
album.update();

TX.commit();

Kita melihat bahwa skenario paling umum untuk transaksi bisnis adalah membaca data dari database ke dalam memori server aplikasi, mengubah sesuatu dan menyimpan nilai baru kembali ke database. Biasanya dalam transaksi seperti itu kami memperbarui beberapa entitas, beberapa tabel.

Saat menjalankan suatu transaksi, modifikasi bersamaan atas data yang sama dari sistem lain dapat terjadi. Misalnya, Antispam mungkin memutuskan bahwa pengguna mencurigakan dan oleh karena itu semua foto pengguna tidak lagi bersifat publik, foto tersebut perlu dikirim untuk moderasi, yang berarti mengubah status foto ke nilai lain dan mematikan penghitung yang sesuai. Jelasnya, jika operasi ini terjadi tanpa jaminan atomisitas penerapan dan isolasi modifikasi yang bersaing, seperti pada ACID, maka hasilnya tidak akan sesuai dengan yang diperlukan - penghitung foto akan menampilkan nilai yang salah, atau tidak semua foto akan dikirim untuk moderasi.

Banyak kode serupa yang memanipulasi berbagai entitas bisnis dalam satu transaksi telah ditulis sepanjang keberadaan Odnoklassniki. Berdasarkan pengalaman migrasi ke NoSQL dari Konsistensi Akhirnya Kita tahu bahwa tantangan terbesar (dan investasi waktu) datang dari pengembangan kode untuk menjaga konsistensi data. Oleh karena itu, kami menganggap persyaratan utama penyimpanan baru adalah penyediaan transaksi ACID nyata untuk logika aplikasi.

Persyaratan lain yang tidak kalah pentingnya adalah:

  • Jika pusat data gagal, pembacaan dan penulisan ke penyimpanan baru harus tersedia.
  • Mempertahankan kecepatan pengembangan saat ini. Artinya, ketika bekerja dengan repositori baru, jumlah kode harus kira-kira sama; tidak perlu menambahkan apa pun ke repositori, mengembangkan algoritma untuk menyelesaikan konflik, memelihara indeks sekunder, dll.
  • Kecepatan penyimpanan baru harus cukup tinggi, baik saat membaca data maupun saat memproses transaksi, yang secara efektif berarti bahwa solusi yang secara akademis ketat, universal, namun lambat, seperti, misalnya, tidak dapat diterapkan. komitmen dua fase.
  • Penskalaan otomatis saat itu juga.
  • Menggunakan server biasa yang murah, tanpa perlu membeli perangkat keras eksotik.
  • Kemungkinan pengembangan penyimpanan oleh pengembang perusahaan. Dengan kata lain, prioritas diberikan pada solusi berpemilik atau sumber terbuka, terutama di Java.

Keputusan, keputusan

Menganalisis solusi yang mungkin, kami sampai pada dua kemungkinan pilihan arsitektur:

Yang pertama adalah menggunakan server SQL apa pun dan menerapkan toleransi kesalahan yang diperlukan, mekanisme penskalaan, kluster failover, resolusi konflik, dan transaksi ACID yang terdistribusi, andal, dan cepat. Kami menilai opsi ini sangat tidak sepele dan memakan waktu.

Opsi kedua adalah menggunakan penyimpanan NoSQL yang sudah jadi dengan penskalaan yang diterapkan, kluster failover, resolusi konflik, dan implementasi transaksi dan SQL sendiri. Pada pandangan pertama, bahkan tugas mengimplementasikan SQL, belum lagi transaksi ACID, tampak seperti tugas yang akan memakan waktu bertahun-tahun. Namun kemudian kami menyadari bahwa rangkaian fitur SQL yang kami gunakan dalam praktiknya jauh dari ANSI SQL Cassandra CQL jauh dari ANSI SQL. Melihat lebih dekat pada CQL, kami menyadari bahwa itu cukup dekat dengan apa yang kami butuhkan.

Cassandra dan CQL

Lantas, apa yang menarik dari Cassandra, kemampuan apa saja yang dimilikinya?

Pertama, di sini Anda bisa membuat tabel yang mendukung berbagai tipe data; Anda bisa melakukan SELECT atau UPDATE pada kunci utama.

CREATE TABLE photos (id bigint KEY, owner bigint,…);
SELECT * FROM photos WHERE id=?;
UPDATE photos SET … WHERE id=?;

Untuk memastikan konsistensi data replika, Cassandra menggunakan pendekatan kuorum. Dalam kasus paling sederhana, ini berarti bahwa ketika tiga replika dari baris yang sama ditempatkan pada node cluster yang berbeda, penulisan dianggap berhasil jika mayoritas node (yaitu, dua dari tiga) mengonfirmasi keberhasilan operasi penulisan ini. . Data baris dianggap konsisten jika, saat membaca, mayoritas node telah disurvei dan dikonfirmasi. Jadi, dengan tiga replika, konsistensi data yang lengkap dan instan terjamin jika satu node gagal. Pendekatan ini memungkinkan kami menerapkan skema yang lebih andal: selalu mengirimkan permintaan ke ketiga replika, menunggu respons dari dua replika tercepat. Respons yang terlambat dari replika ketiga akan dibuang dalam kasus ini. Sebuah node yang terlambat merespons mungkin mengalami masalah serius - rem, pengumpulan sampah di JVM, pengambilan kembali memori langsung di kernel Linux, kegagalan perangkat keras, pemutusan sambungan dari jaringan. Namun, hal ini tidak mempengaruhi operasi atau data klien dengan cara apa pun.

Pendekatan ketika kita menghubungi tiga node dan menerima respons dari dua node disebut spekulasi: permintaan replika tambahan dikirim bahkan sebelum replika tersebut “jatuh”.

Manfaat lain dari Cassandra adalah Batchlog, sebuah mekanisme yang memastikan bahwa sejumlah perubahan yang Anda buat diterapkan sepenuhnya atau tidak diterapkan sama sekali. Hal ini memungkinkan kita untuk menyelesaikan A dalam ACID - atomisitas di luar kotak.

Hal yang paling dekat dengan transaksi di Cassandra adalah apa yang disebut “transaksi ringan". Namun hal ini jauh dari transaksi ACID yang “nyata”: faktanya, ini adalah peluang untuk dilakukan CAS pada data hanya dari satu catatan, menggunakan konsensus menggunakan protokol kelas berat Paxos. Oleh karena itu, kecepatan transaksi tersebut rendah.

Apa yang kami lewatkan di Cassandra

Jadi, kami harus menerapkan transaksi ACID nyata di Cassandra. Dengan menggunakan ini kita dapat dengan mudah mengimplementasikan dua fitur nyaman lainnya dari DBMS klasik: indeks cepat yang konsisten, yang memungkinkan kita melakukan pemilihan data tidak hanya dengan kunci utama, dan generator reguler dari ID penambahan otomatis yang monoton.

Kerucut

Maka lahirlah DBMS baru Kerucut, terdiri dari tiga jenis node server:

  • Penyimpanan – (hampir) server Cassandra standar yang bertanggung jawab untuk menyimpan data pada disk lokal. Seiring bertambahnya beban dan volume data, kuantitasnya dapat dengan mudah ditingkatkan menjadi puluhan dan ratusan.
  • Koordinator transaksi - memastikan pelaksanaan transaksi.
  • Klien adalah server aplikasi yang mengimplementasikan operasi bisnis dan memulai transaksi. Mungkin ada ribuan klien seperti itu.

SQL Baru = NoSQL+ACID

Semua jenis server adalah bagian dari cluster umum, menggunakan protokol pesan Cassandra internal untuk berkomunikasi satu sama lain dan gosip untuk bertukar informasi cluster. Dengan Heartbeat, server belajar tentang kegagalan bersama, memelihara skema data tunggal - tabel, struktur dan replikasinya; skema partisi, topologi cluster, dll.

Pelanggan

SQL Baru = NoSQL+ACID

Alih-alih driver standar, mode Fat Client digunakan. Node tersebut tidak menyimpan data, tetapi dapat bertindak sebagai koordinator eksekusi permintaan, yaitu Klien sendiri yang bertindak sebagai koordinator permintaannya: ia menanyakan replika penyimpanan dan menyelesaikan konflik. Ini tidak hanya lebih andal dan lebih cepat daripada driver standar, yang memerlukan komunikasi dengan koordinator jarak jauh, namun juga memungkinkan Anda mengontrol transmisi permintaan. Di luar transaksi yang terbuka pada klien, permintaan dikirim ke repositori. Jika klien telah membuka transaksi, maka semua permintaan dalam transaksi tersebut dikirim ke koordinator transaksi.
SQL Baru = NoSQL+ACID

C*Satu Koordinator Transaksi

Koordinator adalah sesuatu yang kami terapkan untuk C*One dari awal. Bertanggung jawab untuk mengelola transaksi, kunci, dan urutan penerapan transaksi.

Untuk setiap transaksi yang dilayani, koordinator menghasilkan stempel waktu: setiap transaksi berikutnya lebih besar dari transaksi sebelumnya. Karena sistem penyelesaian konflik Cassandra didasarkan pada stempel waktu (dari dua catatan yang bertentangan, yang memiliki stempel waktu terbaru dianggap terkini), konflik tersebut akan selalu diselesaikan demi transaksi berikutnya. Demikianlah kami menerapkannya Jam tangan Lamport - cara murah untuk menyelesaikan konflik dalam sistem terdistribusi.

Kunci

Untuk memastikan isolasi, kami memutuskan untuk menggunakan metode paling sederhana - kunci pesimis berdasarkan kunci utama rekaman. Dengan kata lain, dalam suatu transaksi, suatu catatan harus dikunci terlebih dahulu, baru kemudian dibaca, diubah, dan disimpan. Hanya setelah penerapan berhasil, catatan dapat dibuka sehingga transaksi pesaing dapat menggunakannya.

Menerapkan penguncian seperti itu sederhana dalam lingkungan yang tidak terdistribusi. Dalam sistem terdistribusi, ada dua pilihan utama: menerapkan penguncian terdistribusi pada cluster, atau mendistribusikan transaksi sehingga transaksi yang melibatkan catatan yang sama selalu dilayani oleh koordinator yang sama.

Karena dalam kasus kami data sudah didistribusikan di antara kelompok transaksi lokal di SQL, diputuskan untuk menugaskan kelompok transaksi lokal ke koordinator: satu koordinator melakukan semua transaksi dengan token dari 0 hingga 9, yang kedua - dengan token dari 10 hingga 19, dan seterusnya. Hasilnya, masing-masing instance koordinator menjadi master grup transaksi.

Kemudian kunci dapat diimplementasikan dalam bentuk HashMap dangkal di memori koordinator.

Kegagalan koordinator

Karena satu koordinator secara eksklusif melayani sekelompok transaksi, sangat penting untuk segera menentukan fakta kegagalannya sehingga upaya kedua untuk mengeksekusi transaksi akan habis waktunya. Untuk menjadikannya cepat dan andal, kami menggunakan protokol dengar kuorum yang terhubung sepenuhnya:

Setiap pusat data menampung setidaknya dua node koordinator. Secara berkala, setiap koordinator mengirimkan pesan detak jantung ke koordinator lain dan memberi tahu mereka tentang fungsinya, serta pesan detak jantung mana yang terakhir kali diterima dari koordinator mana di cluster.

SQL Baru = NoSQL+ACID

Menerima informasi serupa dari orang lain sebagai bagian dari pesan detak jantung mereka, masing-masing koordinator memutuskan sendiri node cluster mana yang berfungsi dan mana yang tidak, dengan berpedoman pada prinsip kuorum: jika node X telah menerima informasi dari mayoritas node di cluster tentang kondisi normal. penerimaan pesan dari node Y, maka Y berfungsi. Dan sebaliknya, begitu mayoritas melaporkan pesan yang hilang dari node Y, maka Y menolak. Sangat mengherankan bahwa jika kuorum memberi tahu node X bahwa ia tidak lagi menerima pesan darinya, maka node X sendiri akan menganggap dirinya gagal.

Pesan detak jantung dikirimkan dengan frekuensi tinggi, sekitar 20 kali per detik, dengan jangka waktu 50 ms. Di Java, sulit untuk menjamin respons aplikasi dalam waktu 50 ms karena lamanya jeda yang disebabkan oleh pengumpul sampah sebanding. Kami dapat mencapai waktu respons ini menggunakan pengumpul sampah G1, yang memungkinkan kami menentukan target durasi jeda GC. Namun, terkadang, sangat jarang, jeda kolektor melebihi 50 ms, yang dapat menyebabkan kesalahan deteksi salah. Untuk mencegah hal ini terjadi, koordinator tidak melaporkan kegagalan node jarak jauh ketika pesan detak jantung pertama dari node tersebut hilang, hanya jika beberapa telah hilang berturut-turut. Beginilah cara kami berhasil mendeteksi kegagalan node koordinator pada tahun 200 MS.

Namun memahami dengan cepat node mana yang berhenti berfungsi saja tidaklah cukup. Kita perlu melakukan sesuatu mengenai hal ini.

Reservasi

Skema klasik melibatkan, jika terjadi kegagalan utama, memulai pemilihan baru menggunakan salah satu dari modis universal algoritma. Namun, algoritma seperti ini terkenal memiliki masalah dengan konvergensi waktu dan lamanya proses pemilu itu sendiri. Kami dapat menghindari penundaan tambahan tersebut dengan menggunakan skema penggantian koordinator di jaringan yang terhubung sepenuhnya:

SQL Baru = NoSQL+ACID

Katakanlah kita ingin melakukan transaksi di grup 50. Mari kita tentukan terlebih dahulu skema penggantiannya, yaitu node mana yang akan mengeksekusi transaksi di grup 50 jika terjadi kegagalan pada koordinator utama. Tujuan kami adalah menjaga fungsionalitas sistem jika terjadi kegagalan pusat data. Mari kita tentukan bahwa cadangan pertama akan menjadi node dari pusat data lain, dan cadangan kedua akan menjadi node dari pusat data ketiga. Skema ini dipilih satu kali dan tidak berubah sampai topologi cluster berubah, yaitu sampai node baru memasukinya (yang sangat jarang terjadi). Tata cara pemilihan master aktif baru jika master lama gagal selalu sebagai berikut: cadangan pertama akan menjadi master aktif, dan jika sudah berhenti berfungsi, cadangan kedua akan menjadi master aktif.

Skema ini lebih dapat diandalkan daripada algoritma universal, karena untuk mengaktifkan master baru cukup menentukan kegagalan master lama.

Tapi bagaimana klien memahami master mana yang sedang bekerja sekarang? Tidak mungkin mengirim informasi ke ribuan klien dalam 50 ms. Situasi mungkin terjadi ketika klien mengirimkan permintaan untuk membuka transaksi, belum mengetahui bahwa master ini tidak lagi berfungsi, dan waktu permintaan akan habis. Untuk mencegah hal ini terjadi, klien secara spekulatif mengirimkan permintaan untuk membuka transaksi kepada master grup dan kedua cadangannya sekaligus, namun hanya orang yang merupakan master aktif saat ini yang akan menanggapi permintaan ini. Klien akan melakukan semua komunikasi selanjutnya dalam transaksi hanya dengan master aktif.

Master cadangan menempatkan permintaan transaksi yang bukan miliknya ke dalam antrian transaksi yang belum dilahirkan, di mana permintaan tersebut disimpan untuk beberapa waktu. Jika master aktif mati, master baru memproses permintaan untuk membuka transaksi dari antriannya dan merespons klien. Jika klien sudah membuka transaksi dengan master lama, maka respons kedua diabaikan (dan, jelas, transaksi seperti itu tidak akan selesai dan akan diulangi oleh klien).

Cara kerja transaksi

Katakanlah klien mengirim permintaan ke koordinator untuk membuka transaksi untuk entitas ini dan itu dengan kunci utama ini dan itu. Koordinator mengunci entitas ini dan menempatkannya di tabel kunci di memori. Jika perlu, koordinator membaca entitas ini dari penyimpanan dan menyimpan data yang dihasilkan dalam status transaksi di memori koordinator.

SQL Baru = NoSQL+ACID

Ketika klien ingin mengubah data dalam suatu transaksi, ia mengirimkan permintaan ke koordinator untuk mengubah entitas, dan koordinator menempatkan data baru dalam tabel status transaksi di memori. Ini menyelesaikan perekaman - tidak ada rekaman yang dibuat ke penyimpanan.

SQL Baru = NoSQL+ACID

Ketika klien meminta datanya sendiri yang diubah sebagai bagian dari transaksi aktif, koordinator bertindak sebagai berikut:

  • jika ID sudah ada dalam transaksi, maka data diambil dari memori;
  • jika tidak ada ID di memori, maka data yang hilang dibaca dari node penyimpanan, digabungkan dengan yang sudah ada di memori, dan hasilnya diberikan kepada klien.

Dengan demikian, klien dapat membaca perubahannya sendiri, tetapi klien lain tidak melihat perubahan tersebut, karena perubahan tersebut hanya disimpan di memori koordinator; belum ada di node Cassandra.

SQL Baru = NoSQL+ACID

Saat klien mengirimkan komit, status yang ada di memori layanan disimpan oleh koordinator dalam kumpulan yang dicatat, dan dikirim sebagai kumpulan yang dicatat ke penyimpanan Cassandra. Toko melakukan semua yang diperlukan untuk memastikan bahwa paket ini diterapkan secara atom (sepenuhnya), dan mengembalikan respons ke koordinator, yang melepaskan kunci dan mengonfirmasi keberhasilan transaksi kepada klien.

SQL Baru = NoSQL+ACID

Dan untuk melakukan rollback, koordinator hanya perlu mengosongkan memori yang ditempati oleh status transaksi.

Sebagai hasil dari perbaikan di atas, kami menerapkan prinsip ACID:

  • atomisitas. Ini adalah jaminan bahwa tidak ada transaksi yang akan dicatat sebagian dalam sistem; seluruh suboperasinya akan diselesaikan, atau tidak ada satu pun yang diselesaikan. Kami mematuhi prinsip ini melalui batch yang dicatat di Cassandra.
  • Konsistensi. Setiap transaksi yang berhasil, menurut definisi, hanya mencatat hasil yang valid. Jika, setelah membuka transaksi dan melakukan sebagian operasi, ditemukan bahwa hasilnya tidak valid, rollback akan dilakukan.
  • Isolasi. Ketika suatu transaksi dieksekusi, transaksi bersamaan tidak akan mempengaruhi hasilnya. Transaksi yang bersaing diisolasi menggunakan kunci pesimistis pada koordinator. Untuk pembacaan di luar transaksi, prinsip isolasi diterapkan pada tingkat Komitmen Baca.
  • Stabilitas. Terlepas dari masalah di tingkat yang lebih rendah—pemadaman sistem, kegagalan perangkat keras—perubahan yang dilakukan oleh transaksi yang berhasil diselesaikan harus tetap dipertahankan ketika operasi dilanjutkan.

Membaca berdasarkan indeks

Mari kita ambil tabel sederhana:

CREATE TABLE photos (
id bigint primary key,
owner bigint,
modified timestamp,
…)

Ini memiliki ID (kunci utama), pemilik dan tanggal modifikasi. Anda perlu membuat permintaan yang sangat sederhana - pilih data pemilik dengan tanggal perubahan "untuk hari terakhir".

SELECT *
WHERE owner=?
AND modified>?

Agar kueri seperti itu dapat diproses dengan cepat, dalam DBMS SQL klasik, Anda perlu membuat indeks berdasarkan kolom (pemilik, diubah). Kami dapat melakukan ini dengan mudah, karena kami sekarang memiliki jaminan ACID!

Indeks di C*One

Ada tabel sumber dengan foto-foto di mana ID rekaman adalah kunci utama.

SQL Baru = NoSQL+ACID

Untuk indeks, C*One membuat tabel baru yang merupakan salinan dari tabel asli. Kuncinya sama dengan ekspresi indeks, dan juga menyertakan kunci utama rekaman dari tabel sumber:

SQL Baru = NoSQL+ACID

Sekarang kueri untuk “pemilik untuk hari terakhir” dapat ditulis ulang sebagai pilihan dari tabel lain:

SELECT * FROM i1_test
WHERE owner=?
AND modified>?

Konsistensi data pada foto tabel sumber dan tabel indeks i1 dijaga secara otomatis oleh koordinator. Berdasarkan skema data saja, ketika perubahan diterima, koordinator membuat dan menyimpan perubahan tidak hanya di tabel utama, tetapi juga dalam salinan. Tidak ada tindakan tambahan yang dilakukan pada tabel indeks, log tidak dibaca, dan tidak ada kunci yang digunakan. Artinya, penambahan indeks hampir tidak menghabiskan sumber daya dan hampir tidak berpengaruh pada kecepatan penerapan modifikasi.

Dengan menggunakan ACID, kami dapat mengimplementasikan indeks mirip SQL. Mereka konsisten, terukur, cepat, dapat disusun, dan dibangun ke dalam bahasa kueri CQL. Tidak diperlukan perubahan pada kode aplikasi untuk mendukung indeks. Semuanya sesederhana di SQL. Dan yang paling penting, indeks tidak mempengaruhi kecepatan eksekusi modifikasi tabel transaksi asli.

Apa yang terjadi

Kami mengembangkan C*One tiga tahun lalu dan meluncurkannya ke dalam operasi komersial.

Apa yang kita dapatkan pada akhirnya? Mari kita evaluasi menggunakan contoh subsistem pemrosesan dan penyimpanan foto, salah satu jenis data terpenting di jejaring sosial. Kami tidak berbicara tentang isi foto itu sendiri, tetapi tentang segala jenis informasi meta. Sekarang Odnoklassniki memiliki sekitar 20 miliar catatan seperti itu, sistem memproses 80 ribu permintaan baca per detik, hingga 8 ribu transaksi ACID per detik terkait dengan modifikasi data.

Saat kami menggunakan SQL dengan faktor replikasi = 1 (tetapi dalam RAID 10), informasi meta foto disimpan di cluster yang terdiri dari 32 mesin yang menjalankan Microsoft SQL Server (ditambah 11 cadangan). 10 server juga dialokasikan untuk menyimpan cadangan. Sebanyak 50 mobil mahal. Pada saat yang sama, sistem beroperasi pada beban terukur, tanpa cadangan.

Setelah bermigrasi ke sistem baru, kami menerima faktor replikasi = 3 - salinan di setiap pusat data. Sistem ini terdiri dari 63 node penyimpanan Cassandra dan 6 mesin koordinator, dengan total 69 server. Tapi mesin ini jauh lebih murah, total biayanya sekitar 30% dari biaya sistem SQL. Pada saat yang sama, beban dipertahankan pada 30%.

Dengan diperkenalkannya C*One, latensi juga menurun: dalam SQL, operasi penulisan memerlukan waktu sekitar 4,5 ms. Di C*One - sekitar 1,6 ms. Durasi transaksi rata-rata kurang dari 40 ms, komit selesai dalam 2 ms, durasi baca dan tulis rata-rata 2 ms. Persentil ke-99 - hanya 3-3,1 ms, jumlah waktu tunggu telah berkurang 100 kali lipat - semua karena meluasnya penggunaan spekulasi.

Saat ini, sebagian besar node SQL Server telah dinonaktifkan; produk baru sedang dikembangkan hanya menggunakan C*One. Kami mengadaptasi C*One agar berfungsi di cloud kami satu awan, yang memungkinkan untuk mempercepat penerapan cluster baru, menyederhanakan konfigurasi, dan mengotomatiskan operasi. Tanpa kode sumber, melakukan hal ini akan jauh lebih sulit dan rumit.

Sekarang kami sedang berupaya untuk mentransfer fasilitas penyimpanan kami yang lain ke cloud - tetapi itu adalah cerita yang sama sekali berbeda.

Sumber: www.habr.com

Tambah komentar