0Pricing
Coding Interview Prep · درس

التخزين المؤقت وشبكات CDN وموازنة الحمل

أضف طبقات تخزين مؤقت باستخدام Redis، وادفع الأصول الثابتة إلى CDN، ووزّع حركة المرور على النسخ باستخدام موازِنات حمل round-robin وconsistent-hashing.

التخزين المؤقت وشبكات CDN وموازنة الحمل درس مجاني في Coding Interview Prep على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Coding Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

لماذا يُعد التخزين المؤقت أساسيًا عند التوسع

يخزن التخزين المؤقت نسخًا من البيانات التي يكثر الوصول إليها في طبقة تخزين أسرع، بحيث يمكن تلبية الطلبات اللاحقة دون الوصول إلى مخزن البيانات الداعم الأبطأ (قاعدة البيانات أو واجهة API خارجية). عند التوسع، يتلقى عدد صغير من العناصر الشائعة الغالبية العظمى من الطلبات — وغالبًا ما تنطبق قاعدة 80/20 (مبدأ باريتو): إذ تمثل 20% من العناصر 80% من حركة المرور.

يمكن لذاكرة تخزين مؤقت تستوعب أعلى 20% من العناصر طلبًا أن تستوعب 80% من حمل قاعدة البيانات. ولهذا تؤدي إضافة ذاكرة تخزين مؤقت باستخدام Redis غالبًا إلى خفض استخدام وحدة المعالجة المركزية لقاعدة البيانات بنسبة 70–90%، وتقليل زمن الوصول p99 من 10ms إلى أقل من 1ms عند العثور على البيانات في ذاكرة التخزين المؤقت — دون تغيير كبير في قاعدة البيانات أو منطق التطبيق.

# Demonstrating the 80/20 caching benefit
import random

# Simulate 1000 requests to 100 items with Zipf-like distribution
def zipf_sample(n_items, n_requests):
    access_counts = {}
    weights = [1.0 / (i + 1) for i in range(n_items)]  # Zipf: item 0 most popular
    total = sum(weights)
    probs = [w / total for w in weights]
    for _ in range(n_requests):
        item = random.choices(range(n_items), weights=probs)[0]
        access_counts[item] = access_counts.get(item, 0) + 1
    return access_counts

random.seed(42)
counts = zipf_sample(100, 10000)
top_20_items = sorted(counts, key=counts.get, reverse=True)[:20]
top_20_requests = sum(counts[i] for i in top_20_items)
print(f'Top 20% of items ({20} of 100) handle {top_20_requests/100:.1f}% of requests')

نمط Cache-Aside (التحميل عند الطلب)

يُعد نمط cache-aside (ويُسمى أيضًا التحميل عند الطلب) استراتيجية التخزين المؤقت الأكثر شيوعًا. وتكون شفرة التطبيق مسؤولة عن إدارة ذاكرة التخزين المؤقت: عند القراءة، افحصوا ذاكرة التخزين المؤقت أولًا. عند العثور على البيانات في ذاكرة التخزين المؤقت (cache hit)، أعيدوها فورًا. وعند عدم العثور عليها في ذاكرة التخزين المؤقت (cache miss)، اجلبوها من قاعدة البيانات، واكتبوها في ذاكرة التخزين المؤقت، ثم أعيدوها. وعند الكتابة، حدّثوا قاعدة البيانات وأبطلوا (احذفوا) مدخل ذاكرة التخزين المؤقت حتى تؤدي القراءة التالية إلى تحديثه.

يضمن هذا النمط أن ذاكرة التخزين المؤقت لا تحتوي إلا على البيانات التي طُلبت فعليًا (من دون تحميل مسبق غير ضروري)، وأنها تظل متسقة مع قاعدة البيانات بفضل الإبطال. أما المفاضلة فهي أن أول وصول بعد عدم العثور على البيانات يتحمل التكلفة الكاملة لقاعدة البيانات (بدء بارد).

# Cache-aside pattern in Python
class CacheAsideService:
    def __init__(self, db, cache):
        self.db = db
        self.cache = cache   # e.g., Redis client

    def get_user(self, user_id):
        cache_key = f'user:{user_id}'
        # 1. Check cache
        cached = self.cache.get(cache_key)
        if cached:
            return cached    # cache hit
        # 2. Cache miss: fetch from DB
        user = self.db.query('SELECT * FROM users WHERE id=%s', user_id)
        # 3. Write to cache with TTL
        self.cache.set(cache_key, user, ttl=3600)  # 1 hour TTL
        return user

    def update_user(self, user_id, data):
        # 1. Write to DB
        self.db.execute('UPDATE users SET ... WHERE id=%s', user_id, data)
        # 2. Invalidate cache (delete, not update)
        self.cache.delete(f'user:{user_id}')
        # Next read will re-populate cache from DB

print('Cache-aside: READ from cache, miss? load from DB + write cache')
print('         WRITE to DB, then DELETE from cache (invalidate)')

التخزين المؤقت بالكتابة المباشرة والكتابة المؤجلة

Write-through: عند كل عملية كتابة، حدّثوا قاعدة البيانات وذاكرة التخزين المؤقت معًا وبشكل متزامن. وبذلك تحتوي ذاكرة التخزين المؤقت دائمًا على بيانات حديثة. أما المفاضلة فهي أن عمليات الكتابة تصبح أبطأ (بسبب تنفيذ عمليتين)، وتمتلئ ذاكرة التخزين المؤقت ببيانات قد لا تتم قراءتها مجددًا.

Write-behind (write-back): عند الكتابة، حدّثوا ذاكرة التخزين المؤقت فقط، ثم أرسلوا البيانات إلى قاعدة البيانات بشكل غير متزامن لاحقًا. يجعل ذلك عمليات الكتابة فائقة السرعة، لكنه يعرّض البيانات للفقدان إذا تعطلت ذاكرة التخزين المؤقت قبل الإرسال. ويُستخدم في أحمال العمل الكثيفة الكتابة التي يكون فيها فقدان بعض البيانات مقبولًا (مثل عدادات المشاهدة والتحليلات).

# Write-through vs Write-behind comparison
strategies = {
    'Cache-aside (Lazy)': {
        'read':  'Check cache; miss => DB + populate cache',
        'write': 'Write DB; delete from cache (invalidate)',
        'consistency': 'Strong (invalidation ensures freshness)',
        'write_latency': 'Fast (one DB write)',
        'risk': 'Cache stampede on popular key expiry',
    },
    'Write-through': {
        'read':  'Always check cache; miss => DB',
        'write': 'Write DB AND cache atomically',
        'consistency': 'Strong (cache always has latest)',
        'write_latency': 'Slower (two writes per operation)',
        'risk': 'Cache polluted with rarely-read data',
    },
    'Write-behind': {
        'read':  'Check cache; miss => DB',
        'write': 'Write cache only; async flush to DB',
        'consistency': 'Eventual (flush may be delayed)',
        'write_latency': 'Very fast (cache write only)',
        'risk': 'Data loss if cache crashes before flush',
    },
}
for name, info in strategies.items():
    print(f'\n{name}:')
    for k, v in info.items(): print(f'  {k}: {v}')

سياسات إخلاء ذاكرة التخزين المؤقت

عندما تمتلئ ذاكرة التخزين المؤقت، تحدد سياسة الإخلاء الإدخال الذي يجب إزالته. ومن أكثر السياسات شيوعًا:

  • LRU (الأقل استخدامًا مؤخرًا): إخلاء الإدخال الذي لم يُصل إليه منذ أطول مدة. ويؤدي أداءً جيدًا مع أحمال العمل ذات المحلية الزمنية. ويستخدمه Redis افتراضيًا.
  • LFU (الأقل استخدامًا): إخلاء الإدخال الذي تم الوصول إليه أقل عدد من المرات. وهو أفضل مع أحمال العمل التي تظل فيها بعض العناصر شائعة باستمرار، بينما قد لا يلتقط LRU ذلك.
  • FIFO: إخلاء أقدم إدخال. بسيط، لكنه يحقق أداءً ضعيفًا مع أحمال عمل الويب المعتادة.
  • Random: إخلاء إدخال عشوائي. ويحقق أداءً منافسًا لـ LRU على نحو مفاجئ في الواقع عند استخدام ذاكرات تخزين مؤقت كبيرة جدًا.
# Implementing LRU cache
from collections import OrderedDict

class LRUCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.cache = OrderedDict()  # maintains insertion/access order

    def get(self, key):
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key)   # mark as recently used
        return self.cache[key]

    def put(self, key, value):
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)   # evict LRU (oldest)

cache = LRUCache(3)
for k, v in [('a',1),('b',2),('c',3)]:
    cache.put(k, v)
print('Get a:', cache.get('a'))   # 1 (a now most recently used)
cache.put('d', 4)                  # evicts 'b' (LRU)
print('Get b:', cache.get('b'))   # -1 (evicted)
print('Get c:', cache.get('c'))   # 3

شبكات توصيل المحتوى (CDN)

شبكة CDN هي شبكة موزعة جغرافيًا من خوادم الحافة (نقاط الحضور، PoPs) التي تخزّن المحتوى الثابت والديناميكي مؤقتًا بالقرب من المستخدمين النهائيين. بدلًا من انتقال طلب كل مستخدم إلى خادم الأصل في مركز بيانات واحد، تقدّم عقد الحافة في CDN المحتوى من أقرب PoP — فتقلّل زمن الاستجابة من ~200ms (عبر القارات) إلى ~5ms (من PoP قريب).

تُعد شبكات CDN أساسية للأصول الثابتة (الصور وCSS وJS)، وبث الفيديو (مقاطع HLS)، وبشكل متزايد لاستجابات API وHTML المُنشأ على الخادم. تتحقق CDN من ذاكرة التخزين المؤقت على الحافة؛ وعند حدوث cache miss، تجلب المحتوى من الأصل وتخزّنه مؤقتًا للطلبات المستقبلية.

# CDN architecture flow
cdn_flow = [
    'User requests https://example.com/image.jpg',
    'DNS resolves to the nearest CDN PoP (e.g., Frankfurt for EU users)',
    'CDN edge checks its local cache:',
    '  HIT:  Return cached image directly (5ms latency)',
    '  MISS: Fetch from origin server (e.g., AWS S3 in us-east-1)',
    '        Cache image at edge with Cache-Control: max-age=86400',
    '        Future requests for this image served from edge (HIT)',
    'Cache-Control headers control CDN behaviour:',
    '  max-age=31536000 s-maxage=31536000  -- cache 1 year',
    '  no-cache                             -- always revalidate',
    '  private                              -- CDN must not cache (user-specific)',
]
for step in cdn_flow:
    print(step)

print('\nCDN providers: Cloudflare, AWS CloudFront, Fastly, Akamai')

موازنة التحميل: توزيع حركة المرور

يوزّع موازن التحميل الطلبات الواردة على عدة خوادم للواجهة الخلفية، مما يمنع تحوّل أي خادم واحد إلى عنق زجاجة. كما يوفّر التوافر العالي: فإذا تعطل أحد الخوادم، يوجّه موازن التحميل حركة المرور تلقائيًا إلى الخوادم السليمة (مع إجراء فحوصات سلامة كل 5-30 ثانية).

تعمل موازنات التحميل في طبقات مختلفة من نموذج OSI: الطبقة 4 (النقل — التوجيه حسب IP/المنفذ، وهي سريعة جدًا) والطبقة 7 (التطبيق — التوجيه حسب مسار URL والرؤوس وملفات تعريف الارتباط، مما يتيح توجيهًا أكثر ذكاءً). تُعد AWS ALB وNginx وHAProxy موازنات تحميل شائعة من الطبقة 7. أما AWS NLB فهو موازن تحميل من الطبقة 4.

# Load balancing algorithms
algorithms = {
    'Round Robin': {
        'how': 'Rotate through servers in sequence',
        'best_for': 'Stateless servers with similar capacity',
        'weakness': 'Does not account for server load or response time',
    },
    'Weighted Round Robin': {
        'how': 'Round robin but servers with more capacity get more requests',
        'best_for': 'Heterogeneous server fleet',
        'weakness': 'Static weights; does not adapt to runtime load',
    },
    'Least Connections': {
        'how': 'Send to server with fewest active connections',
        'best_for': 'Long-lived connections (WebSocket, streaming)',
        'weakness': 'More complex tracking of connection state',
    },
    'Consistent Hashing': {
        'how': 'Hash request key (user_id, session) to server',
        'best_for': 'Sticky sessions, cache locality per server',
        'weakness': 'Uneven distribution if hash space is not balanced',
    },
    'Random': {
        'how': 'Choose server at random',
        'best_for': 'Simple stateless workloads',
        'weakness': 'No guarantee of load balance in short windows',
    },
}
for alg, info in algorithms.items():
    print(f'{alg}: {info["how"]}')

التجزئة المتسقة: إضافة العقد وإزالتها

تحل التجزئة المتسقة مشكلة إعادة توزيع مفاتيح ذاكرة التخزين المؤقت عند إضافة الخوادم أو إزالتها. في التجزئة الساذجة باستخدام modulo (server = hash(key) % n)، تؤدي أيٌّ من تغييرات n إلى إعادة تعيين جميع المفاتيح تقريبًا — مما يسبب اندفاعًا في ذاكرة التخزين المؤقت. تعمل التجزئة المتسقة على تعيين المفاتيح والخوادم معًا على حلقة؛ ويخدم كل مفتاح أقرب خادم إليه في اتجاه عقارب الساعة. لا تؤدي إضافة خادم إلا إلى إعادة تعيين المفاتيح الواقعة بين الخادم الجديد وسلفه — أي نحو 1/n من إجمالي المفاتيح.

تحسّن العقد الافتراضية (vnodes) توزيع الحمل: إذ يُسنَد إلى كل خادم فعلي عدة مواضع على الحلقة، فتتوزع المفاتيح بالتساوي أكبر حتى مع عدد صغير من الخوادم.

import hashlib
import bisect

class ConsistentHashRing:
    def __init__(self, replicas=100):
        self.replicas = replicas      # virtual nodes per server
        self.ring = {}
        self.sorted_keys = []

    def add_server(self, server):
        for i in range(self.replicas):
            key = int(hashlib.md5(f'{server}:{i}'.encode()).hexdigest(), 16)
            self.ring[key] = server
            bisect.insort(self.sorted_keys, key)

    def remove_server(self, server):
        for i in range(self.replicas):
            key = int(hashlib.md5(f'{server}:{i}'.encode()).hexdigest(), 16)
            del self.ring[key]
            self.sorted_keys.remove(key)

    def get_server(self, item):
        key = int(hashlib.md5(item.encode()).hexdigest(), 16)
        idx = bisect.bisect(self.sorted_keys, key) % len(self.sorted_keys)
        return self.ring[self.sorted_keys[idx]]

ring = ConsistentHashRing()
for s in ['server-1', 'server-2', 'server-3']:
    ring.add_server(s)
for item in ['user:1', 'user:2', 'product:abc', 'session:xyz']:
    print(f'{item} => {ring.get_server(item)}')

اندفاع ذاكرة التخزين المؤقت وحلوله

يحدث اندفاع ذاكرة التخزين المؤقت (cache stampede) (أو thundering herd) عندما تنتهي صلاحية إدخال شائع في ذاكرة التخزين المؤقت، فتفشل طلبات متزامنة كثيرة في العثور عليه في الوقت نفسه، مما يغمر قاعدة البيانات بالاستعلام نفسه. الحلول:

  • القفل (Mutex/lock): يحسب طلب واحد القيمة فقط، بينما تنتظر الطلبات الأخرى
  • انتهاء الصلاحية المبكر الاحتمالي: قبل TTL بقليل، يقرر أحد الطلبات عشوائيًا تحديث ذاكرة التخزين المؤقت، مما يمنع انتهاء الصلاحية المتزامن
  • Stale-while-revalidate: تقديم المحتوى القديم فورًا مع تحديث ذاكرة التخزين المؤقت بشكل غير متزامن
  • التحديث في الخلفية: تحدّث عملية منفصلة المفاتيح الشائعة قبل انتهاء صلاحيتها
import time, threading, random

# Probabilistic early expiry (XFetch algorithm)
class ProbabilisticCache:
    def __init__(self):
        self._cache = {}

    def get(self, key, ttl, recompute_fn, beta=1.0):
        if key in self._cache:
            value, expiry, delta = self._cache[key]
            # XFetch: decide to refresh early with probability proportional to delta/TTL
            remaining = expiry - time.time()
            if remaining > 0:
                early_refresh_score = delta * beta * (-1) * (remaining / ttl)
                if random.random() > (1 - early_refresh_score):  # simplified
                    pass  # could trigger async refresh here
                return value
        # Cache miss or expired
        start = time.time()
        value = recompute_fn()
        delta = time.time() - start          # computation time
        expiry = time.time() + ttl
        self._cache[key] = (value, expiry, delta)
        return value

print('XFetch: refresh probabilistically before expiry based on computation cost')
print('High-cost computations => refresh earlier to avoid stampede')
print('Low-cost computations => refresh closer to TTL')

إبطال ذاكرة التخزين المؤقت في CDN

يشتهر إبطال ذاكرة التخزين المؤقت بصعوبته: «لا توجد سوى مشكلتين صعبتين في علوم الحاسوب: إبطال ذاكرة التخزين المؤقت وتسمية الأشياء.» عندما يتغير المحتوى في الأصل، يجب على عقد الحافة في CDN تقديم الإصدار الجديد. الاستراتيجيات:

  • انتهاء الصلاحية المستند إلى TTL: اترك المحتوى تنتهي صلاحيته طبيعيًا (بسيط، لكنه يترك فترة يكون فيها المحتوى قديمًا)
  • إصدار URL: تضمين تجزئة المحتوى في URL (مثلًا، main.a3f2b.js)؛ المحتوى الجديد = URL جديد، فلا حاجة إلى الإبطال
  • الإفراغ عبر CDN API: إفراغ عناوين URL صراحةً عبر استدعاء API بعد النشر (سريع، لكنه يتطلب دمج CDN API)
# Cache invalidation strategies for CDN/browser
strategies = [
    {
        'name': 'Long TTL + URL versioning (best for static assets)',
        'example': '<script src="/app.a3f2b1c.js"></script>',
        'ttl': 'Cache-Control: max-age=31536000 (1 year)',
        'how': 'Content hash in filename; new deploy = new URL; old URL cached forever (OK)',
    },
    {
        'name': 'Short TTL (for frequently changing content)',
        'example': '/api/v1/config',
        'ttl': 'Cache-Control: max-age=60 (1 minute)',
        'how': 'Simple; content is at most 60s stale; no invalidation needed',
    },
    {
        'name': 'CDN API purge (for news / social media)',
        'example': '/news/breaking-story.html',
        'ttl': 'Cache-Control: s-maxage=3600',
        'how': 'On publish, call CDN.purge(url); edge serves new version immediately',
    },
]
for s in strategies:
    print(f'{s["name"]}:')
    print(f'  Example: {s["example"]}')
    print(f'  TTL: {s["ttl"]}')
    print(f'  Strategy: {s["how"]}\n')

البنية المعمارية: جمع كل العناصر معًا

تستخدم طبقة تطبيق ويب موسّعة بالكامل التقنيات الثلاث معًا: توزّع موازنة التحميل حركة المرور، وتمتص CDN الطلبات على الأصول الثابتة وطلبات API القابلة للتخزين المؤقت، بينما يخزّن Redis البيانات الديناميكية مؤقتًا. لا ترى قاعدة البيانات سوى حالات cache miss — وعادةً ما تمثل 5-20% من الطلبات.

تدفق طلب نموذجي لواجهة API كثيفة القراءة: المستخدم → DNS → حافة CDN (cache hit: يُقدَّم فورًا) → cache miss في CDN → موازن التحميل → مجموعة خوادم التطبيق → ذاكرة Redis المؤقتة (cache hit: استجابة خلال 1ms) → cache miss في Redis → قاعدة البيانات (10-50ms) → تُخزَّن الاستجابة مؤقتًا في Redis + اختياريًا في CDN → المستخدم. تقلل كل طبقة حمل قاعدة البيانات بدرجة كبيرة.

# Request flow with cache hit rates
request_flow = [
    ('Browser Cache',       '10%',  '0ms',   'Browser caches GET responses per Cache-Control'),
    ('CDN Edge Cache',      '60%',  '5ms',   'CloudFront/Fastly caches cacheable API responses'),
    ('Load Balancer',        None,  '1ms',   'Routes to healthy app server replica'),
    ('App Server',           None,  '2ms',   'Business logic, auth check'),
    ('Redis Cache',         '25%',  '1ms',   'Caches computed data, hot DB rows'),
    ('Database Read Replica','5%',  '10ms',  'Cache miss: query read replica'),
    ('Database Primary',    '0.1%', '15ms',  'Cache+replica miss: query primary (rare for reads)'),
]
print(f'{'Layer':30s} {'Hit Rate':10s} {'Latency':10s} {'Notes'}')
print('-'*80)
for layer, hit_rate, latency, note in request_flow:
    hr = hit_rate if hit_rate else '-'
    print(f'{layer:30s} {hr:10s} {latency:10s} {note}')
print('\nResult: DB sees ~5% of requests; Redis sees ~25%; CDN absorbs 60%; browser 10%')

نصائح المقابلات: التخزين المؤقت وموازنة التحميل

عند مناقشة التخزين المؤقت في مقابلة لتصميم الأنظمة، عالجوا دائمًا: ما الذي يجب تخزينه مؤقتًا (البيانات كثيرة الاستخدام، والحسابات المكلفة)، وأين يجب التخزين المؤقت (المتصفح، وCDN، والتطبيق، وذاكرة استعلامات قاعدة البيانات)، ومتى يجب الإبطال (عند الكتابة، أو عند انتهاء TTL، أو عبر التحديث في الخلفية)، وما ضمانات الاتساق المقبولة. يضيف التخزين المؤقت نافذة اتساق — فكونوا واضحين بشأنها.

أما في موازنة التحميل، فاذكروا اختيار الخوارزمية، وفحوصات السلامة، والجلسات الثابتة (عند الحاجة)، وما إذا كان التوسع الأفقي لخوادم التطبيقات عديمة الحالة ممكنًا. وإذا كان التطبيق يحتفظ بحالة (مثل اتصالات WebSocket والجلسات)، فعالجوا كيفية إدارة هذه الحالة عبر النسخ المتماثلة.

# Caching design questions checklist
cache_checklist = [
    'What data to cache? (read-heavy, expensive to compute, rarely updated)',
    'Cache layer: client-side / CDN / app-level / DB query cache?',
    'Cache invalidation strategy: TTL / event-driven / write-through?',
    'Eviction policy: LRU / LFU?',
    'Cache key design: ensure uniqueness, avoid hotspots',
    'Consistency window: acceptable staleness in seconds?',
    'Cache stampede prevention: mutex / stale-while-revalidate?',
    'Cache capacity: how much RAM needed for hot set?',
]
lb_checklist = [
    'Layer 4 vs Layer 7: routing by IP or by URL/headers?',
    'Algorithm: round robin / least-connections / consistent hashing?',
    'Health checks: interval, failure threshold, recovery',
    'Session stickiness: needed? Use cookie-based affinity or external session store',
    'Auto-scaling: scale out when CPU > 70%; scale in when < 30%',
]
print('Cache checklist:')
for item in cache_checklist: print(f'  [ ] {item}')
print('\nLoad balancer checklist:')
for item in lb_checklist: print(f'  [ ] {item}')

فحص سريع

اختبروا فهمكم لمفاهيم Data Structures & Algorithms — Coding Interview Prep الواردة في هذا الدرس.

ملخص الدرس

تعلمتم في هذا الدرس أن: التخزين المؤقت يحفظ البيانات كثيرة الاستخدام في طبقات ذاكرة سريعة (Redis وCDN) لامتصاص معظم عمليات القراءة وتقليل الحمل على قاعدة البيانات، وأن cache-aside هو النمط الأكثر شيوعًا — إذ يعني cache miss التحميل من قاعدة البيانات، ويعني cache hit الإرجاع فورًا، وتعني الكتابة حذف البيانات من ذاكرة التخزين المؤقت، وأن التجزئة المتسقة توزّع مفاتيح ذاكرة التخزين المؤقت عبر العقد، بحيث تؤدي إضافة العقد أو إزالتها إلى إعادة تعيين نحو 1/n من المفاتيح فقط بدلًا من إعادة تعيينها جميعًا. بعد ذلك سنصمم محدِّد معدل وتغذية Twitter لتطبيق جميع مفاهيم تصميم الأنظمة في مسائل متكاملة.

الأسئلة الشائعة

هل درس «التخزين المؤقت وشبكات CDN وموازنة الحمل» مجاني؟

نعم — نص درس «التخزين المؤقت وشبكات CDN وموازنة الحمل» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Coding Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

ماذا ستتعلم في «التخزين المؤقت وشبكات CDN وموازنة الحمل»؟

أضف طبقات تخزين مؤقت باستخدام Redis، وادفع الأصول الثابتة إلى CDN، ووزّع حركة المرور على النسخ باستخدام موازِنات حمل round-robin وconsistent-hashing. تتمرن على Coding Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Coding Interview Prep؟

لا تُشترط خبرة سابقة. Coding Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «التخزين المؤقت وشبكات CDN وموازنة الحمل»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Coding Interview Prep هذا؟

نعم. كل درس في Coding Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. إطار مقابلة تصميم الأنظمة
  2. تخزين البيانات القابل للتوسّع: SQL مقابل NoSQL
  3. التخزين المؤقت وشبكات CDN وموازنة الحمل
  4. تصميم محدِّد معدل الطلبات وتصميم موجز Twitter
← العودة إلى Coding Interview Prep