التخزين المؤقت في C++: Redis وأدوات أخرى لتسريع التطبيقات
التخزين المؤقت هو أحد الآليات الرئيسية لتحسين أداء التطبيقات بلغة C++. في الأنظمة الحديثة، حيث سرعة الوصول إلى البيانات أمر بالغ الأهمية (خوادم الويب، الألعاب، الخلفيات عالية التحميل)، يمكن للتخزين المؤقت المهيأ بشكل جيد أن يقلل وقت الاستجابة بعشرات المرات. في هذه المقالة، سنشرح كيفية تنظيم التخزين المؤقت في C++ باستخدام Redis والحلول البديلة، بما في ذلك هياكل البيانات المدمجة.
لماذا نحتاج إلى التخزين المؤقت في C++؟
الهدف الرئيسي من التخزين المؤقت هو تخزين البيانات المطلوبة بشكل متكرر في مخزن سريع (عادة في الذاكرة العشوائية)، لتجنب العمليات المكلفة المتكررة: الاستعلامات إلى قاعدة البيانات، الحسابات، أو الاستدعاءات الشبكية. بالنسبة لـ C++، هذا مهم بشكل خاص، حيث تُستخدم اللغة في أنظمة الوقت الفعلي والخدمات عالية التحميل.
Redis — تخزين مؤقت موزع لـ C++
Redis هو مخزن بيانات في الذاكرة (in-memory) يُستخدم غالبًا كذاكرة تخزين مؤقت. للعمل مع Redis من C++، توجد عدة مكتبات ناضجة:
- hiredis — مكتبة C بسيطة، العميل الرسمي لـ Redis. خفيفة الوزن، لكنها تتطلب إدارة يدوية للذاكرة وكتابة صارمة.
- redis-plus-plus — غلاف حديث بلغة C++17 فوق hiredis. يدعم تجمعات الاتصالات، المعاملات، Redis Cluster والعمليات غير المتزامنة.
- cpp_redis — مكتبة أخرى بلغة C++11 بتركيب مشابه لـ hiredis، لكن مع واجهة برمجة تطبيقات أكثر سهولة.
مثال على الاتصال والاستخدام الأساسي لـ redis-plus-plus:
#include <sw/redis++/redis++.h>
using namespace sw::redis;
int main() { // الاتصال بـ Redis (خادم محلي، منفذ 6379) auto redis = Redis("tcp://127.0.0.1:6379");
// الكتابة في التخزين المؤقت مع TTL (مدة حياة 60 ثانية) redis.setex("user:123:profile", 60, "{\\"name\\":\\"Alice\\",\\"age\\":30}");
// القراءة من التخزين المؤقت auto val = redis.get("user:123:profile"); if (val) { std::cout << "تم العثور على التخزين المؤقت: " << *val << std::endl; } else { std::cout << "التخزين المؤقت فارغ، يجب تحميل البيانات من قاعدة البيانات" << std::endl; }
return 0;}مزايا Redis: التوزيع، دعم الهياكل المعقدة (القوائم، التجزئات، المجموعات)، TTL، pub/sub. العيوب: الحاجة إلى خادم منفصل، تأخير الشبكة (وإن كان ضئيلاً).
التخزين المؤقت المدمج في C++: LRU-cache وطبقة البيانات
إذا لم تكن بحاجة إلى نظام موزع، يمكنك تنفيذ التخزين المؤقت مباشرة في التطبيق. الخوارزمية الأكثر شيوعًا هي LRU (الأقل استخدامًا مؤخرًا). تقوم بإزالة البيانات التي لم يتم استخدامها لأطول فترة.
مثال على تنفيذ LRU-cache بسيط بلغة C++17 باستخدام std::unordered_map و std::list:
#include <unordered_map>#include <list>#include <stdexcept>
template<typename Key, typename Value>class LRUCache {public: LRUCache(size_t capacity) : capacity_(capacity) {}
Value get(const Key& key) { auto it = cache_.find(key); if (it == cache_.end()) { throw std::runtime_error("Key not found"); } // نقل العنصر إلى بداية القائمة (الأكثر استخدامًا مؤخرًا) order_.splice(order_.begin(), order_, it->second.second); return it->second.first; }
void put(const Key& key, const Value& value) { auto it = cache_.find(key); if (it != cache_.end()) { // تحديث القيمة ونقلها إلى البداية it->second.first = value; order_.splice(order_.begin(), order_, it->second.second); return; } // إذا كان التخزين المؤقت ممتلئًا، قم بإزالة العنصر الأخير (LRU) if (cache_.size() == capacity_) { auto last = order_.back(); order_.p