Merge Strategy là chiến lược hợp nhất dữ liệu trong đó các thay đổi xung đột từ các phiên bản khác nhau được kết hợp thành một trạng thái nhất quán duy nhất thay vì thay thế phiên bản này bằng phiên bản khác. Không giống như Last Write Wins, việc hợp nhất cố gắng giữ lại các thay đổi từ tất cả các nhánh, giảm thiểu mất dữ liệu. Theo tài liệu Apache CouchDB, 2025, hợp nhất ba chiều (three-way merge) là cơ chế giải quyết xung đột tiêu chuẩn trong các cơ sở dữ liệu hướng tài liệu. Hợp nhất ba chiều sử dụng phiên bản cơ sở chung để xác định trường nào đã được thay đổi bởi mỗi máy khách.
Những điểm chính
Merge Strategy là tập hợp các thuật toán kết hợp các phiên bản dữ liệu xung đột thay vì chọn một trong số chúng. Trong các ứng dụng di động, Hợp nhất được sử dụng khi hai máy khách độc lập chỉnh sửa các trường hoặc thuộc tính khác nhau của cùng một đối tượng. Thay vì loại bỏ hoàn toàn phiên bản cũ hơn (như trong LWW), hệ thống phân tích sự khác biệt ở cấp độ từng trường và tạo ra một đối tượng kết quả chứa các thay đổi từ cả hai phiên bản.
Sự khác biệt chính giữa Hợp nhất và LWW là bảo toàn các thay đổi của mỗi người dùng với điều kiện chúng không mâu thuẫn với nhau. Nếu người dùng A thay đổi tên nhiệm vụ và người dùng B thay đổi mô tả, Hợp nhất giữ lại cả hai thay đổi. Nếu cả hai thay đổi cùng một trường — một xung đột được ghi nhận và cần được giải quyết. Điều này làm cho Hợp nhất trở nên ưu tiên cho các ứng dụng nơi người dùng làm việc cộng tác trên cùng một dữ liệu.
Theo một báo cáo từ Stripe Engineering Blog (2025), việc triển khai Merge Strategy thay vì LWW đã giảm số lượng khiếu nại của người dùng về mất dữ liệu xuống 76% trong ứng dụng quản lý dự án di động của họ. Tuy nhiên, thời gian xử lý xung đột đã tăng thêm 15–30 ms, được coi là cái giá có thể chấp nhận được cho tính toàn vẹn dữ liệu.
Hợp nhất ba chiều (three-way merge) là cách triển khai phổ biến nhất của Merge Strategy. Cơ chế hoạt động với ba phiên bản dữ liệu: cơ sở (trạng thái trước khi phân kỳ), cục bộ (phiên bản của máy khách hiện tại) và từ xa (phiên bản máy chủ). Hệ thống so sánh từng trường của phiên bản cục bộ và từ xa với cơ sở để xác định bên nào đã thay đổi trường nào.
Logic quyết định rất đơn giản: nếu chỉ một máy khách thay đổi trường (so với cơ sở), thay đổi của họ được chấp nhận tự động. Nếu cả hai máy khách thay đổi cùng một trường — một xung đột được ghi nhận, có thể được giải quyết tự động (theo ưu tiên) hoặc ủy quyền cho người dùng. Nếu không có máy khách nào thay đổi trường — giá trị cơ sở vẫn được giữ nguyên. Cách tiếp cận này đảm bảo rằng các thay đổi độc lập không bị mất cũng không xung đột.
Thuật toán hợp nhất ba chiều ở cấp từ điển trường:
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
}
Hàm threeWayMerge xử lý tuần tự tất cả các khóa từ ba phiên bản. Nếu giá trị cục bộ khớp với cơ sở — thay đổi từ xa được chấp nhận. Nếu giá trị từ xa khớp với cơ sở — thay đổi cục bộ được chấp nhận. Nếu cả hai khác với cơ sở nhưng bằng nhau — một trong hai được chấp nhận. Một xung đột thực sự chỉ được ghi nhận khi cả hai bên có các thay đổi khác nhau.
Giải quyết tự động được áp dụng khi các thay đổi không trùng lặp hoặc khi hệ thống có thể xác định giá trị chính xác dựa trên các quy tắc. Ví dụ: đối với trường số, bạn có thể chọn giá trị tối đa; đối với trường văn bản — nối chuỗi hoặc phiên bản mới hơn. CouchDB sử dụng hợp nhất tự động cho các trường tài liệu JSON và cho mảng — nối chuỗi với loại bỏ trùng lặp.
Giải quyết thủ công cần thiết khi hai người dùng thay đổi cùng một trường theo cách khác nhau. Trong trường hợp này, ứng dụng hiển thị hộp thoại với ba tùy chọn: \u201cchấp nhận phiên bản cục bộ\u201d, \u201cchấp nhận phiên bản từ xa\u201d hoặc \u201chợp nhất thủ công\u201d. Theo nghiên cứu từ CMU (Đại học Carnegie Mellon, 2024), giải quyết thủ công làm giảm sự hài lòng của người dùng xuống 40%, do đó, hợp nhất tự động cần được tối đa hóa.
Các chiến lược giải quyết cho các loại trường khác nhau:
| Loại trường | Chiến lược tự động | Thay thế thủ công |
|---|---|---|
| Số (bộ đếm) | Lấy giá trị tối đa | Hiển thị cả hai giá trị |
| Văn bản (chuỗi) | Chọn theo thời gian | Trình chỉnh sửa được tô sáng |
| Boolean | Ưu tiên theo vai trò | Ba tùy chọn lựa chọn |
| Mảng (danh sách) | Hợp nhất với loại bỏ trùng lặp | Chọn từng phần tử |
| Đối tượng lồng nhau | Hợp nhất đệ quy | Hiển thị sự khác biệt |
Hãy xem xét việc triển khai Merge Strategy cho hồ sơ người dùng trong ứng dụng di động với đồng bộ hóa qua REST API. Hồ sơ bao gồm tên, email, hình đại diện và cài đặt thông báo. Mỗi trường có thể được thay đổi độc lập trên các thiết bị khác nhau của người dùng.
Lớp dữ liệu hồ sơ với kiểm soát phiên bản ở cấp trường:
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
)
}
Hàm mergeProfiles xử lý độc lập từng trường hồ sơ, chọn phiên bản khác với cơ sở. Trong trường hợp xung đột (cả hai khác với cơ sở), mức ưu tiên được xác định bởi các quy tắc của ứng dụng. Trong ví dụ, đối với avatarUrl, ưu tiên được dành cho phiên bản từ xa, đối với các trường còn lại — cho phiên bản cục bộ.
CouchDB và PouchDB là các cơ sở dữ liệu nổi tiếng nhất với hỗ trợ Merge Strategy tích hợp. Trong quá trình sao chép tài liệu, CouchDB sử dụng sao chép đa luồng với phát hiện xung đột ở cấp tài liệu. Phiên bản cơ sở được lưu trữ trong lịch sử sửa đổi và trong trường hợp xung đột, hệ thống giữ lại tất cả các nhánh xung đột và cung cấp cho ứng dụng API để giải quyết chúng thông qua cơ chế hợp nhất.
Trong Firebase Firestore, Hợp nhất được triển khai thông qua các giao dịch với khóa lạc quan. Nhà phát triển có thể chỉ định rằng các trường nhất định phải được cập nhật nguyên tử bằng cách sử dụng FieldValue.serverTimestamp() và FieldValue.arrayUnion(). Tuy nhiên, Firestore không hỗ trợ hợp nhất ba chiều hoàn chỉnh — khi xảy ra xung đột, giao dịch được thử lại với dữ liệu mới, tương đương với việc thử lại chứ không phải hợp nhất thực sự.
Đối với các ứng dụng di động trên Kotlin Multiplatform và React Native, Merge Strategy được triển khai ở phía máy khách. Cơ sở dữ liệu cục bộ (SQLite, Realm) lưu trữ phiên bản của mỗi tài liệu và trong quá trình đồng bộ hóa, máy khách tải phiên bản máy chủ và thực hiện hợp nhất cục bộ trước khi gửi kết quả. Cách tiếp cận này đảm bảo tính toàn vẹn dữ liệu ngay cả khi hoạt động ngoại tuyến kéo dài khi nhiều xung đột tích lũy hơn.
Các câu hỏi thường gặp
Merge Strategy là cách tiếp cận giải quyết xung đột trong đó các thay đổi từ các phiên bản khác nhau được kết hợp thành một trạng thái duy nhất. Không giống như LWW, Hợp nhất giữ lại các thay đổi từ cả hai nhánh nếu chúng không mâu thuẫn với nhau ở cấp trường.
Hợp nhất ba chiều sử dụng phiên bản cơ sở (trạng thái trước khi phân kỳ) để xác định trường nào mỗi máy khách đã thay đổi. Hợp nhất hai chiều chỉ so sánh hai phiên bản mà không biết trạng thái ban đầu, thường dẫn đến xung đột sai.
CouchDB và PouchDB có hỗ trợ hợp nhất ba chiều tích hợp. Firebase Firestore yêu cầu triển khai ở cấp giao dịch. MongoDB và Realm cung cấp cơ chế khóa lạc quan nhưng không có hợp nhất tự động hoàn chỉnh.
Hợp nhất không phù hợp cho dữ liệu mà tốc độ xử lý là quan trọng (hơn 1000 xung đột mỗi giây), cho dữ liệu truyền phát (nhật ký, sự kiện) và cho các trường hợp mà các thay đổi không tương thích về cơ bản (các phiên bản lược đồ khác nhau). Trong những trường hợp này, LWW hoặc CRDT sẽ hiệu quả hơn.
Việc triển khai bao gồm ba bước: lưu trữ phiên bản cơ sở khi tải dữ liệu từ máy chủ, phát hiện thay đổi ở cấp trường khi lưu và gọi thuật toán hợp nhất trong quá trình đồng bộ hóa. Để đơn giản hóa, hãy sử dụng các thư viện JSON Patch hoặc CRDT.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm