التخزين المؤقت في TypeScript: Redis وMemcached والحلول المدمجة (مستوى متقدم)

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا التخزين المؤقت المتقدم ضرورة لتطبيقات TypeScript

\n\n

في الأنظمة الحديثة عالية التحميل، لم يعد التخزين المؤقت خيارًا - بل أصبح عنصرًا إلزاميًا في البنية. لمطوري TypeScript الذين يعملون مع Node.js، يمكن أن يؤدي اختيار استراتيجية التخزين المؤقت الصحيحة إلى تقليل وقت استجابة API من 200 مللي ثانية إلى 2 مللي ثانية وتقليل الحمل على قاعدة البيانات بنسبة 80-90%. في هذه المقالة، سننظر في ثلاثة أساليب للتخزين المؤقت على مستوى متقدم: Redis وMemcached والحلول المدمجة في TypeScript.

\n\n

1. التخزين المؤقت المدمج في TypeScript: من Map البسيط إلى LRU-cache

\n\n

الحلول المدمجة مثالية للتطبيقات المتجانسة أو الخدمات المصغرة حيث لا يلزم وجود ذاكرة تخزين مؤقت موزعة. الخيار الأكثر بدائية هو استخدام Map، لكنه غير مناسب للإنتاج بسبب عدم وجود TTL والتحكم في الذاكرة.

\n\n

تنفيذ LRU-cache مع TTL

\n\n

لننظر في تنفيذ متقدم لذاكرة التخزين المؤقت LRU (الأقل استخدامًا مؤخرًا) مع التنظيف التلقائي:

\n\n
interface CacheEntry<T> {\n  value: T;\n  expiresAt: number;\n}\n\nclass LRUCache<T> {\n  private cache: Map<string, CacheEntry<T>>;\n  private readonly maxSize: number;\n  private readonly ttlMs: number;\n\n  constructor(maxSize: number = 100, ttlMs: number = 60000) {\n    this.cache = new Map();\n    this.maxSize = maxSize;\n    this.ttlMs = ttlMs;\n  }\n\n  get(key: string): T | undefined {\n    const entry = this.cache.get(key);\n    if (!entry) return undefined;\n\n    if (Date.now() > entry.expiresAt) {\n      this.cache.delete(key);\n      return undefined;\n    }\n\n    // نقل المفتاح إلى النهاية (الأحدث)\n    this.cache.delete(key);\n    this.cache.set(key, entry);\n    return entry.value;\n  }\n\n  set(key: string, value: T): void {\n    if (this.cache.has(key)) {\n      this.cache.delete(key);\n    } else if (this.cache.size >= this.maxSize) {\n      // حذف العنصر الأقدم (الأول)\n      const oldestKey = this.cache.keys().next().value;\n      this.cache.delete(oldestKey);\n    }\n\n    this.cache.set(key, {\n      value,\n      expiresAt: Date.now() + this.ttlMs,\n    });\n  }\n\n  clear(): void {\n    this.cache.clear();\n  }\n}\n\n// الاستخدام\nconst userCache = new LRUCache<User>(500, 300000); // 500 عنصر، TTL 5 دقائق
\n\n

نمط متقدم: مصمم التخزين المؤقت

\n\n

لنظافة الكود نستخدم مصممات TypeScript:

\n\n
function Cached(ttlMs: number = 60000) {\n  const cache = new LRUCache<any>(100, ttlMs);\n\n  return function (\n    target: any,\n    propertyKey: string,\n    descriptor: PropertyDescriptor\n  ) {\n    const originalMethod = descriptor.value;\n\n    descriptor.value = async function (...args: any[]) {\n      const key = `${propertyKey}_${JSON.stringify(args)}`;\n      const cached = cache.get(key);\n      if (cached !== undefined) return cached;\n\n      const result = await originalMethod.apply(this, args);\n      cache.set(key, result);\n      return result;\n    };\n\n    return descriptor;\n  };\n}\n\nclass UserService {\n  @Cached(120000) // دقيقتان\n  async getUser(id: string): Promise<User> {\n    // استعلام مكلف لقاعدة البيانات\n    return db.findUser(id);\n  }\n}
\n\n

2. Redis: تخزين مؤقت موزع مع هياكل بيانات متقدمة

\n\n

Redis ليس مجرد مخزن key-value. دعمه لهياكل البيانات المعقدة (Hash, List, Set, Sorted Set) يفتح إمكانيات لتنفيذ استراتيجيات تخزين مؤقت متقدمة.

\n\n

التخزين المؤقت مع الإبطال بالعلامات

\n\n

المشكلة: عند تحديث كيان واحد، يجب إبطال عدة ذاكرات تخزين مؤقت. الحل - العلامات:

\n\n
import Redis from 'ioredis';\n\nclass TaggedCache {\n  private redis: Redis;\n  private readonly keyPrefix: string;\n\n  constructor(redisUrl: string, prefix: string = 'cache') {\n    this.redis = new Redis(redisUrl);\n    this.keyPrefix = prefix;\n  }\n\n  async get<T>(key: string,                

المدونات

توصيات الكتب