Software Architecture

استراتژی‌های شاردینگ پایگاه داده: مدیریت نقاط داغ نوشتن در میکروسرویس‌های پهنای باند بالا

با مقیاس‌پذیری معماری‌های میکروسرویس، پایگاه‌های داده اغلب به گلوگاه تبدیل می‌شوند. در حالی که مقیاس‌بندی افقی سرورهای اپلیکیشن ساده است، مقیاس‌بندی لایه داده پیچیده است. یک حالت خرابی رایج در سیستم‌های پهنای باند بالا «نقطه داغ نوشتن» است که در آن یک شارد یا گره خاص حجم نامتناسبی از ترافیک نوشتن را دریافت می‌کند و منجر به افزایش تأخیر و احتمال از دست رفتن داده‌ها می‌شود. در این پست، استراتژی‌های پیشرفته شاردینگ را برای توزیع یکنواخت بار نوشتن و تضمین پایداری سیستم بررسی می‌کنیم.

درک ریشه‌ای نقاط داغ نوشتن

نقاط داغ نوشتن معمولاً از انتخاب نامناسب کلید در فرآیند شاردینگ ناشی می‌شود. اگر کلید شاردینگ شما دارای انحراف (skew) باشد، برخی شارد‌ها به طور قابل توجهی داده و ترافیک بیشتری نسبت به سایرین را مدیریت خواهند کرد. برای مثال، شاردینگ بر اساس یک برچسب زمانی جهانی یا شناسه یک موجودیت توزیع‌نشده می‌تواند باعث شود چندین سرویس به یک گره پایگاه داده واحد فشار بیاورند. شناسایی توزیع کلیدهای اصلی شما اولین گام در کاهش این مشکل است. شما به یک استراتژی شاردینگ نیاز دارید که توزیع یکنواخت را در تمام گره‌های موجود تضمین کند، به ویژه برای عملیات‌های پرمصرف نوشتن.

استراتژی ۱: هش تصادفی با نمک (Salt)

هش ساده یک شناسه اغلب ناکافی است اگر خود شناسه‌ها ترتیبی یا همبسته باشند. با معرفی یک نمک تصادفی یا استفاده از یک الگوریتم هش پیچیده، می‌توانید نوشتن‌ها را یکنواخت‌تر توزیع کنید. با این حال، این کار پرس‌وجوها را پیچیده می‌کند. رویکرد بهتری برای سیستم‌های پرمصرف نوشتن، استفاده از یک کلید ترکیبی یا یک مکانیزم مسیریابی ثانویه است.

کد شبه‌نویسی جاوا زیر را برای یک مسیریاب شاردینگ که یک عملیات مد (mod) بر اساس یک توکن تصادفی‌سازی شده اعمال می‌کند، در نظر بگیرید:


public class WriteRouter {
    private final int shardCount;
    private final Random random = new Random();

    public WriteRouter(int shardCount) {
        this.shardCount = shardCount;
    }

    public String routeWrite(String userId) {
        // Use a stable hash for consistency but distribute load
        // across a larger virtual space first
        int virtualBucket = Math.abs(userId.hashCode()) % 1024;
        int physicalShard = virtualBucket % shardCount;
        
        return "db-shard-" + physicalShard;
    }
}

استراتژی ۲: شاردینگ بازه‌ای مبتنی بر زمان

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

این استراتژی امکان حذف آسان داده‌های قدیمی را فراهم می‌کند که برای حفظ عملکرد نوشتن روی SSDهای مدرن حیاتی است.

استراتژی ۳: شاردینگ تطبیقی از طریق باربالانس‌ها

قوانین شاردینگ ایستا می‌توانند تحت الگوهای بار غیرقابل پیش‌بینی شکست بخورند. شاردینگ تطبیقی شامل یک لایه پویا است که پهنای باند نوشتن هر شارد را پایش می‌کند. اگر شارد A ۶۰٪ از نوشتن‌ها را دریافت کند، مسیریاب به طور موقت جلسات نوشتن جدید را به شارد B هدایت می‌کند تا زمانی که بار متعادل شود. این کار به یک مکانیزم سبک‌وزن برای بررسی سلامت نیاز دارد.

پیاده‌سازی این کار در Go جنبه پویا را نشان می‌دهد:


func SelectShard(shards []Shard, load map[string]int64) Shard {
    minLoad := int64(math.MaxInt64)
    targetShard := shards[0]

    for _, s := range shards {
        if load[s.ID] < minLoad {
            minLoad = load[s.ID]
            targetShard = s
        }
    }
    return targetShard
}

کاهش هزینه پرس‌وجوهای بین شاردی

در حالی که توزیع نوشتن‌ها هدف اصلی است، نباید یکپارچگی خواندن را کاملاً فدا کنید. از تکثیر ناهمگام (asynchronous replication) درون شارد‌ها و یکپارچگی نهایی (eventual consistency) بین شارد‌ها در صورت امکان استفاده کنید. برای خوانش‌های حیاتی، یک نما (facade) پرس‌وجو پیاده‌سازی کنید که بر اساس کلید بداند کدام شارد را هدف قرار دهد و از اسکن کامل جدول در تمام شارد‌ها جلوگیری کند.

نتیجه‌گیری

مدیریت نقاط داغ نوشتن در میکروسرویس‌ها نیازمند تغییر از شاردینگ ایستا و قابل پیش‌بینی به استراتژی‌های پویا و آگاه از بار است. چه از هش تصادفی، چه بازه‌های مبتنی بر زمان، یا چه مسیریابی تطبیقی استفاده کنید، کلید اصلی پایش مداوم تأخیر نوشتن برای هر شارد است. با متعادل کردن پیش‌دستانه بار نوشتن، اطمینان حاصل می‌کنید که لایه پایگاه داده شما به همان اندازه مؤثری که سرویس‌های اپلیکیشن شما مقیاس‌پذیر هستند، مقیاس‌پذیر است و پایه‌ای محکم برای سیستم‌های پهنای باند بالا فراهم می‌کند.

Share: