یک ذخیرهگاه داده (data store) را به مجموعهای از پارتیشنهای افقی یا shard تقسیم کنید. این گزینه میتواند مقیاسپذیری را هنگام ذخیرهسازی و دسترسی به حجم زیادی از دادهها بهبود بخشد.
یک ذخیرهگاه داده که توسط یک سرور واحد میزبانی میشود ممکن است مشمول محدودیتهای زیر باشد:
* فضای ذخیرهسازی. انتظار میرود که یک ذخیرهگاه داده برای یک برنامه ابری در مقیاس بزرگ حاوی حجم عظیمی از دادهها باشد که میتواند در طول زمان به میزان قابلتوجهی افزایش یابد. یک سرور معمولاً فقط مقدار محدودی از دیسک را فراهم میکند، اما میتوانید دیسکهای موجود را با دیسکهای بزرگتر جایگزین کنید یا با افزایش حجم داده، دیسکهای بیشتری را به دستگاه اضافه کنید. بااینحال، سیستم در نهایت به حدی میرسد که امکان افزایش آسان ظرفیت ذخیرهسازی در یک سرور خاص وجود ندارد.
* منابع محاسباتی. یک برنامه اجرایی در محیط ابری معمولاً باید توانایی پشتیبانی و پاسخ بهصورت همزمان یا موازی به تعداد زیادی از کاربرها را داشته باشد، پس بسیاری از این درخواستهای کاربرها کوئریهایی (queries) را اجرا میکنند که اطلاعات را از ذخیرهگاه داده بازیابی میکنند. ممکن است یک سرور که وظیفه میزبانی ذخیره دادهها را دارند، نتواند قدرت محاسباتی لازم را برای پشتیبانی از این بار فراهم کند که در نتیجه منجر به طولانیتر شدن زمان پاسخدهی برای کاربران شده و خرابیهای مکرر برنامه بهصورت مشکلات ناشی از time-out بهخصوص برای ذخیره و بازیابی دادهها میشود. ممکن است امکان افزودن حافظه یا ارتقای پردازندهها وجود داشته باشد، اما زمانی که امکان افزایش منابع محاسباتی بیشتر نباشد، سیستم به یک محدودیت نهایی میرسد.
* پهنای باند شبکه. در نهایت، عملکرد یک ذخیرهگاه داده که روی یک سرور واحد اجرا میشود، توسط نرخی که سرور میتواند درخواستها و ارسال پاسخها را دریافت کند، کنترل میشود. ممکن است حجم ترافیک شبکه از ظرفیت شبکهای که برای اتصال به سرور استفاده میشود بیشتر باشد و در نتیجه درخواستها ناموفق باشد.
* جغرافیا. ممکن است به دلایلی مثل موارد قانونی یا کارایی برنامه یا کاهش تأخیر دسترسی به داده و بسیاری از موارد دیگر، دادههای تولید شده توسط کاربران خاص در همان منطقه جغرافیایی مربوط به کاربران خاص، ذخیره شود. اگر کاربران در کشورها یا مناطق مختلف پراکنده باشند، ممکن است امکان ذخیره کل دادههای برنامه در یک ذخیرهگاه داده وجود نداشته باشد.
مقیاس عمودی(Scaling vertically) با افزودن ظرفیت دیسک، قدرت پردازش، حافظه و اتصالات شبکه بیشتر میتواند اثرات برخی از این محدودیتها را به تعویق بیندازد، اما احتمالاً تنها یک راهحل موقتی است. یک برنامه تجاری ابری که قادر به پشتیبانی از تعداد زیادی کاربر و حجم بالای داده است، باید تقریباً به طور نامحدود مقیاسپذیر باشد، بنابراین مقیاسدهی عمودی لزوماً بهترین راهحل نیست.
ذخیره داده را به پارتیشنهای افقی یا shard تقسیم کنید. هر shard طرحواره یکسانی دارد، اما زیرمجموعه مجزایی از دادهها را در خود دارد. یک shard بهخودیخود یک ذخیرهگاه داده است (میتواند حاوی دادههای بسیاری از موجودیتهای مختلف باشد) که روی سروری اجرا میشود که بهعنوان یک گره ذخیرهسازی عمل میکند.
این الگو دارای مزایای زیر است:
* میتوانید با افزودن shard های بیشتر در حال اجرا بر روی گرههای ذخیرهسازی اضافی، سیستم را کوچک کنید.
* یک سیستم میتواند برای هر گره ذخیرهسازی بهجای رایانههای تخصصی و گرانقیمت، از سختافزار آماده استفاده کند.
* شما میتوانید با ایجاد تعادل در حجم کار در بین shard ها، مغایرتها را کاهش دهید و کارایی سیستم را بهبود بخشید.
* در فضای ابری، shardها را میتوان از نظر فیزیکی نزدیک به کاربرانی که به دادهها دسترسی دارند، قرارداد.
هنگام تقسیم یک ذخیرهسازی دادهها به shardها، تصمیم بگیرید که کدام داده در هر shard قرار گیرد. یک shard معمولاً شامل مواردی است که در محدوده مشخصی قرار میگیرند که توسط یک یا چند ویژگی داده تعیین میشود. این ویژگیها کلیدی shard (که گاهی اوقات بهعنوان کلید پارتیشن نامیده میشود) را تشکیل میدهند. کلید shard باید ثابت باشد. این کلید نباید بر اساس دادههایی باشد که ممکن است تغییر کند.
معمولاً با استفاده از shard میتوان دادهها را بهصورت فیزیکی سازماندهی کرد. هنگامی که یک برنامه دادهها را ذخیره و بازیابی میکند، sharding logic برنامه را به shard مناسب هدایت میکند. این sharding logic میتواند بهعنوان بخشی از کد دسترسی به دادهها در برنامه پیادهسازی شود یا اگر سیستم ذخیرهسازی دادهها به طور شفاف از sharding پشتیبانی میکند، میتواند آن را پیادهسازی کند.
انتزاعی کردن موقعیت فیزیکی دادهها در sharding logic، سطح بالایی از کنترل بر روی اینکه کدام shardها حاوی چه دادهای هستند را فراهم میکند. همچنین اگر دادههای موجود در shardها باید بعداً و در فرصتی دیگر توزیع شوند (مثلاً اگر shardها نامتعادل شوند) دادهها را قادر میسازد تا بین shardها بدون تغییر منطق تجاری یک برنامه مهاجرت (migrate) کنند. این مبادله، منجر به سربار دسترسی به داده اضافی است که برای تعیین مکان هر مورد داده در زمان بازیابی آن لازم است.
برای اطمینان از کارایی و مقیاسپذیری بهینه در برنامه مهم است که دادهها را بهگونهای تقسیم کنید که برای انواع کوئریهایی (queries) که برنامه انجام میدهد، مناسب باشد. در بسیاری از موارد، بعید است که طرح اشتراکگذاری دقیقاً با الزامات هر کوئری مطابقت داشته باشد. بهعنوانمثال، در یک سیستم multi-tenant، ممکن است یک برنامه نیاز به بازیابی دادههای tenant با استفاده از شناسه tenant داشته باشد، اما ممکن است نیاز به جستجوی این دادهها بر اساس برخی ویژگیهای دیگر مانند نام یا مکان tenant داشته باشد. برای مدیریت این شرایط، یک استراتژی sharding را با یک کلید shard که متداولترین کوئریها را پشتیبانی میکند، اجرا کنید.
اگر کوئریها (queries) به طور منظم دادهها را با استفاده از ترکیبی از مقادیر مشخصه بازیابی میکنند، احتمالاً میتوانید با پیونددادن ویژگیها به یکدیگر، یک کلید shard ترکیبی تعریف کنید. روش دیگر، از الگویی مانند Index Table برای ارائه جستجوی سریع به دادهها بر اساس ویژگیهایی که توسط کلید shard پوشیده نشدهاند استفاده کنید.
معمولاً هنگام انتخاب کلید shard و تصمیمگیری درباره نحوه توزیع دادهها در بین shardها از سه استراتژی مختلف استفاده میشود. توجه داشته باشید که لازم نیست یک ارتباط یکبهیک بین shardها و سرورهایی که آنها را میزبانی میکنند وجود داشته باشد در واقع یک سرور واحد میتواند چندین shard را میزبانی کند. این استراتژیهای موردنظر عبارتاند از:
استراتژی جستجو؛ در این استراتژی sharding logic مسیری را پیادهسازی میکند که درخواست برای دادهها را با استفاده از کلید shard به قطعهای که حاوی آن داده است هدایت میکند. در یک برنامه multi-tenant، تمام دادههای یک مستأجر(tenant) ممکن است با هم در یک shard با استفاده از شناسه مستأجر(tenant ID) بهعنوان کلید shard ذخیره شود. ممکن است multi-tenantها یک shard را به اشتراک بگذارند، اما دادههای یک tenant در چند shard پخش نمیشود. شکل زیر بهاشتراکگذاری دادههای مستأجر بر اساس شناسههای مستأجر را نشان میدهد.
نگاشت بین کلید shard و حافظه فیزیکی میتواند بر اساس shardهای فیزیکی باشد که در آن هر کلید shard به یک پارتیشن فیزیکی نگاشت میشود. روش پیشنهادی دیگر استفاده از یک تکنیک انعطافپذیرتر برای متعادلسازی مجدد شاردها به کمک پارتیشنبندی مجازی است که در آن کلیدهای shard به همان تعداد shard های مجازی نگاشت میشوند که آنها نیز به نوبه خود به پارتیشنهای فیزیکی کمتری نگاشت میشوند. در این رویکرد، یک برنامه دادهها را با استفاده از یک کلید shard که به یک shard مجازی اشاره دارد، مکانیابی میکند و سیستم به طور شفاف، shard های مجازی را به پارتیشنهای فیزیکی نگاشت میکند. نگاشت بین یک پارتیشن مجازی و یک پارتیشن فیزیکی میتواند بدون نیاز به تغییر کد برنامه برای استفاده از مجموعه متفاوتی از کلیدهای shard تغییر کند.
استراتژی محدوده. این استراتژی اقلام مرتبط را با هم در یک shard گروهبندی میکند و آنها را بر اساس کلید shard مرتب میکند - معمولاً کلیدهای shard بهصورت متوالی هستند. برای برنامههایی که همیشه مجموعههایی از آیتمها را با استفاده از کوئریهای محدوده(range queries) بازیابی میکنند (کوئریهایی که مجموعهای از آیتمهای داده را برای یک کلید shard ای که در محدوده معینی قرار میگیرد، برمیگردانند) مناسب است. بهعنوانمثال، اگر یک برنامه به طور همیشگی نیاز به یافتن همه سفارشهای ثبتشده در یک ماه خاص داشته باشد و اگر همه سفارشهای یک ماه به ترتیب تاریخ و زمان در یک قطعه(shard) ذخیره شوند در نتیجه میتوان این دادهها را سریعتر بازیابی کرد. اگر هر سفارش در shard ای متفاوت ذخیره میشد در آن موقع باید با انجام تعداد زیادی کوئری دقیق (کوئریهایی که یک مورد داده واحد را برمیگرداند) بهصورت جداگانه واکشی شوند. شکل بعدی ذخیره مجموعههای متوالی (محدوده) دادهها را بهصورت shard نشان میدهد.
در این مثال، کلید shard یک کلید ترکیبی است که حاوی ماه سفارش بهعنوان مهمترین عنصر و به دنبال آن روز سفارش و زمان است. زمانی که سفارشهای جدید ایجاد میشوند و به یک shard اضافه میشوند، دادههای سفارشها به طور طبیعی مرتب میشوند. برخی از ذخیرهگاههای داده از کلیدهای shard دوقسمتی حاوی یک عنصر کلید پارتیشن که shard را شناسایی میکند و یک کلید ردیفی که به طور منحصربهفرد یک مورد خاص را در shard شناسایی میکند، پشتیبانی میکنند. دادهها معمولاً به ترتیب کلیدهای ردیفی در shard نگهداری میشوند. مواردی که در معرض کوئریهای محدوده (range queries) هستند و باید با هم گروهبندی شوند، میتوانند از کلید shard ای استفاده کنند که مقدار یکسانی برای کلید پارتیشن داشته و یک مقدار منحصربهفرد برای کلید ردیف نیز دارند.
استراتژی هش. هدف از این استراتژی کاهش شانس hotspotها (تکهها یا shard هایی که مقدار نامتناسبی از بار دریافت میکنند) است. این گزینه دادهها را در بین shardها بهگونهای توزیع میکند که بین اندازه هر shard و میانگین باری که هر shard با آن مواجه میشود تعادل ایجاد میکند. sharding logic، شارد را برای ذخیره یک آیتم بر اساس هش یک یا چند ویژگی از دادهها محاسبه میکند. تابع هش انتخاب شده باید دادهها را به طور یکنواخت در بین shardها توزیع کند و احتمالاً این کار را به کمک با واردکردن برخی از عناصر تصادفی در محاسبات خود انجام میدهد. شکل بعدی دادههای sharding tenant را بر اساس هش مربوط به شناسه tenantها نشان میدهد.
برای درک مزیت استراتژی هش نسبت به سایر استراتژیهای sharding این مورد را در نظر بگیرید که چگونه یک application یا برنامه multi-tenant که مستأجران(tenants) جدید را به طور متوالی ثبتنام میکند و ممکن است tenantها را به shard هایی در ذخیرهگاه داده(data store) ربط دهد. هنگام استفاده از استراتژی محدوده (Range)، دادههای مستأجران(tenants) از شماره ۱ تا n همگی در قطعه A، دادههای tenantها از شماره n+1 تا m همه در shard B ذخیره میشوند. اگر tenant هایی که اخیراً ثبتنام کردهاند فعالیت بالایی نیز داشته باشند، پس این به معنی بیشترین فعالیت داده در تعداد کمی از shardها رخ میدهد که میتواند باعث ایجاد hotspotها شود. در مقابل، استراتژی هش مستأجران را بر اساس هش tenant ID به shardها اختصاص میدهد. این بدان معنی است که tenant های متوالی بهاحتمال زیاد به shard های مختلف اختصاص داده میشوند که بار را در بین آنها توزیع میکند. شکل قبلی این موضوع را برای tenant های شماره ۵۵ و ۵۶ نشان میدهد.
سه استراتژی اشتراکگذاری دارای مزایا و ملاحظات زیر است:
* جستجو (Lookup). این امر کنترل بیشتری بر نحوه پیکربندی و استفاده از shardها ارائه میدهد. استفاده از shardهای مجازی، اثربخشی را هنگام متعادلسازی مجدد دادهها کاهش میدهد، زیرا میتوان پارتیشنهای فیزیکی جدیدی اضافه کرد تا حجم کار(workload) را یکنواخت کند. نگاشت بین یک shard مجازی و پارتیشنهای فیزیکی که آن را پیادهسازی میکنند میتواند قابلتغییر باشد و این کار را بدون تأثیرگذاری بر کد برنامهای که از یک کلید shard برای ذخیره و بازیابی دادهها استفاده میکند را انجام دهد، در نهایت جستوجوی مکانهای shard میتواند هزینههای اضافی را تحمیل کند.
* محدوده (Range). پیادهسازی این کار آسان است و بهخوبی با پرسوجوهای محدوده(range queries) کار میکند؛ زیرا آنها اغلب میتوانند چندین مورد داده را از یک قطعه(shard) واحد در یک عملیات واحد واکشی کنند. این استراتژی مدیریت دادهها را آسانتر میکند. بهعنوانمثال، اگر کاربران در همان منطقه در یک shard باشند، بهروزرسانیها را میتوان در هر منطقه زمانی بر اساس بار محلی و الگوی تقاضا برنامهریزی کرد. بااینحال، این استراتژی تعادل مطلوب بین shardها را فراهم نمیکند. تعادل مجدد shardها دشوار است و اگر بیشتر فعالیتها برای کلیدهای shard مجاور باشد، ممکن است مشکل بار ناهموار را حل نکند.
* هش. این استراتژی شانس بیشتری برای توزیع یکنواخت داده و بار ارائه میدهد. مسیریابی درخواست را میتوان مستقیماً با استفاده از تابع هش انجام داد و نیازی به نگهداری نگاشتها نیست. توجه داشته باشید که محاسبه هش ممکن است سربار اضافی را تحمیل کند. همچنین تعادل مجدد shardها در این روش تا حدودی دشوار است.
اکثر سیستمهای اشتراکگذاری رایج یکی از رویکردهای شرح داده شده در بالا را اجرا میکنند، اما باید الزامات تجاری application خود و الگوهای استفاده از داده آنها را نیز در نظر بگیرید. بهعنوانمثال، در یک برنامه multi-tenant:
* شما میتوانید دادهها را بر اساس حجم کاری، خرد(shard) کنید. شما میتوانید دادهها را برای tenantها بسیار زودگذر( highly volatile tenants) در shardهای جداگانه تفکیک کنید. در نتیجه سرعت دسترسی به دادهها برای سایر tenantها ممکن است بهبود یابد.
* میتوانید دادهها را بر اساس موقعیت مکانی tenant ها، خرد(shard) کنید. میتوانید دادههای tenantها را در یک منطقه جغرافیایی خاص را برای پشتیبانگیری و نگهداری در ساعات شلوغی در آن منطقه بهصورت آفلاین دریافت کنید، درحالیکه دادههای tenantها در مناطق دیگر آنلاین و در ساعات کاری آنها در دسترس است.
* به tenantهای ارزشمند میتوان shardهای خصوصی با کارایی بالا و دارای بار سبک (lightly loaded) اختصاص داد، درحالیکه از tenantهای کمارزشتر میتوان انتظار داشت که shardهای پرمشغلهتر و متراکمتری را به اشتراک بگذارند.
* دادههای tenantهای که به درجه بالایی از جداسازی دادهها و حفظ حریم خصوصی نیاز دارند را میتوان در یک سرور کاملاً مجزا ذخیره کرد.
هر یک از راهبُردهای بهاشتراکگذاری مستلزم قابلیتها و سطوح پیچیدگی متفاوتی برای مدیریت scale in و scale out، جابهجایی دادهها و حفظ وضعیت است.
استراتژی Lookup یا جستجو اجازه میدهد تا عملیات مقیاسگذاری(scaling) و جابهجایی دادهها در سطح کاربر، بهصورت آنلاین یا آفلاین انجام شود. این تکنیک عبارت است از تعلیق برخی یا تمام فعالیتهای کاربر (شاید در دورههای غیر اوج مصرف)، انتقال دادهها به پارتیشن مجازی یا قطعه فیزیکی جدید، تغییر نگاشتها، نامعتبر کردن یا refresh کردن حافظههای موقت(caches) که این دادهها را در خود نگه میدارد و در ادامه دوباره اجازه فعالیتکردن یا ادامهدادن را به کاربر را میدهد. اغلب این نوع عملیات را میتوان بهصورت متمرکز مدیریت کرد. استراتژی Lookup یا جستجو مستلزم آن است که وضعیت دادهها قابلذخیرهسازی (cacheable) و تکثیر یا replica شدن باشد.
استراتژی محدوده یا Range محدودیتهایی را بر مقیاسگذاری و عملیات جابهجایی داده اعمال میکند که معمولاً باید زمانی انجام شود که بخشی یا تمام ذخیرهسازی داده آفلاین است، زیرا دادهها باید تقسیم شده و در بین shardها ادغام شوند. اگر بیشتر فعالیتها مربوط به کلیدهای shard مجاور یا شناسههای دادهای است که در محدوده یکسان هستند، انتقال دادهها به تعادل مجدد shardها ممکن است مشکل بار ناهموار را حل نکند. استراتژی Range همچنین ممکن است برای نگاشت محدوده(map ranges) به پارتیشنهای فیزیکی نیاز به حفظ وضعیت خود داشته باشد.
استراتژی هش، مقیاسگذاری و عملیات جابهجایی دادهها را پیچیدهتر میکند، زیرا کلیدهای پارتیشن هشهای کلیدهای shard یا شناسه دادهها هستند. مکان جدید هر قطعه باید از تابع هش تعیین شود یا تابع اصلاح شود تا نگاشت صحیح را ارائه دهد. بااینحال، استراتژی Hash نیازی به حفظ وضعیت خود ندارد.
هنگام تصمیمگیری در مورد نحوه اجرای این الگو به نکات زیر توجه کنید:
* استفاده از Sharding مکمل سایر اشکال پارتیشنبندی مانند پارتیشنبندی عمودی و پارتیشنبندی تابعی (functional) است. بهعنوانمثال، یک shard بهتنهایی میتواند شامل موجودیتهایی باشد که بهصورت عمودی پارتیشنبندی شدهاند و یک پارتیشن تابعی میتواند بهصورت چند shard پیادهسازی شود. برای اطلاعات بیشتر در مورد پارتیشن بندی، به راهنمای پارتیشن بندی دادهها( Data Partitioning Guidance) مراجعه کنید.
* همیشه shardها را متعادل نگه دارید تا همه آنها حجم مشابهی از I/O (ورودی/خروجی) را کنترل کنند. ازآنجاییکه دادهها درج و حذف میشوند، لازم است که بهصورت دورهای دوباره تعادل shardها را برای تضمین توزیع یکنواخت و کاهش وقوع هاتاسپاتها تنظیم کنید. متعادلسازی مجدد shardها میتواند یک عملیات پرهزینه باشد. برای کاهش نیاز به متعادلسازی مجدد باید با اطمینان از اینکه هر shard دارای فضای خالی کافی برای رسیدگی به حجم مورد انتظار تغییرات است برای رشد برنامهریزی کنید. همچنین باید راهبُردها و اسکریپتهایی را ایجاد کنید که در صورت لزوم، میتوانید از آنها برای متعادلکردن مجدد سریع shardها استفاده کنید.
* از دادههای پایدار برای کلید shard استفاده کنید. اگر کلید shard تغییر کند، آیتم داده مربوطه ممکن است باید بین shardها جابهجا شود و میزان کار انجام شده توسط عملیات بهروزرسانی افزایش یابد. به همین دلیل از قراردادن کلید خرد بر روی اطلاعات بالقوه فرار خودداری کنید. در عوض، به دنبال ویژگیهایی باشید که ثابت هستند یا به طور طبیعی ویژگیهای لازم برای یک کلید را تشکیل میدهند.
* اطمینان حاصل کنید که کلیدهای shard شده منحصربهفرد هستند. بهعنوانمثال، از استفاده از فیلدهای افزایش خودکار (autoincrement) بهعنوان کلید shard خودداری کنید. در برخی سیستمها، فیلدهای افزایشیافته خودکار را نمیتوان در بین shardها هماهنگ و بهروزرسانی کرد که در نهایت منجر به این میشود که آیتمهایی در shardهای مختلف کلید shard یکسانی داشته باشند.
مقادیر افزایشیافته خودکار (autoincrement) در سایر فیلدها که کلیدهای shard نیستند نیز میتواند باعث ایجاد مشکل شود. بهعنوانمثال، اگر از فیلدهای افزایشیافته خودکار برای تولید شناسههای منحصربهفرد ( unique IDs) استفاده میکنید، ممکن است به دو مورد مختلف که در shardهای مختلف قرار دارند، یک شناسه اختصاص داده شود.
* ممکن است طراحی یک کلید shard ای که با الزامات هر درخواست در برابر دادهها مطابقت داشته باشد، امکانپذیر نباشد. دادهها را برای پشتیبانی از اغلب کوئریهای انجامشده تکهتکه و تقسیمبندی کنید و در صورت لزوم جداول index ثانویه را برای پشتیبانی از کوئریهای (queries) که دادهها را با استفاده از معیارهایی بر اساس ویژگیهایی که بخشی از کلید shard نیست ایجاد کنید. برای اطلاعات بیشتر، Index Table pattern را ببینید.
* کوئریهایی که فقط به یک shard دسترسی دارند کارآمدتر از آنهایی هستند که دادهها را از چند shard بازیابی میکنند، بنابراین از پیادهسازی یک سیستم اشتراکگذاری که منجر به اجرای تعداد زیادی کوئری توسط برنامههایی میشود که دادههای نگهداری شده در shardهای مختلف را به هم میپیوندند خودداری کنید. به یاد داشته باشید که یک shard میتواند حاوی دادههای چند نوع موجودیت باشد. غیرنرمال سازی کردن (denormalizing) دادههای خود را در نظر بگیرید تا موجودیتهای مرتبطی را که معمولاً در کنار هم جستجو میشوند (مانند جزئیات مشتریان و سفارشهایی که آنها انجام دادهاند) در یک shard نگه دارید تا تعداد خواندنهای جداگانهای که یک برنامه انجام میدهد کاهش یابد.
اگر موجودی در یک shard به یک موجودیت ذخیره شده در shard دیگر ارجاع دهد، کلید shard موجودیت دوم را بهعنوان بخشی از طرح موجودیت اول وارد کنید. این گزینه میتواند به بهبود عملکرد کوئریهایی که به دادههای مرتبط در سراسر shard ارجاع میدهند کمک کند.
* اگر یک برنامه باید کوئریهایی را انجام دهد که دادهها را از چند shard بازیابی میکند، ممکن است بتوان این دادهها را با استفاده از تسکهای موازی واکشی کرد. مثالهایی شامل کوئریهای fan-out که در آن دادههای چند shard بهصورت موازی بازیابی میشوند و سپس در یک نتیجه نهایی جمع میشوند. بااینحال، این رویکرد پیچیدگیهایی را به منطق دسترسی به دادههای مربوط به این راهحل اضافه میکند.
* برای بسیاری از کاربردها ایجاد تعداد بیشتر shardهای کوچک میتواند کارآمدتر از داشتن تعداد کمی shardهای بزرگ باشد، زیرا میتوانند فرصتهای بیشتری را برای متعادلکردن بار ارائه دهند. این مورد هم میتواند مفید باشد اگر شما نیاز به مهاجرت shardها از یک مکان فیزیکی به مکان دیگر را پیشبینی کنید. حرکتدادن یک قطعه(shard) کوچک سریعتر از حرکتدادن یک shard بزرگ است.
* اطمینان حاصل کنید که منابع موجود برای هر گره ذخیرهسازی shard برای رسیدگی به نیازهای مقیاسپذیری از نظر اندازه و توان داده کافی است. برای اطلاعات بیشتر، بخش 'طراحی پارتیشنها برای مقیاسپذیری' را در راهنمای پارتیشن بندی دادهها (Data Partitioning Guidance) را ببینید.
* تکرار دادههای مرجع (replicating reference) برای همه shardها را در نظر بگیرید. اگر عملیاتی که دادهها را از یک shard بازیابی میکند به دادههای ثابت یا دادههای دارای حرکت آهسته نیز بهعنوان بخشی از همان پرسوجو (query) اشاره میکند پس باید این دادهها را به shard اضافه کنید. سپس برنامه میتواند تمام دادههای مربوط به کوئری را بهراحتی واکشی کند بدون اینکه نیازی به رفتوبرگشت اضافی به یک ذخیرهگاه داده جداگانه داشته باشد.
اگر دادههای مرجع نگهداری شده در چند shard تغییر کند، سیستم باید این تغییرات را در همه shardها همگامسازی کند. هر چند سیستم میتواند درجهای از ناهماهنگی را در زمانی که این همگامسازی رخ میدهد را تجربه و تحمل کند. در نتیجه اگر این کار را انجام میدهید بهتر است برنامههای خود را طوری طراحی کنید که توانایی مدیریت این مورد را داشته باشید.
* حفظ یکپارچگی ارجاعها و یکپارچگی بین shardها میتواند دشوار باشد، بنابراین باید عملیات را که روی دادهها در چند shard تأثیر میگذارند، به حداقل برسانید. اگر برنامهای باید دادهها را در بین shardها تغییر دهد ابن نکته را ارزیابی کنید که آیا یکپارچگی کامل دادهها(complete data consistency) واقعاً موردنیاز است یا خیر. در عوض، یک رویکرد رایج در فضای ابری، اجرای حالت یکپارچگی تدریجی (eventual consistency) است. در این حالت دادههای هر پارتیشن به طور جداگانه بهروزرسانی میشوند و منطق برنامه باید مسئولیت اطمینان از تکمیل موفقیتآمیز بهروزرسانیها و همچنین رسیدگی به ناهماهنگیهایی را که میتواند از جستجوی دادهها در زمانی که یک عملیات در حالت یکپارچگی تدریجی(eventually consistent) در حال اجرا است، به عهده بگیرد. برای اطلاعات بیشتر در مورد اجرای یکپارچگی تدریجی، به Data Consistency Primer مراجعه کنید.
* پیکربندی و مدیریت تعداد زیادی از shardها میتواند چالشبرانگیز باشد. وظایفی مانند نظارت(monitoring)، تهیه نسخه پشتیبان، بررسی یکپارچگی و ثبت لاگها(logging) یا بررسیها(auditing) باید بر روی چندین shard و سرور انجام شود که احتمالاً در مکانهای مختلف نگهداری میشوند. این وظایف احتمالاً با استفاده از اسکریپتها یا سایر راهحلهای اتوماسیون اجرا میشوند، اما ممکن است الزامات اجرایی اضافی را به طور کامل حذف نکند.
* گاهی shardها را میتوان بهگونهای مکانیابی کرد که دادههای موجود در آنها به نمونههای برنامهای که از آن استفاده میکند نزدیک باشد. این رویکرد میتواند کارایی برنامه را به طور قابلتوجهی بهبود بخشد، اما برای تسکهایی که باید به چند shard در مکانهای مختلف دسترسی داشته باشند، نیاز به بررسی بیشتری دارد.
از این الگو زمانی استفاده کنید که یک ذخیرهگاه داده بهاحتمال زیاد نیاز به مقیاسپذیری فراتر از منابع موجود برای یک گره ذخیرهسازی دارد یا حالتی که بهبود کارایی با کاهش مغایرت در یک ذخیرهگاه داده را در نظر دارد.
توجه داشته باشید
تمرکز اصلی sharding بهبود کارایی و مقیاسپذیری یک سیستم است، اما بهعنوان یک محصول جانبی میتواند به دلیل نحوه تقسیم دادهها به پارتیشنهای جداگانه، قابلیتهای دسترسی(availability) را نیز بهبود بخشد. خرابی در یک پارتیشن لزوماً مانع از دسترسی یک برنامه به دادههای نگهداری شده در پارتیشنهای دیگر نمیشود و یک اپراتور میتواند تعمیر و نگهداری یا بازیابی یک یا چند پارتیشن را بدون اینکه کل دادههای یک برنامه غیرقابلدسترسی باشد، انجام دهد. برای اطلاعات بیشتر، به راهنمای پارتیشن بندی دادهها [Data Partitioning Guidance](https://learn.microsoft.com/en-us/previous-versions/msp-n-p/dn589795(v=pandp.10)) مراجعه کنید.
توجه: در سند بهروزرسانی شده مایکروسافت این مثال عوض شده است.
<مثال زیر در سیشارپ از مجموعهای از پایگاهدادههای SQL Server استفاده میکند که بهعنوان shard عمل میکنند. هر پایگاهداده زیرمجموعهای از دادههای مورداستفاده یک برنامه را در خود جای میدهد. این برنامه دادههایی را که در بین shardها توزیع شده است را با استفاده از sharding logic خودش بازیابی میکند (این نمونهای از یک fan-out query است). جزئیات دادههایی که در هر shard قرار دارند با روشی به نام GetShards
بازگردانده میشوند. این روش یک لیست قابلشمارش از اشیای ShardInformation
را برمیگرداند که در آن نوع ShardInformation
شامل یک شناسه برای هر شارد و رشته اتصال به SQL Server است که برنامه باید از آن برای اتصال به shard استفاده کند (رشتههای اتصال (connection strings) در مثال کد زیر نشان داده نشدهاند).
private IEnumerable<ShardInformation> GetShards()
{
// This retrieves the connection information from a shard store
// (commonly a root database).
return new[]
{
new ShardInformation
{
Id = 1,
ConnectionString = ...
},
new ShardInformation
{
Id = 2,
ConnectionString = ...
}
};
}
کد زیر نشان میدهد که چگونه برنامه از فهرست اشیای ShardInformation
برای انجام پرسوجو (query) استفاده میکند که دادهها را از هر shard بهصورت موازی واکشی میکند. هر چند در این مثال جزئیات کوئری نشان داده نمیشود؛ اما دادههای بازیابی شده حاوی رشتهای است که میتواند اطلاعاتی مانند نام مشتری را در خود نگه دارد، اگر shardها حاوی جزئیات مشتریان باشند. نتایج در یک مجموعه ConcurrentBag
برای پردازش توسط برنامه تجمیع میشوند.
// Retrieve the shards as a ShardInformation[] instance.
var shards = GetShards();
var results = new ConcurrentBag<string>();
// Execute the query against each shard in the shard list.
// This list would typically be retrieved from configuration
// or from a root/primary shard store.
Parallel.ForEach(shards, shard =>
{
// NOTE: Transient fault handling isn't included,
// but should be incorporated when used in a real world application.
using (var con = new SqlConnection(shard.ConnectionString))
{
con.Open();
var cmd = new SqlCommand("SELECT ... FROM ...", con);
Trace.TraceInformation("Executing command against shard: {0}", shard.Id);
var reader = cmd.ExecuteReader();
// Read the results in to a thread-safe data structure.
while (reader.Read())
{
results.Add(reader.GetString(0));
}
}
});
Trace.TraceInformation("Fanout query complete - Record Count: {0}",
results.Count);
راهنمایی زیر ممکن است هنگام اجرای این الگو نیز مرتبط باشد:
* Data Consistency Primer. ممکن است لازم باشد برای دادههای توزیع شده در shardهای مختلف ویژگی یکپارچگی (consistency) حفظ شود. در واقع این گزینه مسائل مربوط به حفظ یکپارچگی در دادههای توزیع شده را خلاصه میکند و مزایا و سبکوسنگین کردنهای (tradeoffs) مدلهای یکپارچگی مختلف را شرح میدهد.
* Data Partitioning Guidance. استفاده از Sharding در یک ذخیرهگاه داده میتواند طیف وسیعی از مسائل اضافی را ایجاد کند. این مسائل دررابطهبا پارتیشنبندی ذخیرهگاههای داده در فضای ابری برای بهبود مقیاسپذیری، کاهش مغایرتها و بهینهسازی کارایی برنامه را توضیح میدهد.
الگوهای زیر نیز ممکن است هنگام اجرای این الگو مرتبط باشند:
* الگوی جدول شاخص(Index Table pattern). گاهی اوقات پشتیبانی کامل از کوئریها فقط از طریق طراحی کلید shard امکانپذیر نیست. پس این مورد برنامه را قادر میسازد تا با تعیین کلیدی غیر از کلید shard، دادهها را از یک storage بهسرعت بازیابی کند.
* الگوی نمای مادی(Materialized View pattern). برای حفظ کارایی برخی از عملیات کوئری، ایجاد Materialized View که دادهها را جمعآوری و خلاصه میکند بسیار مناسب است، بهخصوص اگر این دادههای خلاصه بر اساس اطلاعاتی باشد که در بین shardها توزیع شده است. در نهایت این مورد نحوه تولید و پر کردن این نماها را شرح میدهد.