التخزين المؤقت في Go: Redis وMemcached والحلول المدمجة: من الألف إلى الياء
\n\nالتخزين المؤقت هو أحد الأدوات الرئيسية لتحسين أداء تطبيقات الويب والخدمات المصغرة. في Go (Golang) توجد العديد من الأساليب: من الخرائط البسيطة في الذاكرة إلى الأنظمة الموزعة القوية مثل Redis وMemcached. في هذه المقالة، سنستعرض جميع الخيارات الشائعة للتخزين المؤقت في Go مع أمثلة على الكود، ونقارن أداءها، ونعرض أفضل ممارسات التنفيذ.
\n\nلماذا نحتاج التخزين المؤقت في Go؟
\nبدون التخزين المؤقت، كل وصول إلى البيانات قد يؤدي إلى عمليات مكلفة: استعلامات قاعدة البيانات، استدعاءات API الخارجية، أو حسابات معقدة. يسمح التخزين المؤقت بحفظ نتيجة العملية الأولى وإعادتها بسرعة عند الطلبات المتكررة. هذا يقلل الحمل على الخادم الخلفي، ويقلل زمن الاستجابة (latency)، ويحسن تجربة المستخدم. في Go، بفضل goroutines والتزامن العالي، فإن التنفيذ الصحيح للتخزين المؤقت مهم بشكل خاص.
\n\n1. الحلول المدمجة: sync.Map وbigcache
\nللمشاريع الصغيرة أو الحالات التي لا تتطلب التوزيع، تقدم Go آليات تخزين مؤقت مدمجة.
\n\n1.1 sync.Map
\nsync.Map هي خريطة آمنة للاستخدام المتزامن من المكتبة القياسية لـ Go. تم تحسينها للحالات التي يتم فيها كتابة المفاتيح مرة واحدة وقراءتها عدة مرات. مثالية للتخزين المؤقت ذو التحديثات المنخفضة التردد.
package main\n\nimport (\n "fmt"\n "sync"\n)\n\nfunc main() {\n var cache sync.Map\n\n // كتابة\n cache.Store("user:1", "Alice")\n cache.Store("user:2", "Bob")\n\n // قراءة\n if value, ok := cache.Load("user:1"); ok {\n fmt.Println("تم العثور على:", value)\n }\n\n // حذف\n cache.Delete("user:2")\n\n // تكرار (Range)\n cache.Range(func(key, value interface{}) bool {\n fmt.Printf("المفتاح: %v، القيمة: %v\\n", key, value)\n return true\n })\n}\nالمزايا: لا تبعيات خارجية، واجهة برمجة تطبيقات بسيطة، آمن للاستخدام المتزامن.
\nالعيوب: لا يوجد TTL (وقت الحياة)، لا يوجد حد للحجم، ينخفض الأداء مع الكتابات المتكررة.
1.2 BigCache
\nBigCache هي مكتبة تخزين مؤقت سريعة في الذاكرة، مصممة خصيصًا لـ Go. تقلل من تخصيصات الذاكرة وتدعم TTL.
\npackage main\n\nimport (\n "fmt"\n "time"\n "github.com/allegro/bigcache/v3"\n)\n\nfunc main() {\n config := bigcache.Config{\n Shards: 1024,\n LifeWindow: 10 * time.Minute,\n CleanWindow: 5 * time.Minute,\n MaxEntriesInWindow: 1000,\n MaxEntrySize: 500,\n Verbose: true,\n }\n\n cache, err := bigcache.NewBigCache(config)\n if err != nil {\n panic(err)\n }\n\n // كتابة\n cache.Set("session:abc123", []byte("user_data"))\n\n // قراءة\n data, err := cache.Get("session:abc123")\n if err == nil {\n fmt.Println("البيانات:", string(data))\n }\n}\nالمزايا: سرعة عالية، دعم TTL، حمل منخفض على GC (جامع القمامة).
\nالعيوب: فقط لمثيل واحد من التطبيق، لا يوجد نسخ متماثل.
2. Memcached في Go
\nMemcached هو نظام تخزين مؤقت موزع في الذاكرة. إنه بسيط ولكنه سريع جدًا ويدعم TTL. في Go، يتم استخدام برنامج التشغيل github.com/bradfitz/gomemcache/memcache للعمل مع Memcached.
package main\n\nimport (\n "fmt"\n "github.com/bradfitz/gomemcache/memcache"\n)\n\nfunc main() {\n mc := memcache.New("127.0.0.1:11211")\n\n // كتابة\n err := mc.Set(&memcache.Item{\n Key: "user:42",\n Value: []byte("John Doe"),\n Expiration: 300, // 5 دقائق\n })\n if err != nil {\n panic(err)\n }\n\n // قراءة\n item, err := mc.Get("user:42")\n if err == nil {\n fmt.Println("القيمة:", string(item.Value))\n }\n\n // حذف