Pola / algoritma sinkronisasi klien-server?

224

Saya merasa harus ada pola sinkronisasi client-server di luar sana. Tetapi saya benar-benar gagal untuk google satu.

Situasinya cukup sederhana - server adalah simpul pusat, yang terhubung ke banyak klien dan memanipulasi data yang sama. Data dapat dipecah dalam atom, jika terjadi konflik, apa pun yang ada di server, memiliki prioritas (untuk menghindari pengguna masuk ke penyelesaian konflik). Sinkronisasi parsial lebih disukai karena jumlah data yang berpotensi besar.

Apakah ada pola / praktik yang baik untuk situasi seperti itu, atau jika Anda tidak tahu - apa pendekatan Anda?

Di bawah ini adalah bagaimana saya sekarang berpikir untuk menyelesaikannya: Paralel dengan data, jurnal modifikasi akan diadakan, memiliki semua transaksi dicap waktu. Ketika klien terhubung, ia menerima semua perubahan sejak pemeriksaan terakhir, dalam bentuk gabungan (server memeriksa daftar dan menghapus tambahan yang diikuti oleh penghapusan, menggabungkan pembaruan untuk setiap atom, dll.). Et voila, kami terbaru.

Alternatif akan menjaga tanggal modifikasi untuk setiap catatan, dan alih-alih melakukan penghapusan data, tandai saja sebagai dihapus.

Adakah pikiran?

tm_lv
sumber
27
setuju ada sedikit pembicaraan tentang pola untuk hal semacam ini ... meskipun skenario ini cukup umum
Jack Ukleja

Jawaban:

88

Anda harus melihat bagaimana manajemen perubahan yang didistribusikan bekerja. Lihatlah SVN, CVS dan repositori lain yang mengelola pekerjaan delta.

Anda memiliki beberapa kasus penggunaan.

  • Sinkronisasi perubahan. Pendekatan perubahan-log (atau sejarah delta) Anda terlihat bagus untuk ini. Klien mengirim delta mereka ke server; server mengkonsolidasikan dan mendistribusikan delta ke klien. Ini adalah kasus khas. Basis data menyebut ini "replikasi transaksi".

  • Klien telah kehilangan sinkronisasi. Baik melalui backup / restore atau karena bug. Dalam hal ini, klien perlu mendapatkan status saat ini dari server tanpa melalui delta. Ini adalah salinan dari master hingga detail, delta dan kinerja dikutuk. Itu adalah satu hal; klien rusak; jangan mencoba untuk mengoptimalkan ini, cukup terapkan salinan yang dapat diandalkan.

  • Klien curiga. Dalam hal ini, Anda perlu membandingkan klien dengan server untuk menentukan apakah klien mutakhir dan membutuhkan delta.

Anda harus mengikuti pola desain basis data (dan SVN) penomoran berurutan setiap perubahan. Dengan cara itu klien dapat membuat permintaan sepele ("Revisi apa yang harus saya miliki?") Sebelum mencoba menyinkronkan. Dan meskipun begitu, kueri ("Semua delta sejak 2149") sangat sederhana dan mudah untuk diproses oleh klien dan server.

S.Lott
sumber
Bolehkah Anda menjelaskan apa sebenarnya delta itu? Dugaan saya adalah kombinasi hash / cap waktu ... Saya ingin mendengar dari Anda, Pak.
Anis
Delta mengacu pada perubahan antara dua revisi. Misalnya, jika nama pengguna telah berubah maka delta dapat berupa {revisi: 123, nama: "John Doe"}
dipole_moment
31

Sebagai bagian dari tim, saya melakukan banyak proyek yang melibatkan sinkronisasi data, jadi saya harus kompeten untuk menjawab pertanyaan ini.

Sinkronisasi data adalah konsep yang cukup luas dan ada terlalu banyak untuk didiskusikan. Ini mencakup berbagai pendekatan yang berbeda dengan kelebihan dan kekurangannya. Berikut adalah salah satu klasifikasi yang mungkin didasarkan pada dua perspektif: Sinkron / Asinkron, Klien / Server / Peer-to-Peer. Implementasi sinkronisasi sangat tergantung pada faktor-faktor ini, kompleksitas model data, jumlah data yang ditransfer dan disimpan, dan persyaratan lainnya. Jadi dalam setiap kasus tertentu pilihan harus mendukung implementasi yang paling sederhana memenuhi persyaratan aplikasi.

Berdasarkan ulasan dari solusi yang ada, kami dapat menggambarkan beberapa kelas utama sinkronisasi, berbeda dalam granularitas objek yang akan disinkronkan:

  • Sinkronisasi seluruh dokumen atau basis data digunakan dalam aplikasi berbasis cloud, seperti Dropbox, Google Drive atau Yandex.Disk. Ketika pengguna mengedit dan menyimpan file, versi file baru diunggah ke cloud sepenuhnya, menimpa salinan sebelumnya. Jika terjadi konflik, kedua versi file disimpan sehingga pengguna dapat memilih versi mana yang lebih relevan.
  • Sinkronisasi pasangan kunci-nilai dapat digunakan dalam aplikasi dengan struktur data sederhana, di mana variabel dianggap atom, yaitu tidak dibagi menjadi komponen logis. Opsi ini mirip dengan menyinkronkan seluruh dokumen, karena nilai dan dokumen dapat ditimpa sepenuhnya. Namun, dari perspektif pengguna, dokumen adalah objek kompleks yang terdiri dari banyak bagian, tetapi pasangan nilai kunci hanyalah string pendek atau angka. Oleh karena itu, dalam hal ini kita dapat menggunakan strategi resolusi konflik yang lebih sederhana, mengingat nilainya lebih relevan, jika telah menjadi yang terakhir berubah.
  • Sinkronisasi data terstruktur sebagai pohon atau grafik digunakan dalam aplikasi yang lebih canggih di mana jumlah data cukup besar untuk mengirim database secara keseluruhan pada setiap pembaruan. Dalam hal ini, konflik harus diselesaikan di tingkat objek, bidang, atau hubungan individu. Kami terutama berfokus pada opsi ini.

