ساخت سیستمهای توزیعشده جغرافیایی دیگر یک تجمل نیست؛ بلکه یک ضرورت است. چه برای رعایت مقررات، بهینهسازی تأخیر یا بازیابی از فاجعه، تیمها به طور فزایندهای برنامههای خود را در چندین منطقه AWS یا زونهای Google Cloud پیادهسازی میکنند. با این حال، انتقال دادهها بین مناطق یک چالش بنیادین را ایجاد میکند: حفظ تضمینهای یکپارچگی قوی در حالی که عملکرد قابل قبولی را حفظ میکنیم. در این مقاله، ما بررسی خواهیم کرد که چگونه خوانشهای خطیپذیر را با استفاده از دو سرویس پایگاه داده مدیریتشده پیشرو، یعنی Google Cloud Spanner و Amazon DynamoDB، پیادهسازی کنیم.
چرا خطیپذیری اهمیت دارد
خطیپذیری قویترین سطح مدل یکپارچگی در سلسلهمراتب است. این تضمین میکند که هر خوانش، مقداری را که توسط آخرین نوشتار تکمیلشده ثبت شده است، برمیگرداند و اینکه هر نوشتار به صورت اتمی در سراسر سیستم اعمال میشود. در یک زمینه چندمنطقهای، دستیابی به این امر دشوار است زیرا تقسیمات شبکه رایج هستند و فاصله فیزیکی تأخیر قابل توجهی را ایجاد میکند. بدون خطیپذیری، کاربران ممکن است پس از یک نوشتار، دادههای منقضیشده را مشاهده کنند که منجر به باگهای حیاتی در تراکنشهای مالی، مدیریت موجودی یا وضعیتهای نشست میشود. برای مسیرهای حیاتی در برنامه شما، خوانشهای خطیپذیر شبکهای ایمنی را فراهم میکنند که مدلهای یکپارچگی نهایی به سادگی نمیتوانند ارائه دهند.
Google Cloud Spanner: خطیپذیری واقعی
Spanner از پایه برای ارائه یکپارچگی قوی در مقیاس جهانی طراحی شده است. از تکنیکی به نام "TrueTime" برای مدیریت عدم قطعیت ساعت در چندین مرکز داده استفاده میکند. هنگام انجام خوانش در Spanner، میتوانید سطح جداسازی را برای اطمینان از خطیپذیری مشخص کنید. به طور پیشفرض، خوانشهای Spanner سریالپذیر هستند که حتی قویتر از خطیپذیر است، اما میتوانید سطوح جداسازی خود را برای موارد استفاده خاص تنظیم کنید.
from google.cloud import spanner
def get_account_balance(client, account_id):
with client.snapshot(
read_timestamp=None, # Uses strong consistency by default
staleness=None,
min_read_timestamp=None,
max_staleness=None
) as snapshot:
sql = "SELECT balance FROM Accounts WHERE id = @id"
params = {"id": account_id}
param_types = {"id": spanner.param_types.INT64}
result = snapshot.execute_sql(sql, params=params, param_types=param_types)
for row in result:
return row[0]
به استفاده از read_timestamp=None در اسنپشات توجه کنید. این نشاندهنده یک خوانش قوی است که برای اطمینان از خطیپذیری خوانش، منتظر ساعت میماند. اگرچه این به دلیل عدم قطعیت ساعت تأخیر کوچکی ایجاد میکند، اما تضمین میکند که هرگز مقداری را که توسط نوشتار جدیدتری جایگزین شده است، نمیخوانید. برای خوانشهای حساس به تأخیر که تأخیر جزئی قابل قبول است، معماری Spanner به شما امکان میدهد به طور مؤثر تعادلی بین یکپارچگی و سرعت ایجاد کنید.
Amazon DynamoDB: دستیابی به خطیپذیری با خوانشهای شرطی
DynamoDB عمدتاً یک مخزن با یکپارچگی نهایی است، اما گزینه خوانش "یکپارچگی قوی" را ارائه میدهد. با تنظیم ConsistentRead=True در درخواست خوانش خود، اطمینان حاصل میکنید که خوانش، آخرین نوشتار را منعکس میکند. با این حال، در یک تنظیم چندمنطقهای با استفاده از DynamoDB Global Tables، دستیابی به خطیپذیری واقعی نیاز به مدیریت دقیق نسخهبندی و بهروزرسانیهای شرطی دارد.
DynamoDB Global Tables به طور پیشفرض از استراتژی حل تعارض "آخرین نویسنده برنده است" استفاده میکند. برای دستیابی به رفتار خطیپذیر، باید یک بردار نسخه یا یک ساعت منطقی را در لایه برنامه خود پیادهسازی کنید. هنگامی که نوشتاری رخ میدهد، یک شماره نسخه را افزایش دهید. هنگام خوانش، اطمینان حاصل کنید که خوانش با آخرین نسخه در سراسر همه مناطق یکپارچه است.
import boto3
dynamodb = boto3.client('dynamodb', region_name='us-east-1')
def get_item_strongly_consistent(table_name, key):
response = dynamodb.get_item(
TableName=table_name,
Key=key,
ConsistentRead=True
)
return response.get('Item')
اگرچه ConsistentRead یکپارچگی قوی را در داخل یک منطقه فراهم میکند، خطیپذیری بینمنطقهای به منطق اضافی نیاز دارد. یک الگوی عملی استفاده از یک "توکن خوانش-پس-از-نوشتار" است. پس از نوشتار، کلاینت یک شناسه نوشتار منحصر به فرد دریافت میکند. خوانشهای بعدی میتوانند این شناسه را شامل شوند تا اطمینان حاصل کنند که دادههای منقضیشده از منطقه دیگری که هنوز آخرین نوشتار را همگامسازی نکرده است، برنمیگردانند. این رویکرد خطیپذیری را در سطح برنامه تقلید میکند و اطمینان حاصل میکند که کاربران همیشه آخرین وضعیت را مشاهده میکنند.
الگوهای عملی برای پیادهسازی
هنگام طراحی سیستم خود، الگوهای زیر را در نظر بگیرید:
- یکپارچگی ترکیبی: خوانشهای خطیپذیر را فقط برای مسیرهای دادهای حیاتی، مانند تراکنشهای مالی یا احراز هویت نشست استفاده کنید. برای سایر دادهها، مانند لاگها یا تحلیلها، یکپارچگی نهایی کافی است و از نظر هزینه مؤثرتر است.
- نسخههای خوانش با توکنهای یکپارچگی: در DynamoDB، از خوانشهای شرطی با شمارههای نسخه استفاده کنید تا اطمینان حاصل شود که خوانش آخرین نوشتار را منعکس میکند. در Spanner، از مدلهای یکپارچگی داخلی استفاده کنید تا از نسخهبندی دستی اجتناب کنید.
- آگاهی از تأخیر: خوانشهای خطیپذیر تأخیر اضافی ایجاد میکنند. API خود را طوری طراحی کنید که این موضوع را به طور ظریف مدیریت کند، احتمالاً با استفاده از بهروزرسانیهای UI خوشبینانه که در آن کلاینت موفقیت را فرض میکند و بعداً با سرور آشتی مییابد.
نتیجهگیری
پیادهسازی خوانشهای خطیپذیر در سیستمهای چندمنطقهای یک وظیفه پیچیده اما قابل دستیابی است. Google Cloud Spanner پشتیبانی داخلی برای یکپارچگی قوی ارائه میدهد و آن را به انتخابی ساده برای تیمهایی تبدیل میکند که یکپارچگی را بر همه چیز اولویت میدهند. Amazon DynamoDB از طریق خوانشهای یکپارچگی قوی و نسخهبندی در سطح برنامه، انعطافپذیری ارائه میدهد و به توسعهدهندگان اجازه میدهد مدلهای یکپارچگی خود را متناسب با نیازهای خاص تنظیم کنند. با درک مصالحهها و بهرهگیری از ابزارهای مناسب، میتوانید سیستمهای چندمنطقهای مستحکمی بسازید که یکپارچگی داده را بدون قربانی کردن عملکرد حفظ میکنند. هنگامی که سیستم شما مقیاس میشود، به یاد داشته باشید که یکپارچگی یک طیف است و انتخاب سطح مناسب برای هر مسیر داده کلید معماری موفق است.