التخزين المؤقت في Go (Golang): Redis وحلول أخرى
التخزين المؤقت هو أحد الأدوات الرئيسية لتحسين أداء تطبيقات Go. فهو يسمح بتقليل وقت الاستجابة، وتخفيف الحمل على قواعد البيانات وواجهات API الخارجية. في هذه المقالة، سنستعرض الأساليب الرئيسية للتخزين المؤقت في Go: استخدام Redis كمخزن خارجي، التخزين المؤقت المدمج في الذاكرة، والتخزين المؤقت الجماعي باستخدام مكتبة groupcache.
1. لماذا نحتاج إلى التخزين المؤقت في Go؟
Go هي لغة موجهة نحو الأداء العالي، ولكن حتى أسرع الاستعلامات إلى قاعدة البيانات أو الخدمات الخارجية يمكن أن تصبح عنق زجاجة. يساعد التخزين المؤقت في:
- تقليل التأخير في الاستعلامات المتكررة.
- تقليل الحمل على قواعد البيانات وواجهات API الخارجية.
- زيادة سعة الخادم.
- تخفيف الأحمال الذروة.
2. Redis — التخزين المؤقت الموزع
Redis هو الحل الأكثر شيوعًا للتخزين المؤقت الخارجي في Go. يعمل كمخزن في الذاكرة مع دعم TTL، والثبات، وهياكل البيانات المعقدة.
ربط Redis في Go
للعمل مع Redis في Go، غالبًا ما تُستخدم مكتبة go-redis. التثبيت:
go get github.com/redis/go-redis/v9مثال: تخزين مؤقت بسيط مع TTL
package main
import ( "context" "fmt" "time" "github.com/redis/go-redis/v9")
var ctx = context.Background()
func main() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", })
// تعيين قيمة مع TTL لمدة 10 ثوانٍ err := rdb.Set(ctx, "user:42", `{"name":"Alice"}`, 10*time.Second).Err() if err != nil { panic(err) }
// الحصول على القيمة val, err := rdb.Get(ctx, "user:42").Result() if err != nil { fmt.Println("خطأ في التخزين المؤقت") } else { fmt.Println("إصابة التخزين المؤقت:", val) }}استراتيجيات إبطال التخزين المؤقت في Redis
- TTL (Time To Live) — الحذف التلقائي بعد انتهاء الوقت.
- الإبطال الكسول (Lazy-invalidation) — مسح التخزين المؤقت عند تحديث البيانات.
- الحذف حسب المفتاح — استدعاء
DELلمفتاح معين. - نمط "Cache-Aside" — يدير التطبيق التخزين المؤقت بنفسه: يتحقق أولاً من Redis، وعند الخطأ يقوم بتحميل البيانات من قاعدة البيانات ويحفظها في التخزين المؤقت.
3. التخزين المؤقت في الذاكرة في Go
للتخزين المؤقت المحلي دون تبعيات خارجية، يمكن استخدام هياكل Go المدمجة أو المكتبات المتخصصة.
تخزين مؤقت بسيط في الذاكرة باستخدام sync.Map
package main
import ( "fmt" "sync" "time")
type CacheItem struct { Value interface{} Expiration int64}
type Cache struct { items sync.Map}
func (c *Cache) Set(key string, value interface{}, ttl time.Duration) { c.items.Store(key, CacheItem{ Value: value, Expiration: time.Now().Add(ttl).UnixNano(), })}
func (c *Cache) Get(key string) (interface{}, bool) { item, found := c.items.Load(key) if !found { return nil, false } cacheItem := item.(CacheItem) if time.Now().UnixNano() > cacheItem.Expiration { c.items.Delete(key) return nil, false } return cacheItem.Value, true}
func main() { cache := &Cache{} cache.Set("key1", "value1", 5*time.Second) val, ok := cache.Get("key1") if ok { fmt.Println("تم العثور على:", val) }}مكتبة freecache
freecache — مكتبة عالية الأداء للتخزين المؤقت في الذاكرة مع دعم TTL واستهلاك منخفض للذاكرة. التثبيت:
go get github.com/coocood/freecachecache := freecache.NewCache(100 * 1024 * 1024) // 100 MBcache.Set([]by