در معماری نرمافزار توزیعشده مدرن، رویای استقرار پایگاه داده با حالت «فعال-فعال» اغلب دنبال میشود تا تأخیر را به حداقل برساند و در دسترس بودن بالا را در سراسر مناطق جهانی تضمین کند. با این حال، به محض اینکه نوشتن دوطرفه را بین نمونههای پستگرسکیو در مناطق جغرافیایی جداگانه فعال کنید، وارد قلمرو پیچیده تراکنشهای توزیعشده میشوید. بزرگترین مانع در این پارادایم، تعارض نوشتن اجتنابناپذیر است، جایی که تغییرات همزمان روی همان داده در مناطق مختلف با هم برخورد میکنند که اگر به درستی مدیریت نشود، میتواند منجر به از دست رفتن یا فساد دادهها شود.
این راهنما استراتژیهای عملی برای تشخیص و حل این تعارضهای نوشتن در پستگرسکیو را بررسی میکند و از محدودیتهای نظری فراتر رفته و راهکارهای مهندسی قابل اجرا برای محیطهای تولید ارائه میدهد.
آناتومی یک تعارض نوشتن
برخلاف پایگاههای داده تکمنطقهای سنتی، یک پیکربندی فعال-فعال فاقد یک منبع حقیقت واحد برای ترتیب تراکنشها است. وقتی دو کاربر در مناطق مختلف (مثلاً نیویورک و سنگاپور) همزمان تلاش میکنند تا همان سطر را بهروزرسانی کنند، سیستم باید تصمیم بگیرد کدام تغییر برنده میشود. در پستگرسکیو استاندارد، این کار به صورت بومی غیرممکن است زیرا این سیستم به یک مدل قابلیت سرریز سراسری سختگیرانه متکی است که اغلب با طراحی خود، با کپیبرداری فعال-فعال در تضاد است.
بدون یک مکانیزم قوی برای حل تعارض، شما در معرض خطر «بهروزرسانیهای از دست رفته» هستید، جایی که یک تراکنش به صورت خاموش تراکنش دیگر را بازنویسی میکند، یا «قفلشدگیهای مرگبار» که پردازش تراکنش را در سراسر خوشه متوقف میکنند.
استراتژی ۱: تشخیص تعارض در سطح برنامه
شفافترین رویکرد شامل انتقال منطق حل تعارض به لایه برنامه است. این کار نیازمند حفظ متادیتای نسخهبندی روی هر سطر است. با الحاق یک ستون مانند last_modified_version یا last_updated_at به جداول خود، برنامه شما میتواند تغییرات را قبل از ثبت نهایی اعتبارسنجی کند.
در اینجا یک مثال عملی از پیادهسازی قفلگذاری خوشبینانه در یک تراکنش آورده شده است:
BEGIN;
-- تلاش برای بهروزرسانی سطر فقط در صورتی که نسخه تغییر نکرده باشد
UPDATE inventory
SET quantity = quantity - 1,
last_modified_version = last_modified_version + 1
WHERE id = 123 AND last_modified_version = 5;
-- بررسی کنید چند سطر تحت تأثیر قرار گرفتند
-- اگر 0 سطر تحت تأثیر قرار گرفت، تعارض در منطقه دیگری رخ داده است
IF (ROW_COUNT() = 0) THEN
RAISE EXCEPTION 'تغییر همزمان تشخیص داده شد. لطفاً دوباره تلاش کنید.';
END IF;
COMMIT;
در این سناریو، برنامه استثنا را دریافت میکند، عملیات را با دادههای جدیدتر تکرار میکند یا کاربر را مطلع میسازد. اگرچه این استراتژی مؤثر است، اما نیازمند مدیریت دقیق در سمت کلاینت است و ممکن است تحت فشارهای بالا منجر به افزایش ترافیک تکرار شود.
استراتژی ۲: بهرهگیری از توابع حل تعارض
برای سناریوهایی که نیاز به رویکردی خودکارتر دارید، برخی از ابزارهای کپیبرداری یا تریگرهای سفارشی میتوانند مداخله کنند. اگرچه کپیبرداری منطقی استاندارد تعارضها را مدیریت نمیکند، اما افزونههایی مانند pg_replication_slot به همراه تریگرهای حل تعارض سفارشی میتوانند برای اولویتدهی به مناطق خاص یا اعمال منطق «آخرین نوشتن برنده» بر اساس زمانسنجها مهندسی شوند.
با این حال، تکیه صرف بر منطق «آخرین نوشتن برنده» مبتنی بر زمانسنج بدون همگامسازی دقیق ساعت، خطرناک است. در نظر بگیرید که یک تریگر زمانسنج را با یک بافر مقایسه میکند:
CREATE OR REPLACE FUNCTION resolve_conflict() RETURNS trigger AS $$
BEGIN
-- اگر سطر ورودی جدیدتر است، آن را نگه دارید؛ در غیر این صورت، سطر محلی را نگه دارید
-- این یک منطق سادهشده برای نمایش است
IF (TG_OP = 'INSERT' AND NEW.timestamp > (SELECT timestamp FROM conflicts_table WHERE id = NEW.id)) THEN
RETURN NEW;
ELSE
RETURN OLD;
END IF;
END;
$$ LANGUAGE plpgsql;
نکته بسیار مهم این است که پستگرسکیو به صورت بومی از تریگرهای دوطرفه برای حل تعارض در یک خوشه بدون هزینههای قابل توجه پشتیبانی نمیکند. ابزارهایی مانند pg_partman یا میانیافزارهای تخصصی مانند Citus یا async-replication اغلب چارچوبهای بهتری برای این جریانهای منطق سفارشی ارائه میدهند.
استراتژی ۳: طراحی طرحواره برای پیشگیری از تعارض
کارآمدترین استراتژی حل تعارض، اغلب استراتژیای است که از وقوع تعارضها از ابتدا جلوگیری میکند. این کار از طریق «شردینگ بر اساس شناسه کاربر» یا «پارتیشنبندی داده بر اساس منطقه» محقق میشود.
اگر دادههای شما به گونهای قابل تفکیک منطقی باشند که یک کاربر در آمریکا هرگز دادهای را که توسط یک کاربر در اتحادیه اروپا ایجاد شده است، تغییر ندهد، تعارضها غیرممکن میشوند. به عنوان مثال، در یک سیستم مدیریت کاربر، میتوانید الزام کنید که تمام دادههای متعلق به region_code = 'US' به صورت فیزیکی در خوشه آمریکا ذخیره و فقط به آن کپی شوند، در حالی که خوشه اروپا فقط دادههای region_code = 'EU' را مدیریت میکند. این محدودیت معماری، سطح تماس برای تعارضهای نوشتن را به شدت کاهش میدهد.
نتیجهگیری
ساخت یک خوشه پستگرسکیو فعال-فعال چندمنطقهای یک تلاش با پاداش بالا و ریسک بالا است. هیچ راه حل جادویی وجود ندارد؛ بهترین راه حل کاملاً به نیازهای سازگاری خاص شما بستگی دارد. برای سازگاری قوی، در نظر بگیرید که از کپیهای خواندنفقط یا پیکربندیهای رهبر/پیرو استفاده کنید. برای در دسترس بودن و تأخیر کم، سازگاری نهایی را بپذیرید و مکانیزمهای قوی حل تعارض را پیادهسازی کنید، چه از طریق قفلگذاری خوشبینانه در سطح برنامه و چه از طریق طراحی طرحواره.
هنگام مهندسی سیستم خود، همیشه یکپارچگی داده را بر در دسترس بودن خام ترجیح دهید. یک نوشتن ناموفق، ترجیح بر یک پایگاه داده فاسد است. با درک مکانیک تعارضهای نوشتن و اعمال این استراتژیهای عملی، میتوانید پایگاههای داده جهانی مقاومی بسازید که در مقیاس بزرگ به طور قابل اعتماد عملکرد دارند.