Jadi, kami mengambil pengetahuan kami dalam artikel ini yang saya pikir mungkin sangat berguna bagi semua orang yang tertarik pada topik => Sinkronisasi Data di aplikasi iOS Berbasis Data Inti ( http://blog.denivip.ru/index.php/2014/04 / data-syncing-in-core-data-based-ios-apps /? lang = en )

Denis Bulichenko
sumber
3
^^^^^^ ini sejauh ini merupakan jawaban terbaik, kawan!
hgoebl
Saya setuju, Denis membawa banyak ke topik + tautan artikel yang mengagumkan. Juga berbicara tentang PL yang disebutkan oleh Daniel Paull. Jawaban oleh S.Lott baik tetapi ini jauh lebih mendalam.
Krystian
28

Yang benar-benar Anda butuhkan adalah Operational Transform (OT). Ini bahkan dapat memenuhi konflik dalam banyak kasus.

Ini masih merupakan area penelitian aktif, tetapi ada implementasi dari berbagai algoritma PL sekitar. Saya telah terlibat dalam penelitian semacam itu selama beberapa tahun sekarang, jadi beri tahu saya jika rute ini menarik minat Anda dan saya akan dengan senang hati mengarahkan Anda ke sumber daya yang relevan.

Daniel Paull
sumber
7
Daniel, penunjuk ke sumber daya yang relevan akan dihargai.
Parand
4
Saya baru saja membaca ulang artikel wikipedia. Ini sangat panjang dan memiliki banyak referensi yang relevan di bagian bawah halaman itu. Saya akan mengarahkan Anda ke karya Chengzheng Sun - karyanya dirujuk dari wikipedia. en.wikipedia.org/wiki/Operational_transformation . Semoga itu bisa membantu!
Daniel Paull
13

Pertanyaannya tidak sejernih kristal, tetapi saya akan mencari penguncian optimis jika saya adalah Anda. Ini dapat diimplementasikan dengan nomor urut yang dikembalikan server untuk setiap catatan. Ketika klien mencoba untuk menyimpan catatan kembali, itu akan termasuk nomor urut yang diterima dari server. Jika nomor urut cocok dengan apa yang ada di database pada saat pembaruan diterima, pembaruan diizinkan dan nomor urut bertambah. Jika nomor urut tidak cocok, pembaruan tidak diizinkan.

erikkallen
sumber
2
Nomor urut adalah teman Anda di sini. Pikirkan tentang antrian pesan yang persisten.
Daniel Paull
7

Saya membangun sistem seperti ini untuk aplikasi sekitar 8 tahun yang lalu, dan saya dapat berbagi beberapa cara bahwa itu telah berevolusi karena penggunaan aplikasi telah berkembang.

Saya mulai dengan mencatat setiap perubahan (masukkan, perbarui atau hapus) dari perangkat apa pun ke tabel "riwayat". Jadi jika, misalnya, seseorang mengubah nomor telepon mereka di tabel "kontak", sistem akan mengedit bidang contact.phone, dan juga menambahkan catatan riwayat dengan action = update, field = phone, record = [ID kontak], value = [nomor telepon baru]. Kemudian kapan pun suatu perangkat disinkronkan, ia mengunduh item sejarah sejak sinkronisasi terakhir dan menerapkannya ke basis data lokalnya. Ini terdengar seperti pola "replikasi transaksi" yang dijelaskan di atas.

Satu masalah adalah menjaga ID unik ketika item dapat dibuat pada perangkat yang berbeda. Saya tidak tahu tentang UUID ketika saya memulai ini, jadi saya menggunakan ID penambahan-otomatis dan menulis beberapa kode berbelit-belit yang berjalan di server pusat untuk memeriksa ID baru yang diunggah dari perangkat, mengubahnya ke ID unik jika ada konflik, dan beri tahu perangkat sumber untuk mengubah ID di database lokalnya. Hanya mengubah ID catatan baru tidak seburuk itu, tetapi jika saya membuat, misalnya, item baru di tabel kontak, lalu membuat item terkait baru di tabel acara, sekarang saya memiliki kunci asing yang saya juga perlu periksa dan perbarui.

Akhirnya saya mengetahui bahwa UUID dapat menghindari hal ini, tetapi pada saat itu basis data saya semakin besar dan saya takut implementasi penuh UUID akan menciptakan masalah kinerja. Jadi alih-alih menggunakan UUID penuh, saya mulai menggunakan kunci alfanumerik 8 karakter yang dibuat secara acak sebagai ID, dan saya meninggalkan kode saya yang ada untuk menangani konflik. Di suatu tempat antara kunci 8-karakter saya saat ini dan 36 karakter UUID harus ada sweet spot yang akan menghilangkan konflik tanpa mengasapi yang tidak perlu, tetapi karena saya sudah memiliki kode resolusi konflik, itu belum menjadi prioritas untuk bereksperimen dengan itu .

Masalah berikutnya adalah bahwa tabel sejarah sekitar 10 kali lebih besar dari seluruh database. Ini membuat penyimpanan menjadi mahal, dan perawatan apa pun di atas tabel riwayat bisa jadi menyakitkan. Menjaga seluruh tabel memungkinkan pengguna untuk memutar kembali perubahan sebelumnya, tetapi itu mulai terasa seperti berlebihan. Jadi saya menambahkan rutin ke proses sinkronisasi di mana jika item riwayat yang terakhir diunduh perangkat tidak lagi ada di tabel riwayat, server tidak memberikannya item riwayat baru-baru ini, tetapi sebaliknya memberikannya file yang berisi semua data untuk akun itu. Kemudian saya menambahkan cronjob untuk menghapus item riwayat yang lebih lama dari 90 hari. Ini berarti pengguna masih dapat memutar kembali perubahan yang berumur kurang dari 90 hari, dan jika mereka menyinkronkan setidaknya sekali setiap 90 hari, pembaruan akan menjadi tambahan seperti sebelumnya. Tetapi jika mereka menunggu lebih dari 90 hari,

Perubahan itu mengurangi ukuran tabel riwayat hampir 90%, jadi sekarang mempertahankan tabel sejarah hanya membuat database dua kali lebih besar daripada sepuluh kali lebih besar. Manfaat lain dari sistem ini adalah bahwa sinkronisasi masih dapat bekerja tanpa tabel histori jika diperlukan - seperti jika saya perlu melakukan beberapa pemeliharaan yang menjadikannya offline untuk sementara. Atau saya dapat menawarkan periode waktu pengembalian yang berbeda untuk akun pada titik harga yang berbeda. Dan jika ada lebih dari 90 hari perubahan untuk diunduh, file lengkap biasanya lebih efisien daripada format inkremental.

Jika saya memulai hari ini, saya akan melewatkan pemeriksaan konflik ID dan hanya bertujuan untuk panjang kunci yang cukup untuk menghilangkan konflik, dengan semacam pengecekan kesalahan untuk berjaga-jaga. Tetapi tabel riwayat dan kombinasi unduhan tambahan untuk pembaruan terbaru atau unduhan lengkap saat diperlukan telah berfungsi dengan baik.

arlomedia
sumber
1

Untuk sinkronisasi delta (perubahan), Anda dapat menggunakan pola pubsub untuk mempublikasikan perubahan kembali ke semua klien berlangganan, layanan seperti pusher dapat melakukan ini.

Untuk cermin basis data, beberapa kerangka kerja web menggunakan basis data mini lokal untuk menyinkronkan basis data sisi server ke lokal dalam basis data peramban, sinkronisasi sebagian didukung. Periksa meter atau .

fuyi
sumber