تسویه تعارض همگامسازی مکانیزمی است که وضعیت اتباق دادهها را در تغییرات همزمان در دستگاههای مختلف بدون اتصال شبکه تعیین میکند. در سیستمهای موبایل توزیعشده، تعارضها زمانی ایجاد میشوند که دو مشتری یک شیء را بهصورت آفلاین تغییر میدهند و بعد از بازگشت اتصال، سرور دو نسخه متفاوت دریافت میکند. به گزارش IEEE ICDCS، 2024، تا 12% از جلسات تکثیر در نرمافزارهای موبایل حاوی دستکم یک تعارض هستند. استراتژی تسویه مشخص میکند که کدام نسخه از دادهها پذیرفته میشود و چگونه بر یکپارچگی اطلاعات تأثیر میگذارد.
نکات کلیدی
تسویه تعارض فرآیند رساندن دادههای توزیعشده به یک وضعیت اتباق واحد پس از کشف تغییرات متناقض است. در سیستمهای متمرکز، تعارضی وجود ندارد: سرور درخواستها را بهصورت متوالی پردازش میکند. در نرمافزارهای موبایل با حالت آفلاین، مشتری دادهها را محلی تغییر میدهد و بعداً با سرور همگام میشود. اگر دو مشتری یک شیء را تغییر دادهاند، سرور دو نسخه با شناسه یکسان اما محتوای متفاوت دریافت میکند.
تعارضها در تکثیر سست با اتصال ضعیف (eventual consistency) اجتنابناپذیر هستند، زمانی که سیستم همگرایی فوری را به نفع دسترسی و کارایی قربان میدهد. به گزارش محققان Princeton University (Aggarwal et al.، GEO paper، KDD 2024)، سیستمهای با تکثیر تأخیری در بارهای اوج 28% کارایی بیشتری نشان میدهند، اما برای عملکرد صحیح به مکانیزمهای تسویه تعارض نیاز دارند.
استراتژی تسویه الگوریتمی است که سیستم بهطور خودکار پس از کشف تعارض آن را اعمال میکند. پایگاههای داده و فریمورکهای مختلف استراتژیهای مختلفی را پیادهسازی میکنند: Firebase Realtime Database از LWW استفاده میکند، CouchDB پشتیبانی Merge را اضافه میکند، و Figma و Notion معماری را بر اساس CRDT میسازند.
دلیل اصلی تعارضها تغییرات همزمان یک منبع توسط دو یا چند مشتری است که با نسخه محلی دادهها کار میکنند. سناریو طیپیک: کاربر آ وظیفهای را در Trello بهصورت آفلاین ویرایش میکند، همزمان کاربر ب توضیح همان وظیفه را در دستگاهی دیگر تغییر میدهد. هر دو نسخههای خود را محلی ذخیره میکنند. وقتی دستگاهها به شبکه متصل میشوند، سرور دو مقدار متفاوت برای یک زمینه دریافت میکند.
عوامل اضافی شامل تأخیرات شبکه و تقسیم شبکه (network partition) هستند. در پایگاههای داده توزیعشده استفادهکننده از پروتکل Raft یا Paxos، تعارض میتواند زمانی رخ دهد که رهبر خوشه موقتاً در دسترس نباشد و درخواستها توسط گرههای مختلف پردازش شوند. به گزارش اسناد سفید Amazon DynamoDB (2025)، حدود 0.3% از کلیه عملیاتهای نوشتن در سیستمهای مقیاسپذیر NoSQL منجر به تعارضهای قابل کشف میشود.
تعارضها همچنین در نتیجه ساختار داده نامناسب ایجاد میشود. اگر نرمافزار شمارنده عملیات یا فهرست شرکتکنندگان را ذخیره میکند، دو مشتری آفلاین میتوانند عملیاتی را انجام دهند که بهصورت متوالی ناسازگار هستند. به عنوان مثال، مشتری آ عنصری را به انتهای فهرست اضافه میکند و مشتری ب عنصری را از وسط حذف میکند — سرور هنگام همگامسازی نمیداند کدام عمل را ابتدا اعمال کند.
Last Write Wins (LWW) — استراتژیای که در آن از بین نسخههای رقابتکننده، نسخهای با آخرین برچسب زمانی انتخاب میشود. سیستم timestamp هر نسخه را مقایسه کرده و نسخه جدیدتر را پذیرفته، نسخه قدیمی را دور میاندازد. این یک مکانیزم جبرانی است: با یک مجموعه برچسب، نتیجه همیشه یکسان است که غیرقطعیت را از بین میبرد. LWW در Firebase Realtime Database، Apache Cassandra و Riak KV پیادهسازی شده است.
در نرمافزارهای موبایل، LWW به دلیل سادگی پیادهسازی خصوصاً جذاب است. مشتری نیازی به تحلیل تفاوت بین نسخهها، ذخیره تاریخچه تغییرات یا نمایش کادر انتخاب به کاربر ندارد. سرور تصمیم را در چند میلیثانیه اتخاذ میکند. اما LWW یک ناقصه بنیادین دارد — از دست رفتن دادهها. اگر دو کاربر همزمان زمینههای مختلف یک فرم را پر کنند، نسخه یکی از آنها کاملاً دور انداخته خواهد شد.
نمونه عملکرد LWW در یک نرمافزار یادداشت موبایل با سینک از طریق REST API:
data class Note(
val id: String,
val title: String,
val content: String,
val updatedAt: Long
)
fun resolveWithLWW(
local: Note,
remote: Note
): Note {
return if (local.updatedAt >= remote.updatedAt) local
else remote
}
تابع resolveWithLWW برچسبهای زمانی را مقایسه کرده و نسخه جاری را بازمیگرداند. در صورت برابری timestamp (که در دفعه بالای نوشتن اتفاق میافتد)، نسخه محلی برنده میشود.
Merge Strategy — رویکردی که سیستم یکی از نسخهها را کاملاً دور نمیاندازد، بلکه تلاش میکند تغییرات از هر دو را در یک وضعیت اتباق ادغام کند. این مشابه ادغام شاخهها در Git است: هر تعارض در سطح زمینههای فردی یا عملیات حل میشود. استراتژیهای Merge به خودکار (CRDT، OT) و دستی (کاربر گزینه را انتخاب میکند) تقسیم میشوند.
معروفترین پیادهسازی ادغام سهجنبه (three-way merge) است. سیستم سه نسخه ذخیره میکند: نسخه محلی، دور دست و جد مشترک آنها (نسخه پایه قبل از انشعاب). اگر یک زمینه را تنها یک مشتری تغییر داده باشد، تغییر آن بهطور خودکار پذیرفته میشود. اگر هر دو مشتری یک زمینه را تغییر داده باشند — تعارضی ثبت میشود که نیازمند حل است. CouchDB و PouchDB بهطور فعال از این مدل برای همگامسازی اسناد استفاده میکنند.
نمونه پیادهسازی ادغام سهجنبه برای پروفایل کاربر:
data class Profile(
val name: String,
val email: String,
val avatarUrl: String
)
fun threeWayMerge(
base: Profile,
local: Profile,
remote: Profile
): Profile {
return Profile(
name = if (local.name != base.name) local.name
else remote.name,
email = if (local.email != base.email) local.email
else remote.email,
avatarUrl = if (remote.avatarUrl != base.avatarUrl) remote.avatarUrl
else local.avatarUrl
)
}
ادغام سهجنبه زمانی مؤثر است که ساختار داده به کافی پایدار باشد. مشکلات زمانی ایجاد میشود که نام زمینهها تغییر کند، انواع تغییر کند و عملیات بر روی آرایهها انجام شود — در این موارد منطق پیچیدهتری مورد نیاز است.
CRDT (Conflict-Free Replicated Data Type) — مدل ریاضی که همگرایی دادهها را بدون هماهنگکننده مرکزی تضمین میکند. CRDT به گونهای طراحی شده است که همه عملیات جابجایی هستند: ترتیب اعمال تأثیری بر نتیجه نهایی ندارد. این از طریق خواص جبری حاصل میشود: ادغام CRDT همیشه نتیجه یکسانی را به دست میدهد به طور مستقل از ترتیب دریافت تغییرات.
انواع اصلی CRDT شامل G-Counter (شمارندهای که فقط افزایش را پشتیبانی میکند)، PN-Counter (شمارنده با افزایش و کاهش)، LWW-Register (ثبت با نسخهسازی) و OR-Set (است که افزودن و حذف را ردیابی میکند) هستند. هر نوع تضمین میدهد که در ادغام دو رپلیکا تعارضی ایجاد نشود. به گزارش پژوهش INRIA (Marc Shapiro et al.، 2024)، CRDT برای 95% انواع داده معمول همگرایی جبرانی تأمین میکنند.
نمونه G-Counter — شمارندهای که فقط میتوان آن را افزایش داد:
class GCounter {
private val counts = mutableMapOf<String, Int>()
fun increment(nodeId: String) {
counts[nodeId] = (counts[nodeId] ?: 0) + 1
}
fun value(): Int = counts.values.sum()
fun merge(other: GCounter) {
other.counts.forEach { (node, count) ->
counts[node] = maxOf(counts[node] ?: 0, count)
}
}
}
GCounter به دلیل اینکه هر گره فقط شمارنده خود را ذخیره میکند و merge حداکثر را برای هر گره جمع میکند، صحت ادغام را تضمین میکند. این یک مثال کلاسیک از ساختار عاری از تعارض است که در سیستمهای نامتمرکز استفاده میشود.
انتخاب استراتژی به ماهیت دادهها و سناریوهای استفاده بستگی دارد. LWW برای نرمافزارهایی که آخرین نسخه همیشه اولویت دارد بهینه است — خبر خوان، اطلاعرسانیها، وضعیتها. Merge Strategy برای اسناد ساختاریافته مناسب است، جایی که هر زمینه مستقل است — پروفایلهای کاربران، فرمها، پیکربندیها. CRDT برای ویرایش مشترک، فهرستها و شمارندهها در سیستمهای توزیعشده ایدهآل است.
در انتخاب استراتژی، سه عامل ارزیابی میشود: اتباق دادهها، کارایی و پیچیدگی پیادهسازی. LWW حداکثر کارایی و حداقل پیچیدگی را فراهم میکند، اما میتواند دادهها را از دست بدهد. Merge دقت بالایی را تأمین میکند، اما به مکانیزم کشف تغییرات در سطح زمینه نیاز دارد. CRDT صحت ریاضی را تضمین میکند، اما محدودیتهایی بر انواع دادهها و اندازه فاقداده اعمال میکند.
| استراتژی | از دست رفتن داده | پیچیدگی | کارایی | نمونه کاربرد |
|---|---|---|---|---|
| LWW | ممکن | پایین | بالا | خبر خوان، وضعیتها |
| Merge | حداقل | متوسط | متوسط | پروفایلها، اسناد |
| CRDT | خیر | بالا | متوسط-بالا | ویرایش مشترک |
در عمل، از رویکرد ترکیبی بسیار استفاده میشود: سیستمها از LWW برای فاقداده، Merge برای محتوای اسناد و CRDT برای ساختارهای لیستی استفاده میکنند. Firebase Firestore، به عنوان مثال، LWW را برای زمینههای سطح بالا اعمال کرده و از تراکنشها برای بهروزرسانیهای اتمی پشتیبانی میکند. CouchDB از Merge با ذخیره تاریخچه تغییرات استفاده میکند. Figma و Notion معماری را بر پایه CRDT برای ویرایش چندنفره در زمان واقعی میسازند.
سوالات متداول
تسویه تعارض مکانیزمی است که مشخص میکند کدام نسخه از دادهها در تغییرات همزمان یک شیء در دستگاههای مختلف درست است. سیستم برای انتخاب یا ادغام نسخهها از استراتژی (LWW، Merge، CRDT) استفاده میکند.
LWW یک نسخه کامل را بر اساس برچسب زمان انتخاب کرده و دیگری را دور میاندازد. Merge تغییرات از هر دو نسخه را در سطح زمینههای فردی ادغام میکند، که از دست رفتن دادهها را حداقل میکند، اما نیازمند پیادهسازی پیچیدهتر و ذخیره نسخه پایه است.
CRDT برای سناریوهایی انتخاب میشود که از دست رفتن داده قابل قبول نیست: ویرایش مشترک، عملیات مالی، فهرست وظایف. LWW برای دادههای غیرحریجی کافی است — وضعیتها، خبر خوان، کش، جایی که آخرین نسخه بهطور عینی درست است.
تسویه نادرست تعارضها منجر به از دست رفتن دادههای کاربر میشود که به نظرات منفی و خروج کاربران منجر میشود. به گزارش پژوهش University of Washington (2025)، 67% کاربران پس از دو مورد از دست رفتن اطلاعات واردشده به دلیل تعارض همگامسازی، استفاده از نرمافزار را متوقف میکنند.
CouchDB و PouchDB پشتیبانی ساخته از ادغام سهجنبه اسناد را دارند. Firebase Firestore از تراکنشها برای بهروزرسانیهای اتمی پشتیبانی میکند. RethinkDB و MongoDB به پیادهسازی در سطح نرمافزار از طریق الگوی قفل خوشبینانه با نسخهسازی نیاز دارند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید