Database Engineering

تسلط بر Event Sourcing و CQRS در CockroachDB: راهنمای سازگاری توزیع‌شده

در عصر برنامه‌های بومی ابری، ساخت سیستم‌هایی که هم در دسترس بالا و هم سازگاری دقیق داشته باشند دیگر یک انتخاب لوکس نیست، بلکه یک ضرورت است. با دور شدن توسعه‌دهندگان از معماری‌های یکپارچه، الگوهایی مانند تفکیک مسئولیت دستورات و پرس‌وجوها (CQRS) و منبع رویداد (ES) محبوبیت زیادی یافته‌اند. با این حال، پیاده‌سازی این الگوها در یک محیط توزیع‌شده، چالش‌های پیچیده‌ای را در زمینه سازگاری داده‌ها و یکپارچگی تراکنشی ایجاد می‌کند. این پست بررسی می‌کند که چگونه می‌توان از CockroachDB، یک پایگاه داده SQL توزیع‌شده، به‌طور مؤثر برای پیاده‌سازی این الگوها استفاده کرد و در عین حال تضمین‌های قوی‌ای که توسعه‌دهندگان از پایگاه‌های داده رابطه‌ای سنتی انتظار دارند، حفظ شود.

چرا CockroachDB برای Event Sourcing؟

منبع رویداد (Event Sourcing) یک الگوی معماری است که در آن تغییرات حالت به صورت دنباله‌ای از رویدادها ذخیره می‌شوند. برخلاف برنامه‌های CRUD سنتی که حالت فعلی را در محل به‌روزرسانی می‌کنند، ES رویدادهای جدید را به یک لاگ غیرقابل تغییر می‌افزاید. این رویکرد یک ردیابی حسابرسی کامل ارائه می‌دهد و پرس‌وجوهای زمانی پیچیده را امکان‌پذیر می‌سازد. با این حال، این رویکرد به یک لایه ذخیره‌سازی قوی نیاز دارد که قادر به مدیریت نرخ نوشتن بالا و اطمینان از اعمال رویدادها به ترتیب دقیق وقوع آن‌ها باشد.

CockroachDB در این زمینه درخشش دارد زیرا رابط آشنا SQL استاندارد را با مقیاس‌پذیری افقی پایگاه‌های داده NoSQL ترکیب می‌کند. مزیت رقابتی منحصر به فرد آن، پشتیبانی از استقرارهای چندمنطقه‌ای با سازگاری قوی است. برای منبع رویداد، این بدان معناست که لاگ رویداد شما در سراسر جغرافیا سازگار باقی می‌ماند و از شرایط رقابتی (Race Conditions) جلوگیری می‌کند که در آن‌ها رویدادها ممکن است به دلیل شکاف‌های شبکه جابجا یا تکرار شوند.

پیاده‌سازی CQRS با مدل‌های خواندن و نوشتن جداگانه

CQRS مدل خواندن داده‌ها را از مدل نوشتن داده‌ها جدا می‌کند. در یک زمینه SQL توزیع‌شده، این جداسازی امکان مقیاس‌پذیری مستقل را فراهم می‌کند. شما می‌توانید لایه نوشتن خود را برای مدیریت ورود رویدادهای با حجم بالا و لایه خواندن خود را برای مدیریت پرس‌وجوهای تحلیلی پیچیده بدون تداخل مقیاس دهید.

در CockroachDB، می‌توانید این کار را با استفاده از طرح‌های (schemas) جداگانه یا حتی پایگاه‌های داده جداگانه برای عملیات نوشتن و خواندن پیاده‌سازی کنید. برای اطمینان از همگام‌سازی مدل خواندن با آخرین رویدادها، می‌توانید از ضبط تغییرات داده (CDC) یا تکثیر مبتنی بر تریگر استفاده کنید. این امر تضمین می‌کند که جداول بهینه‌شده برای خواندن (که ممکن است برای عملکرد نرمالیزه نشده باشند) همیشه بازتاب‌دهنده منبع حقیقت حفظ‌شده در انبار رویداد باشند.

مدیریت تراکنش‌های توزیع‌شده و سازگاری

یکی از بزرگترین سوءتفاهم‌ها درباره منبع رویداد این است که تنها به سازگاری نهایی (Eventual Consistency) نیاز دارد. اگرچه سمت خواندن اغلب از سازگاری نهایی بهره می‌برد، اما خود جریان رویداد باید ترتیب و یکپارچگی دقیق را حفظ کند. تراکنش‌های توزیع‌شده CockroachDB، که بر اساس پروتکل Percolator هستند، تضمین می‌کنند که حتی زمانی که رویدادها به گره‌های مختلف نوشته می‌شوند، یا همه با هم تأیید (Commit) می‌شوند یا همه برگشت داده می‌شوند، که خواص ACID را حفظ می‌کند.

سناریویی را در نظر بگیرید که یک کاربر سفارشی ثبت می‌کند. شما باید یک رویداد OrderCreated را اضافه کنید و تعداد موجودی را به‌روزرسانی نمایید. در یک پایگاه داده SQL سنتی، این یک تراکنش ساده است. در CockroachDB، می‌توانید همان سطح از اتمی بودن را دستیابی کنید:


BEGIN;

-- Append the event to the event store
INSERT INTO events (id, aggregate_id, event_type, payload, created_at)
VALUES (gen_random_uuid(), 'order-123', 'ORDER_CREATED', '{"quantity": 1}'::JSONB, NOW());

-- Update the inventory in a separate table
UPDATE inventory SET stock_count = stock_count - 1 WHERE product_id = 'prod-456';

COMMIT;

زیبایی این رویکرد این است که اگر به‌روزرسانی موجودی شکست بخورد (مثلاً به دلیل کمبود موجودی)، کل تراکنش برگشت داده می‌شود و هیچ رویدادی ذخیره نمی‌شود. این امر از ایجاد رویدادهای «فانتوم» که تغییرات حالتی را توصیف می‌کنند که هرگز اتفاق نیفتاده‌اند، جلوگیری می‌کند.

بهینه‌سازی برای عملکرد

در حالی که CockroachDB سازگاری را به‌طور خودکار مدیریت می‌کند، تنظیم عملکرد برای بارهای کاری منبع رویداد با نرخ بالا همچنان ضروری است. از درج‌های دسته‌ای برای گروه‌بندی رویدادها استفاده کنید و از پارتیشن‌بندی روی ستون created_at برای بهینه‌سازی پرس‌وجوهای سری زمانی بهره ببرید. علاوه بر این، در نظر بگیرید که از جداول نمای‌شده (Materialized Views) برای مدل‌های خواندن خود استفاده کنید تا عملیات پیچیده Join را از جدول اصلی رویدادها بردارید.

نتیجه‌گیری

پیاده‌سازی منبع رویداد و CQRS در یک محیط توزیع‌شده می‌تواند ترسناک باشد، اما ابزارهایی مانند CockroachDB پیچیدگی را ساده می‌کنند. با ارائه تضمین‌های سازگاری قوی از پیش‌فرض، توسعه‌دهندگان می‌توانند بر منطق کسب‌وکار تمرکز کنند به جای درگیری با الگوریتم‌های اجماع توزیع‌شده. هنگامی که سیستم بعدی خود را طراحی می‌کنید، در نظر بگیرید که ترکیب معماری رویداد-محور و SQL توزیع‌شده چگونه می‌تواند مقیاس‌پذیری و قابلیت اطمینانی را که کاربران شما درخواست می‌کنند، فراهم کند.

Share: