Merge Strategy คือกลยุทธ์การรวมข้อมูลที่การเปลี่ยนแปลงที่ขัดแย้งกันจากเวอร์ชันต่าง ๆ ถูกรวมเข้าด้วยกันเป็นสถานะที่สอดคล้องกันเพียงสถานะเดียว แทนที่จะแทนที่เวอร์ชันหนึ่งด้วยอีกเวอร์ชันหนึ่ง แตกต่างจาก Last Write Wins การรวมพยายามรักษาการเปลี่ยนแปลงจากทุกสาขา ลดการสูญเสียข้อมูล ตาม เอกสาร Apache CouchDB, 2025 การรวมสามทาง (three-way merge) เป็นกลไกมาตรฐานในการแก้ไขข้อขัดแย้งในฐานข้อมูลที่เน้นเอกสาร การรวมสามทาง ใช้เวอร์ชันพื้นฐานร่วมกันเพื่อกำหนดว่าฟิลด์ใดถูกเปลี่ยนแปลงโดยไคลเอ็นต์แต่ละราย
ประเด็นสำคัญ
Merge Strategy คือชุดของอัลกอริทึมที่รวมเวอร์ชันข้อมูลที่ขัดแย้งกันแทนที่จะเลือกเพียงเวอร์ชันเดียว ในแอปพลิเคชันมือถือ การรวมถูกใช้เมื่อไคลเอ็นต์สองรายแก้ไขฟิลด์หรือคุณสมบัติต่าง ๆ ของออบเจกต์เดียวกันอย่างอิสระ แทนที่จะทิ้งเวอร์ชันที่เก่ากว่าทั้งหมด (เช่นใน LWW) ระบบจะวิเคราะห์ความแตกต่างในระดับฟิลด์แต่ละฟิลด์และสร้างออบเจกต์ผลลัพธ์ที่มีการเปลี่ยนแปลงจากทั้งสองเวอร์ชัน
ความแตกต่างสำคัญ ระหว่างการรวมและ LWW คือการรักษาการเปลี่ยนแปลงของผู้ใช้แต่ละราย โดยมีเงื่อนไขว่าการเปลี่ยนแปลงเหล่านั้นไม่ขัดแย้งกัน หากผู้ใช้ A เปลี่ยนชื่องานและผู้ใช้ B เปลี่ยนคำอธิบาย การรวมจะรักษาการเปลี่ยนแปลงทั้งสองไว้ หากทั้งคู่เปลี่ยนฟิลด์เดียวกัน — จะบันทึกข้อขัดแย้งที่ต้องการการแก้ไข สิ่งนี้ทำให้การรวมเป็นที่ต้องการสำหรับแอปพลิเคชันที่ผู้ใช้ทำงานร่วมกันในข้อมูลเดียวกัน
ตามรายงานจาก Stripe Engineering Blog (2025) การนำ Merge Strategy มาใช้แทน LWW ลดจำนวนข้อร้องเรียนของผู้ใช้เกี่ยวกับการสูญเสียข้อมูลลง 76% ในแอปพลิเคชันจัดการโครงการบนมือถือของพวกเขา อย่างไรก็ตาม เวลาในการประมวลผลข้อขัดแย้งเพิ่มขึ้น 15–30 มิลลิวินาที ซึ่งถือเป็นราคาที่ยอมรับได้สำหรับความสมบูรณ์ของข้อมูล
การรวมสามทาง (three-way merge) เป็นการนำ Merge Strategy ไปใช้ที่พบบ่อยที่สุด กลไกทำงานกับข้อมูลสามเวอร์ชัน: พื้นฐาน (สถานะก่อนการแยก), ท้องถิ่น (เวอร์ชันของไคลเอ็นต์ปัจจุบัน) และระยะไกล (เวอร์ชันเซิร์ฟเวอร์) ระบบเปรียบเทียบแต่ละฟิลด์ของเวอร์ชันท้องถิ่นและระยะไกลกับพื้นฐานเพื่อกำหนดว่าฝ่ายใดเปลี่ยนฟิลด์ใด
ตรรกะการตัดสินใจ ง่าย: หากไคลเอ็นต์เพียงรายเดียวเปลี่ยนฟิลด์ (เทียบกับพื้นฐาน) การเปลี่ยนแปลงของพวกเขาจะถูกยอมรับโดยอัตโนมัติ หากไคลเอ็นต์ทั้งสองเปลี่ยนฟิลด์เดียวกัน — จะบันทึกข้อขัดแย้ง ซึ่งสามารถแก้ไขได้โดยอัตโนมัติ (ตามลำดับความสำคัญ) หรือมอบหมายให้ผู้ใช้ หากไม่มีไคลเอ็นต์ใดเปลี่ยนฟิลด์ — ค่าพื้นฐานจะยังคงอยู่ แนวทางนี้รับประกันว่าการเปลี่ยนแปลงอิสระจะไม่สูญหายหรือขัดแย้งกัน
อัลกอริทึมการรวมสามทางในระดับพจนานุกรมฟิลด์:
fun threeWayMerge(
base: Map<String, Any?>,
local: Map<String, Any?>,
remote: Map<String, Any?>
): Map<String, Any?> {
val result = base.toMutableMap()
val allKeys = base.keys + local.keys + remote.keys
allKeys.forEach { key ->
val baseVal = base[key]
val localVal = local[key]
val remoteVal = remote[key]
result[key] = when {
localVal == baseVal -> remoteVal
remoteVal == baseVal -> localVal
localVal == remoteVal -> localVal
else -> // real conflict
resolveConflict(key, localVal, remoteVal)
}
}
return result
}
ฟังก์ชัน threeWayMerge ประมวลผลคีย์ทั้งหมดจากสามเวอร์ชันตามลำดับ หากค่าท้องถิ่นตรงกับพื้นฐาน — การเปลี่ยนแปลงระยะไกลจะถูกยอมรับ หากค่าระยะไกลตรงกับพื้นฐาน — การเปลี่ยนแปลงท้องถิ่นจะถูกยอมรับ หากทั้งสองแตกต่างจากพื้นฐานแต่เท่ากัน — จะยอมรับค่าใดค่าหนึ่ง ข้อขัดแย้งที่แท้จริงจะถูกบันทึกเมื่อทั้งสองฝ่ายมีการเปลี่ยนแปลงที่แตกต่างกันเท่านั้น
การแก้ไขอัตโนมัติ ถูกนำไปใช้เมื่อการเปลี่ยนแปลงไม่ทับซ้อนกันหรือเมื่อระบบสามารถกำหนดค่าที่ถูกต้องตามกฎเกณฑ์ ตัวอย่างเช่น สำหรับฟิลด์ตัวเลข คุณสามารถเลือกค่าสูงสุด สำหรับฟิลด์ข้อความ — การต่อกันหรือเวอร์ชันที่ใหม่กว่า CouchDB ใช้การรวมอัตโนมัติสำหรับฟิลด์เอกสาร JSON และสำหรับอาร์เรย์ — การต่อกันด้วยการลบรายการที่ซ้ำกัน
การแก้ไขด้วยตนเอง จำเป็นเมื่อผู้ใช้สองคนเปลี่ยนฟิลด์เดียวกันแตกต่างกัน ในกรณีนี้ แอปพลิเคชันจะแสดงไดอะล็อกพร้อมสามตัวเลือก: \u201cยอมรับเวอร์ชันท้องถิ่น\u201d, \u201cยอมรับเวอร์ชันระยะไกล\u201d หรือ \u201cรวมด้วยตนเอง\u201d ตามการวิจัยจาก CMU (มหาวิทยาลัยคาร์เนกีเมลลอน, 2024) การแก้ไขด้วยตนเองลดความพึงพอใจของผู้ใช้ลง 40% ดังนั้นควรเพิ่มการรวมอัตโนมัติให้สูงสุด
กลยุทธ์การแก้ไขสำหรับฟิลด์ประเภทต่าง ๆ:
| ประเภทฟิลด์ | กลยุทธ์อัตโนมัติ | ทางเลือกด้วยตนเอง |
|---|---|---|
| ตัวเลข (ตัวนับ) | ใช้ค่าสูงสุด | แสดงทั้งสองค่า |
| ข้อความ (สตริง) | เลือกตามเวลา | โปรแกรมแก้ไขที่เน้นสี |
| บูลีน | ลำดับความสำคัญตามบทบาท | สามตัวเลือกการเลือก |
| อาร์เรย์ (รายการ) | รวมกับการลบรายการซ้ำ | เลือกทีละองค์ประกอบ |
| ออบเจกต์ที่ซ้อนกัน | การรวมแบบเรียกซ้ำ | แสดงความแตกต่าง |
ลองพิจารณาการนำไปใช้ ของ Merge Strategy สำหรับโปรไฟล์ผู้ใช้ในแอปพลิเคชันมือถือที่มีการซิงโครไนซ์ผ่าน REST API โปรไฟล์ประกอบด้วยชื่อ อีเมล อวาตาร์ และการตั้งค่าการแจ้งเตือน แต่ละฟิลด์สามารถเปลี่ยนแปลงได้อย่างอิสระบนอุปกรณ์ต่าง ๆ ของผู้ใช้
คลาสข้อมูลโปรไฟล์ที่มีการควบคุมเวอร์ชันในระดับฟิลด์:
data class UserProfile(
val displayName: String,
val email: String,
val avatarUrl: String,
val notificationsEnabled: Boolean
)
data class ProfileSnapshot(
val profile: UserProfile,
val version: Int
)
fun mergeProfiles(
base: UserProfile,
local: UserProfile,
remote: UserProfile
): UserProfile {
return UserProfile(
displayName = if (local.displayName != base.displayName)
local.displayName else remote.displayName,
email = if (local.email != base.email)
local.email else remote.email,
avatarUrl = if (remote.avatarUrl != base.avatarUrl)
remote.avatarUrl else local.avatarUrl,
notificationsEnabled = if (local.notificationsEnabled != base.notificationsEnabled)
local.notificationsEnabled
else remote.notificationsEnabled
)
}
ฟังก์ชัน mergeProfiles ประมวลผลแต่ละฟิลด์ของโปรไฟล์อย่างอิสระ โดยเลือกเวอร์ชันที่แตกต่างจากพื้นฐาน ในกรณีที่ข้อขัดแย้ง (ทั้งคู่แตกต่างจากพื้นฐาน) ลำดับความสำคัญจะถูกกำหนดโดยกฎของแอปพลิเคชัน ในตัวอย่าง สำหรับ avatarUrl จะให้ลำดับความสำคัญกับเวอร์ชันระยะไกล สำหรับฟิลด์ที่เหลือ — ให้กับเวอร์ชันท้องถิ่น
CouchDB และ PouchDB เป็นฐานข้อมูลที่รู้จักกันดีที่สุดที่มีการสนับสนุน Merge Strategy ในตัว ในระหว่างการจำลองแบบเอกสาร CouchDB ใช้การจำลองแบบหลายเธรดพร้อมการตรวจจับข้อขัดแย้งในระดับเอกสาร เวอร์ชันพื้นฐานถูกเก็บไว้ในประวัติการแก้ไข และในกรณีที่ข้อขัดแย้ง ระบบจะเก็บรักษาสาขาที่ขัดแย้งทั้งหมดและให้ API แก่แอปพลิเคชันเพื่อแก้ไขผ่านกลไกการรวม
ใน Firebase Firestore การรวมถูกนำไปใช้ผ่านธุรกรรมด้วยการล็อกในแง่ดี นักพัฒนาสามารถระบุว่าฟิลด์บางฟิลด์ควรถูกอัปเดตแบบอะตอมมิกโดยใช้ FieldValue.serverTimestamp() และ FieldValue.arrayUnion() อย่างไรก็ตาม Firestore ไม่สนับสนุนการรวมสามทางอย่างสมบูรณ์ — เมื่อเกิดข้อขัดแย้ง ธุรกรรมจะถูกดำเนินการซ้ำด้วยข้อมูลใหม่ ซึ่งเทียบเท่ากับการลองใหม่มากกว่าการรวมที่แท้จริง
สำหรับแอปพลิเคชันมือถือบน Kotlin Multiplatform และ React Native Merge Strategy ถูกนำไปใช้ฝั่งไคลเอ็นต์ ฐานข้อมูลท้องถิ่น (SQLite, Realm) เก็บเวอร์ชันของแต่ละเอกสาร และในระหว่างการซิงโครไนซ์ ไคลเอ็นต์จะโหลดเวอร์ชันเซิร์ฟเวอร์และดำเนินการรวมในเครื่องก่อนส่งผลลัพธ์ แนวทางนี้รับประกันความสมบูรณ์ของข้อมูลแม้ในระหว่างการทำงานออฟไลน์เป็นเวลานานเมื่อมีข้อขัดแย้งสะสมมากขึ้น
คำถามที่พบบ่อย
Merge Strategy เป็นแนวทางในการแก้ไขข้อขัดแย้งที่การเปลี่ยนแปลงจากเวอร์ชันต่าง ๆ ถูกรวมเป็นสถานะเดียว แตกต่างจาก LWW การรวมรักษาการเปลี่ยนแปลงจากทั้งสองสาขาหากไม่ขัดแย้งกันในระดับฟิลด์
การรวมสามทาง ใช้เวอร์ชันพื้นฐาน (สถานะก่อนการแยก) เพื่อกำหนดว่าฟิลด์ใดที่ไคลเอ็นต์แต่ละรายเปลี่ยนแปลง การรวมสองทางเปรียบเทียบเพียงสองเวอร์ชันโดยไม่ทราบสถานะเดิม ซึ่งมักนำไปสู่ข้อขัดแย้งที่ผิดพลาด
CouchDB และ PouchDB มีการสนับสนุนการรวมสามทางในตัว Firebase Firestore ต้องการการนำไปใช้ในระดับธุรกรรม MongoDB และ Realm มีกลไกการล็อกในแง่ดี แต่ไม่มีการรวมอัตโนมัติอย่างสมบูรณ์
การรวมไม่เหมาะสม สำหรับข้อมูลที่ความเร็วในการประมวลผลสำคัญ (มากกว่า 1,000 ข้อขัดแย้งต่อวินาที) สำหรับข้อมูลสตรีมมิ่ง (ล็อก เหตุการณ์) และสำหรับกรณีที่การเปลี่ยนแปลงไม่เข้ากันโดยพื้นฐาน (เวอร์ชันสคีมาต่างกัน) ในกรณีเหล่านี้ LWW หรือ CRDT จะมีประสิทธิภาพมากกว่า
การนำไปใช้รวมถึง สามขั้นตอน: การเก็บเวอร์ชันพื้นฐานเมื่อโหลดข้อมูลจากเซิร์ฟเวอร์ การตรวจจับการเปลี่ยนแปลงในระดับฟิลด์เมื่อบันทึก และการเรียกอัลกอริทึมการรวมระหว่างการซิงโครไนซ์ เพื่อความง่าย ให้ใช้ไลบรารี JSON Patch หรือ CRDT
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม