Maison  >  Article  >  développement back-end  >  Comment mettre en œuvre une stratégie d’élimination du cache dans Golang ?

Comment mettre en œuvre une stratégie d’élimination du cache dans Golang ?

王林
王林original
2023-06-20 11:16:57814parcourir

Golang est un langage de programmation devenu très populaire ces dernières années. L'une de ses caractéristiques est sa grande efficacité et sa forte concurrence. Lorsque nous utilisons Golang pour développer des applications Web, nous impliquons souvent l'utilisation du cache. La mise en cache peut améliorer les performances des applications et la vitesse de réponse, mais si nous ne gérons pas correctement l'expulsion du cache, le cache occupera trop de mémoire et affectera la stabilité du système. Cet article expliquera comment mettre en œuvre une stratégie d'élimination du cache dans Golang.

Qu'est-ce que l'élimination du cache ?

En termes simples, l'élimination du cache signifie que lorsque l'espace du cache n'est pas suffisant, certaines données du cache doivent être éliminées pour faire de la place à de nouvelles données du cache. La stratégie d’élimination des données du cache est souvent liée aux besoins réels de l’application.

Élimination du cache dans Golang

Dans Golang, nous pouvons utiliser le package conteneur dans la bibliothèque standard pour implémenter la stratégie d'élimination du cache. Ce package fournit deux structures de données, List et Heap, qui peuvent toutes deux être utilisées pour implémenter l'expulsion du cache.

List

List est une liste doublement chaînée dans la bibliothèque standard Golang. Nous pouvons ajouter des données mises en cache à la liste selon certaines règles et mettre à jour l'utilisation des données en temps réel. Lorsque l'espace du cache est insuffisant, nous pouvons supprimer certaines données du cache qui ne sont plus utilisées de la fin de la liste chaînée selon une certaine stratégie d'élimination.

Ce qui suit est un exemple de code simple pour implémenter la stratégie d'élimination LRU (Least Récemment Utilisé) :

type Cache struct {
    maxBytes  int64                    // 允许使用的最大内存
    usedBytes int64                    // 当前已使用的内存
    lruList   *list.List               // 双向链表
    cache     map[string]*list.Element // map 作为缓存数据的索引
    onEvicted func(key string, value []byte)
}

type entry struct {
    key   string
    value []byte
}

// Add 新增一个缓存
func (c *Cache) Add(key string, value []byte) {
    if ele, ok := c.cache[key]; ok {
        c.lruList.MoveToFront(ele)
        kv := ele.Value.(*entry)
        c.usedBytes += int64(len(value) - len(kv.value))
        kv.value = value
        return
    }
    ele := c.lruList.PushFront(&entry{key, value})
    c.cache[key] = ele
    c.usedBytes += int64(len(key) + len(value))
    if c.maxBytes > 0 && c.usedBytes > c.maxBytes {
        c.RemoveOldest()
    }
}

// Get 获取一个缓存
func (c *Cache) Get(key string) ([]byte, bool) {
    if ele, ok := c.cache[key]; ok {
        c.lruList.MoveToFront(ele)
        kv := ele.Value.(*entry)
        return kv.value, true
    }
    return nil, false
}

// RemoveOldest 删除最久未使用的缓存
func (c *Cache) RemoveOldest() { 
    ele := c.lruList.Back()
    if ele != nil {
        c.lruList.Remove(ele)
        kv := ele.Value.(*entry)
        delete(c.cache, kv.key)
        c.usedBytes -= int64(len(kv.key) + len(kv.value))
        if c.onEvicted != nil {
            c.onEvicted(kv.key, kv.value)
        }
    }
}

