مقدمة: لماذا نحتاج إلى التخزين المؤقت في C++؟
في التطوير الحديث باستخدام C++، غالبًا ما تكون سرعة التطبيق عاملاً حاسمًا. يتيح التخزين المؤقت تقليل وقت الوصول إلى البيانات بشكل كبير، وتقليل الحمل على قواعد البيانات، وتسريع تنفيذ العمليات الحسابية المتكررة. في هذه المقالة، سنستعرض بالتفصيل ثلاثة مناهج رئيسية للتخزين المؤقت في C++: استخدام الأنظمة الخارجية (Redis و Memcached) والحلول المدمجة المتاحة "مباشرة من الصندوق".
بغض النظر عما إذا كنت تطور خادم ويب عالي التحميل، أو محرك ألعاب، أو نظامًا ماليًا، فإن الاختيار الصحيح لاستراتيجية التخزين المؤقت يمكن أن يعزز الأداء بعشرات المرات. سنقوم بتحليل إيجابيات وسلبيات كل نهج، بالإضافة إلى تقديم أمثلة جاهزة على الكود للتكامل السريع.
1. الحلول المدمجة للتخزين المؤقت في C++
قبل توصيل الخدمات الخارجية، يجدر النظر في الإمكانيات التي توفرها لغة C++ نفسها ومكتبتها القياسية. الحلول المدمجة مثالية للتطبيقات أحادية الخيط أو الحالات التي لا تحتاج فيها البيانات إلى المشاركة بين عمليات متعددة.
1.1. ذاكرة تخزين مؤقت بسيطة LRU تعتمد على std::unordered_map
خوارزمية LRU (الأقل استخدامًا مؤخرًا) هي واحدة من أكثر الخوارزميات شيوعًا للتخزين المؤقت المدمج. تقوم تلقائيًا بحذف العناصر الأقل استخدامًا عندما تصل الذاكرة المؤقتة إلى حد الذاكرة.
#include <unordered_map>#include <list>#include <mutex>
template <typename Key, typename Value>class LRUCache {private: size_t capacity_; std::list<Key> items_; std::unordered_map<Key, std::pair<Value, typename std::list<Key>::iterator>> cache_; std::mutex mutex_;
public: LRUCache(size_t capacity) : capacity_(capacity) {}
void put(const Key& key, const Value& value) { std::lock_guard<std::mutex> lock(mutex_); auto it = cache_.find(key); if (it != cache_.end()) { items_.erase(it->second.second); cache_.erase(it); } items_.push_front(key); cache_[key] = {value, items_.begin()}; if (cache_.size() > capacity_) { auto last = items_.back(); items_.pop_back(); cache_.erase(last); } }
std::optional<Value> get(const Key& key) { std::lock_guard<std::mutex> lock(mutex_); auto it = cache_.find(key); if (it == cache_.end()) return std::nullopt; // نقل المفتاح إلى بداية القائمة items_.splice(items_.begin(), items_, it->second.second); return it->second.first; }};المزايا: تكاليف إضافية ضئيلة، لا تأخيرات في الشبكة، تحكم كامل في البيانات.
العيوب: البيانات غير مشتركة بين الخيوط (تتطلب مزامنة يدوية)، محدودة بذاكرة عملية واحدة.
1.2. استخدام std::shared_mutex لتعدد الخيوط
للتطبيقات متعددة الخيوط، يمكن تحسين الأداء باستخدام shared_mutex، الذي يسمح بالقراءة المتزامنة لعدة خيوط:
#include <shared_mutex>
class ThreadSafeCache { std::map<std::string, std::string> data_; mutable std::shared_mutex mutex_; public: std::string read(const std::string& key) { std::shared_lock lock(mutex_); auto it = data_.find(key); return (it != data_.end()) ? it->second : ""; } void write(const std::string& key, const std::string& value) { std::unique_lock lock(mutex_); data_[key] = value; }};2. التخزين المؤقت مع Memcached في C++
Memcached هو نظام تخزين مؤقت موزع في الذاكرة العشوائية، مثالي لتخزين أجزاء صغيرة من البيانات. يتميز بسرعته العالية وبساطته، مما يجعله خيارًا شائعًا لتسريع تطبيقات الويب وقواعد البيانات.
```