รูปแบบ / อัลกอริทึมการซิงโครไนซ์ไคลเอนต์ - เซิร์ฟเวอร์


224

ฉันรู้สึกว่าต้องมีรูปแบบการซิงโครไนซ์ไคลเอ็นต์กับเซิร์ฟเวอร์ที่นั่น แต่ฉันล้มเหลวในการ google ขึ้นหนึ่งทั้งหมด

สถานการณ์ค่อนข้างง่าย - เซิร์ฟเวอร์เป็นโหนดกลางที่ลูกค้าหลายรายเชื่อมต่อและจัดการข้อมูลเดียวกัน ข้อมูลสามารถแบ่งเป็นอะตอมในกรณีที่มีข้อขัดแย้งสิ่งที่อยู่บนเซิร์ฟเวอร์มีลำดับความสำคัญ (เพื่อหลีกเลี่ยงการทำให้ผู้ใช้เข้าสู่การแก้ไขข้อขัดแย้ง) ต้องการการซิงโครไนซ์บางส่วนเนื่องจากมีข้อมูลจำนวนมาก

มีรูปแบบ / แนวทางปฏิบัติที่ดีสำหรับสถานการณ์ดังกล่าวหรือไม่หรือหากคุณไม่ทราบวิธีการของคุณคืออะไร?

ด้านล่างเป็นวิธีที่ฉันคิดว่าจะแก้ปัญหา: ขนานไปกับข้อมูลจะมีการจัดทำเจอร์นัลการแก้ไขโดยมีธุรกรรมทั้งหมดที่ประทับเวลา เมื่อไคลเอนต์เชื่อมต่อจะได้รับการเปลี่ยนแปลงทั้งหมดตั้งแต่การตรวจสอบครั้งล่าสุดในรูปแบบรวม (เซิร์ฟเวอร์ต้องผ่านรายการและลบส่วนที่ตามด้วยการลบรวมการอัพเดตสำหรับแต่ละอะตอม ฯลฯ ) และอื่น ๆ เรามีความทันสมัย

ทางเลือกจะคงวันที่แก้ไขสำหรับแต่ละระเบียนและแทนที่จะทำการลบข้อมูลเพียงทำเครื่องหมายว่าถูกลบ

ความคิดใด ๆ


27
ตกลงมีการพูดคุยน้อยมากของรูปแบบสำหรับสิ่งนี้ ... ถึงแม้ว่าสถานการณ์นี้เป็นเรื่องธรรมดา
Jack Ukleja

คำตอบ:


88

คุณควรดูว่าการจัดการการเปลี่ยนแปลงแบบกระจายทำงานอย่างไร ดู SVN, CVS และที่เก็บอื่น ๆ ที่จัดการกับงานเดลต้า

คุณมีหลายกรณีการใช้งาน

  • ประสานการเปลี่ยนแปลง วิธีการบันทึกการเปลี่ยนแปลง (หรือประวัติเดลต้า) ของคุณดูดีสำหรับเรื่องนี้ ลูกค้าส่ง delta ของพวกเขาไปยังเซิร์ฟเวอร์ เซิร์ฟเวอร์รวมและกระจาย delta ไปยังไคลเอนต์ นี่เป็นกรณีทั่วไป ฐานข้อมูลเรียกสิ่งนี้ว่า "การจำลองแบบรายการ"

  • ลูกค้าสูญเสียการประสาน ผ่านการสำรองข้อมูล / กู้คืนหรือเนื่องจากข้อผิดพลาด ในกรณีนี้ลูกค้าจะต้องได้รับสถานะปัจจุบันจากเซิร์ฟเวอร์โดยไม่ต้องผ่านเดลตา นี่คือสำเนาจากต้นแบบไปยังรายละเอียด deltas และประสิทธิภาพถูกสาป มันเป็นเพียงครั้งเดียว ลูกค้าเสีย; อย่าพยายามเพิ่มประสิทธิภาพสิ่งนี้เพียงใช้สำเนาที่เชื่อถือได้

  • ลูกค้าต้องสงสัย ในกรณีนี้คุณต้องเปรียบเทียบไคลเอ็นต์กับเซิร์ฟเวอร์เพื่อตรวจสอบว่าไคลเอนต์เป็นรุ่นล่าสุดและต้องการเดลตาใด ๆ หรือไม่

คุณควรทำตามรูปแบบการออกแบบฐานข้อมูล (และ SVN) ของการกำหนดหมายเลขตามลำดับทุกการเปลี่ยนแปลง ด้วยวิธีนี้ลูกค้าสามารถร้องขอเล็กน้อย ("ฉันควรแก้ไขอะไร?") ก่อนที่จะพยายามซิงโครไนซ์ และถึงตอนนั้นการสืบค้น ("deltas ทั้งหมดตั้งแต่ 2149") ก็เป็นเรื่องง่ายสำหรับลูกค้าและเซิร์ฟเวอร์ในการประมวลผล


ขอให้คุณอธิบายว่าเดลต้าคืออะไร ฉันเดาว่าจะเป็นชุดค่าผสม hash / timestamp ... ฉันต้องการได้ยินจากคุณ
Anis

เดลต้าหมายถึงการเปลี่ยนแปลงระหว่างการแก้ไขสองครั้ง ตัวอย่างเช่นหากชื่อผู้ใช้มีการเปลี่ยนแปลงแล้วเดลต้าสามารถเป็นเช่น {แก้ไข: 123 ชื่อ: "John Doe"}
dipole_moment

31

ในฐานะส่วนหนึ่งของทีมของฉันฉันได้ทำโครงการมากมายที่เกี่ยวข้องกับการซิงค์ข้อมูลดังนั้นฉันควรมีความสามารถในการตอบคำถามนี้

การซิงค์ข้อมูลเป็นแนวคิดที่กว้างขวางและมีวิธีที่จะพูดคุยกันมากเกินไป มันครอบคลุมแนวทางที่แตกต่างหลากหลายด้วยอัพไซด์และอัพไซด์ นี่คือหนึ่งในการจัดประเภทที่เป็นไปได้ตามสองมุมมอง: ซิงโครนัส / อะซิงโครนัสไคลเอ็นต์ / เซิร์ฟเวอร์ / เพียร์ทูเพียร์ การปรับใช้การซิงค์ขึ้นอยู่กับปัจจัยเหล่านี้ความซับซ้อนของตัวแบบข้อมูลจำนวนการถ่ายโอนและจัดเก็บข้อมูลและข้อกำหนดอื่น ๆ ดังนั้นในแต่ละกรณีตัวเลือกควรเป็นไปตามการใช้งานที่ง่ายที่สุดซึ่งตรงตามข้อกำหนดของแอพ

จากการทบทวนโซลูชันที่มีอยู่ในปัจจุบันเราสามารถวิเคราะห์คลาสการซิงค์ที่สำคัญหลายคลาสซึ่งแตกต่างกันไปในแต่ละส่วนของวัตถุที่ต้องทำการซิงโครไนซ์:

  • การซิงค์เอกสารหรือฐานข้อมูลทั้งหมดใช้ในแอพพลิเคชั่นบนคลาวด์เช่น Dropbox, Google Drive หรือ Yandex.Disk เมื่อผู้ใช้แก้ไขและบันทึกไฟล์เวอร์ชันไฟล์ใหม่จะถูกอัพโหลดไปยังคลาวด์โดยสมบูรณ์เขียนทับสำเนาก่อนหน้า ในกรณีที่มีข้อขัดแย้งทั้งสองเวอร์ชันของไฟล์จะถูกบันทึกไว้เพื่อให้ผู้ใช้สามารถเลือกเวอร์ชันที่เกี่ยวข้องมากกว่า
  • การซิงค์คู่ของคีย์ - ค่าสามารถใช้ในแอปที่มีโครงสร้างข้อมูลแบบง่ายซึ่งตัวแปรถูกพิจารณาว่าเป็นอะตอมมิกคือไม่แบ่งออกเป็นองค์ประกอบเชิงตรรกะ ตัวเลือกนี้คล้ายกับการซิงค์เอกสารทั้งหมดเนื่องจากทั้งค่าและเอกสารสามารถเขียนทับได้อย่างสมบูรณ์ อย่างไรก็ตามจากมุมมองของผู้ใช้เอกสารเป็นวัตถุที่ซับซ้อนซึ่งประกอบด้วยหลายส่วน แต่คู่คีย์ - ค่านั้นเป็นเพียงสตริงสั้น ๆ หรือตัวเลข ดังนั้นในกรณีนี้เราสามารถใช้กลยุทธ์การแก้ปัญหาความขัดแย้งได้ง่ายขึ้นโดยพิจารณาถึงคุณค่าที่เกี่ยวข้องมากขึ้นหากเป็นสิ่งสุดท้ายที่จะเปลี่ยนแปลง
  • การซิงค์ข้อมูลที่มีโครงสร้างเป็นทรีหรือกราฟถูกใช้ในแอปพลิเคชันที่มีความซับซ้อนมากขึ้นซึ่งจำนวนข้อมูลมีขนาดใหญ่พอที่จะส่งฐานข้อมูลอย่างครบถ้วนในทุก ๆ การอัพเดท ในกรณีนี้ความขัดแย้งต้องได้รับการแก้ไขในระดับของแต่ละวัตถุเขตข้อมูลหรือความสัมพันธ์ เรามุ่งเน้นไปที่ตัวเลือกนี้เป็นหลัก

ดังนั้นเราจึงรวบรวมความรู้ของเราไว้ในบทความนี้ซึ่งฉันคิดว่าอาจเป็นประโยชน์กับทุกคนที่สนใจในหัวข้อ => การซิงค์ข้อมูลในแอป iOS ที่ใช้ข้อมูลหลัก ( http://blog.denivip.ru/index.php/2014/04) / data-syncing-in-core-data-based-ios-apps /? lang = th )


3
^^^^^^ นี่เป็นคำตอบที่ดีที่สุดแล้ว!
hgoebl

ฉันเห็นด้วยเดนิสได้นำเรื่องมากมายเข้ามา + การเชื่อมโยงบทความนั้นยอดเยี่ยมมาก ยังพูดถึง OT ที่กล่าวถึงโดย DanielPaull คำตอบโดย S.Lott นั้นดี แต่ก็มีความลึกมากกว่านี้
Krystian

28

สิ่งที่คุณต้องการจริงๆคือOperational Transform (OT) สิ่งนี้สามารถตอบสนองต่อความขัดแย้งในหลายกรณี

ยังคงเป็นพื้นที่ของการวิจัย แต่มีการใช้อัลกอริทึม OT ต่าง ๆ รอบ ฉันมีส่วนร่วมในการวิจัยเป็นเวลาหลายปีแล้วดังนั้นแจ้งให้ฉันทราบว่าเส้นทางนี้สนใจคุณและฉันยินดีที่จะนำคุณไปสู่แหล่งข้อมูลที่เกี่ยวข้อง


7
แดเนียลจะเป็นตัวชี้ไปยังแหล่งข้อมูลที่เกี่ยวข้อง
Parand

4
ฉันเพิ่งอ่านบทความวิกิพีเดียอีกครั้ง มันมาไกลแล้วและมีการอ้างอิงที่เกี่ยวข้องมากมายที่ด้านล่างของหน้านั้น ฉันจะชี้ให้คุณเห็นงานของเฉิงเจิ้งซุน - งานของเขาอ้างอิงจากวิกิพีเดีย en.wikipedia.org/wiki/Operational_transformation หวังว่าจะช่วย!
Daniel Paull

13

คำถามไม่ชัดเจน แต่ฉันจะตรวจสอบการล็อคในแง่ดีถ้าฉันเป็นคุณ สามารถนำไปใช้กับหมายเลขลำดับที่เซิร์ฟเวอร์ส่งคืนสำหรับแต่ละระเบียน เมื่อลูกค้าพยายามที่จะบันทึกบันทึกกลับจะมีหมายเลขลำดับที่ได้รับจากเซิร์ฟเวอร์ หากหมายเลขลำดับตรงกับสิ่งที่อยู่ในฐานข้อมูล ณ เวลาที่ได้รับการอัพเดตอนุญาตการอัปเดตและหมายเลขลำดับจะเพิ่มขึ้น หากหมายเลขลำดับไม่ตรงกันการอัพเดทจะไม่ได้รับอนุญาต


2
หมายเลขลำดับคือเพื่อนของคุณที่นี่ คิดถึงคิวข้อความที่ต่อเนื่อง
Daniel Paull

7

ฉันสร้างระบบเช่นนี้สำหรับแอพเมื่อประมาณ 8 ปีที่แล้วและฉันสามารถแบ่งปันสองสามวิธีที่มันมีการพัฒนาเมื่อการใช้งานแอพเพิ่มขึ้น

ฉันเริ่มต้นด้วยการบันทึกทุกการเปลี่ยนแปลง (แทรกอัปเดตหรือลบ) จากอุปกรณ์ใด ๆ ลงในตาราง "ประวัติ" ตัวอย่างเช่นหากมีคนเปลี่ยนหมายเลขโทรศัพท์ในตาราง "ผู้ติดต่อ" ระบบจะแก้ไขฟิลด์ contact.phone และเพิ่มบันทึกประวัติด้วย action = update, field = phone, record = [contact ID], value = [หมายเลขโทรศัพท์ใหม่] จากนั้นเมื่อใดก็ตามที่อุปกรณ์ทำการซิงค์อุปกรณ์จะทำการดาวน์โหลดรายการประวัติตั้งแต่การซิงค์ครั้งล่าสุดและนำไปใช้กับฐานข้อมูลท้องถิ่น ดูเหมือนว่ารูปแบบ "การจำลองแบบรายการ" ที่อธิบายไว้ข้างต้น

ปัญหาหนึ่งคือการรักษา ID ที่ไม่ซ้ำกันเมื่อสามารถสร้างรายการบนอุปกรณ์ที่แตกต่างกัน ฉันไม่รู้เกี่ยวกับ UUID เมื่อฉันเริ่มต้นใช้งานดังนั้นฉันจึงใช้ ID ที่เพิ่มขึ้นอัตโนมัติและเขียนรหัสที่ซับซ้อนที่รันบนเซิร์ฟเวอร์กลางเพื่อตรวจสอบ ID ใหม่ที่อัปโหลดจากอุปกรณ์เปลี่ยนเป็น ID เฉพาะหากมีข้อขัดแย้งและ บอกอุปกรณ์ต้นทางให้เปลี่ยน ID ในฐานข้อมูลโลคัล เพียงแค่เปลี่ยน ID ของบันทึกใหม่ก็ไม่ได้แย่ขนาดนั้น แต่ถ้าฉันสร้างตัวอย่างเช่นรายการใหม่ในตารางที่ติดต่อจากนั้นสร้างรายการที่เกี่ยวข้องใหม่ในตารางเหตุการณ์ตอนนี้ฉันมีกุญแจต่างประเทศที่ฉันต้อง ตรวจสอบและอัปเดต

ในที่สุดฉันก็ได้เรียนรู้ว่า UUID สามารถหลีกเลี่ยงปัญหานี้ได้ แต่จากนั้นฐานข้อมูลของฉันก็ค่อนข้างใหญ่และฉันก็กลัวว่าการติดตั้ง UUID แบบเต็มรูปแบบจะทำให้เกิดปัญหาเรื่องประสิทธิภาพ ดังนั้นแทนที่จะใช้ UUID เต็มรูปแบบฉันเริ่มใช้คีย์ตัวอักษรผสมตัวเลขที่สร้างขึ้นแบบสุ่ม 8 ตัวเป็น ID และฉันปล่อยรหัสที่มีอยู่ของฉันไว้เพื่อจัดการกับความขัดแย้ง อยู่ระหว่างคีย์ 8 ตัวอักษรปัจจุบันของฉันกับ 36 ตัวอักษรของ UUID จะต้องมีจุดหวานที่จะกำจัดความขัดแย้งโดยไม่ต้องขยายตัวที่ไม่จำเป็น แต่เนื่องจากฉันมีรหัสการแก้ปัญหาความขัดแย้งอยู่แล้ว .

ปัญหาต่อไปคือตารางประวัติมีขนาดใหญ่กว่าฐานข้อมูลที่เหลือทั้งหมดประมาณ 10 เท่า สิ่งนี้ทำให้การจัดเก็บมีราคาแพงและการบำรุงรักษาใด ๆ ในตารางประวัติอาจเจ็บปวด การรักษาทั้งตารางนั้นช่วยให้ผู้ใช้ย้อนกลับการเปลี่ยนแปลงก่อนหน้านี้ได้บ้าง ดังนั้นฉันจึงเพิ่มกิจวัตรประจำวันในกระบวนการซิงค์โดยที่หากรายการประวัติที่อุปกรณ์ดาวน์โหลดล่าสุดไม่มีอยู่ในตารางประวัติเซิร์ฟเวอร์จะไม่ให้รายการประวัติล่าสุด แต่ให้แฟ้มที่มีข้อมูลทั้งหมดแทน บัญชีนั้น จากนั้นฉันเพิ่ม cronjob เพื่อลบรายการประวัติที่เก่ากว่า 90 วัน ซึ่งหมายความว่าผู้ใช้ยังคงสามารถย้อนกลับการเปลี่ยนแปลงน้อยกว่า 90 วันและถ้าพวกเขาซิงค์อย่างน้อยหนึ่งครั้งในทุก ๆ 90 วันการปรับปรุงจะเพิ่มขึ้นตามเดิม แต่ถ้าพวกเขารอนานกว่า 90 วัน

การเปลี่ยนแปลงนั้นลดขนาดของตารางประวัติลงเกือบ 90% ดังนั้นตอนนี้การดูแลรักษาตารางประวัติจะทำให้ฐานข้อมูลใหญ่ขึ้นสองเท่าแทนที่จะใหญ่กว่าสิบเท่า ประโยชน์อีกอย่างของระบบนี้คือการซิงค์ยังคงทำงานได้โดยไม่ต้องมีตารางประวัติหากต้องการ - เช่นถ้าฉันต้องการทำการบำรุงรักษาบางอย่างที่ใช้งานออฟไลน์ชั่วคราว หรือฉันสามารถเสนอช่วงเวลาย้อนกลับที่แตกต่างกันสำหรับบัญชีที่มีจุดราคาต่างกัน และหากมีการเปลี่ยนแปลงมากกว่า 90 วันในการดาวน์โหลดไฟล์ที่สมบูรณ์มักจะมีประสิทธิภาพมากกว่ารูปแบบที่เพิ่มขึ้น

หากฉันเริ่มใหม่วันนี้ฉันจะข้ามการตรวจสอบความขัดแย้งของ ID และเพียงแค่กำหนดระยะเวลาที่สำคัญเพียงพอที่จะกำจัดข้อขัดแย้งด้วยการตรวจสอบข้อผิดพลาดบางประเภท แต่ตารางประวัติและการรวมกันของการดาวน์โหลดที่เพิ่มขึ้นสำหรับการอัปเดตล่าสุดหรือการดาวน์โหลดแบบเต็มเมื่อจำเป็นทำงานได้ดี


1

สำหรับการซิงค์เดลต้า (เปลี่ยน) คุณสามารถใช้รูปแบบ pubsub เพื่อเผยแพร่การเปลี่ยนแปลงกลับไปยังไคลเอนต์ที่สมัครสมาชิกทั้งหมดบริการเช่นpusherสามารถทำสิ่งนี้ได้

สำหรับมิร์เรอร์ฐานข้อมูลบางเฟรมเวิร์กของเว็บใช้ฐานข้อมูลขนาดเล็กโลคัลเพื่อซิงค์ฐานข้อมูลฝั่งเซิร์ฟเวอร์กับโลคัลในฐานข้อมูลเบราว์เซอร์สนับสนุนการซิงโครไนซ์บางส่วน ตรวจสอบเครื่องวัดหรือ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.