Dans le code ci-dessus, nous utilisons List pour enregistrer les données du cache et utilisons la carte du cache comme index pour trouver rapidement et facilement un certain cache. Lorsque l'espace de stockage du cache dépasse la limite, nous supprimons le cache qui n'a pas été utilisé depuis le plus longtemps en commençant par la fin de la liste (c'est-à-dire la politique LRU) pour libérer de l'espace. Dans le même temps, nous prenons également en charge d'autres fonctionnalités, telles que la définition de la mémoire maximale requise pour chaque cache et la prise en charge de certaines opérations spécifiques lorsque les données du cache sont supprimées.

Heap

Heap est le tas de la bibliothèque standard Golang. Il gère un ensemble de données selon une certaine règle de priorité (telle que le temps d'accès aux données mises en cache, la taille des données, etc.) et les insère automatiquement. et supprime les données selon les règles et la requête. De même, lorsque l'espace du cache est insuffisant, nous pouvons utiliser Heap pour éliminer automatiquement certaines données.

Ce qui suit est un exemple de code simple pour implémenter la stratégie d'élimination LFU (Least Frequency Used) :

type Item struct {
    Value  []byte
    Priority int // 优先级,即缓存访问次数
    Index  int    // 在 heap 中的索引
}

type PriorityQueue []*Item

// 实现 heap.Interface 接口的 Push 方法
func (pq *PriorityQueue) Push(x interface{}) {
    n := len(*pq)
    item := x.(*Item)
    item.Index = n
    *pq = append(*pq, item)
}

// 实现 heap.Interface 接口的 Pop 方法
func (pq *PriorityQueue) Pop() interface{} {
    old := *pq
    n := len(old)
    item := old[n-1]
    item.Index = -1 // 为了安全起见
    *pq = old[0 : n-1]
    return item
}

// 实现 heap.Interface 接口的 Len 方法
func (pq PriorityQueue) Len() int {
    return len(pq)
}

// 实现 heap.Interface 接口的 Less 方法
func (pq PriorityQueue) Less(i, j int) bool {
    return pq[i].Priority < pq[j].Priority
}

// 实现 heap.Interface 接口的 Swap 方法
func (pq PriorityQueue) Swap(i, j int) {
    pq[i], pq[j] = pq[j], pq[i]
    pq[i].Index = i
    pq[j].Index = j
}

type Cache struct {
    maxBytes  int64
    usedBytes int64
    cache     map[string]*Item
    queue     PriorityQueue
    onEvicted func(key string, value []byte)
}

// Add 新增一个缓存
func (c *Cache) Add(key string, value []byte) {
    if item, ok := c.cache[key]; ok {
        item.Priority++
        item.Value = value
        heap.Fix(&c.queue, item.Index)
    } else {
        item = &Item{Value: value, Priority: 1}
        c.cache[key] = item
        heap.Push(&c.queue, item)
    }
    c.usedBytes += int64(len(key) + len(value))
    if c.maxBytes > 0 && c.usedBytes > c.maxBytes {
        c.RemoveOldest()
    }
}

// Get 获取一个缓存
func (c *Cache) Get(key string) ([]byte, bool) {
    if item, ok := c.cache[key]; ok {
        item.Priority++
        heap.Fix(&c.queue, item.Index)
        return item.Value, true
    }
    return nil, false
}

// RemoveOldest 删除访问次数最少的缓存
func (c *Cache) RemoveOldest() {
    item := heap.Pop(&c.queue).(*Item)
    delete(c.cache, item.Value)
    c.usedBytes -= int64(len(item.Value) + item.Priority)
    if c.onEvicted != nil {
        c.onEvicted(item.Value, item.Value)
    }
}

Dans le code ci-dessus, nous utilisons Heap pour enregistrer les données du cache et utiliser la carte du cache comme index. Différent de List, en tas, nous gérons automatiquement la priorité des données mises en cache et des opérations telles que l'insertion et la suppression. Lorsque l'espace de stockage du cache dépasse la limite, le tas supprime automatiquement certaines données du cache auxquelles on accède moins fréquemment.

Résumé

Lors de l'écriture d'applications Web dans Golang, l'utilisation du cache est souvent inévitable. Mais pour éviter que les données mises en cache n’occupent trop de mémoire, nous devons gérer correctement l’expulsion du cache. En utilisant les structures de données List et Heap de la bibliothèque standard Golang, nous pouvons facilement mettre en œuvre des stratégies d'élimination du cache couramment utilisées et garantir le fonctionnement stable de l'application.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn