Go语言中LRU缓存机制的实现

 更新时间:2025年08月14日 10:53:00   作者:程序员爱钓鱼  
本文详解如何用Go语言实现LRU缓存,通过哈希表+双向链表组合确保Get和Put操作均为O(1),具有一定的参考价值,感兴趣的可以了解一下

在高性能服务开发中,缓存是提升访问速度和减少后端负载的重要手段。常见的缓存淘汰策略中,**LRU(Least Recently Used,最近最少使用)**是应用最广的一种。本篇我们用Go语言手写一个LRU缓存机制的模拟实现。

一、LRU缓存机制简介

1. 定义

LRU缓存是一种固定容量的缓存结构。当缓存已满时,它会淘汰最近最少使用的那个数据。简单理解:

谁最久没被访问,就先删除谁。

2. 使用场景

  • Web浏览器缓存
  • 数据库查询结果缓存
  • 操作系统页面置换

二、设计要求

LRU缓存应支持以下操作:

  1. Get(key):如果key存在,返回对应的value,并将该key标记为最近使用;否则返回-1。
  2. Put(key, value):插入或更新key。如果容量已满,需要删除最近最少使用的key。

要求两种操作都能在 O(1)  时间复杂度内完成。

三、核心数据结构

要实现 O(1) 操作,需要组合以下两种结构:

1. 哈希表(map)

  • 用于存储 key → 节点 的映射;
  • 可在 O(1) 时间内找到节点。

2. 双向链表

  • 用于维护数据访问的顺序;
  • 头部表示最近使用,尾部表示最久未使用;
  • 插入、删除节点都是 O(1)。

四、Go语言实现

1. 节点结构

type Node struct {
    key, value int
    prev, next *Node
}

2. LRU缓存结构

type LRUCache struct {
    capacity int
    cache    map[int]*Node
    head     *Node
    tail     *Node
}
  • head 和 tail 是哨兵节点(dummy),方便操作。

3. 初始化

func Constructor(capacity int) LRUCache {
    head := &Node{}
    tail := &Node{}
    head.next = tail
    tail.prev = head

    return LRUCache{
        capacity: capacity,
        cache:    make(map[int]*Node),
        head:     head,
        tail:     tail,
    }
}

4. 辅助方法

// moveToHead:将节点移动到头部
func (l *LRUCache) moveToHead(node *Node) {
    l.removeNode(node)
    l.addToHead(node)
}

// removeNode:删除链表中的节点
func (l *LRUCache) removeNode(node *Node) {
    prev := node.prev
    next := node.next
    prev.next = next
    next.prev = prev
}

// addToHead:在头部插入节点
func (l *LRUCache) addToHead(node *Node) {
    node.prev = l.head
    node.next = l.head.next
    l.head.next.prev = node
    l.head.next = node
}

// removeTail:删除尾部节点并返回它
func (l *LRUCache) removeTail() *Node {
    node := l.tail.prev
    l.removeNode(node)
    return node
}

5. 核心操作

Get

func (l *LRUCache) Get(key int) int {
    if node, ok := l.cache[key]; ok {
        l.moveToHead(node)
        return node.value
    }
    return -1
}

Put

func (l *LRUCache) Put(key int, value int) {
    if node, ok := l.cache[key]; ok {
        node.value = value
        l.moveToHead(node)
    } else {
        newNode := &Node{key: key, value: value}
        l.cache[key] = newNode
        l.addToHead(newNode)

        if len(l.cache) > l.capacity {
            tail := l.removeTail()
            delete(l.cache, tail.key)
        }
    }
}

五、完整代码示例

package main

import "fmt"

type Node struct {
    key, value int
    prev, next *Node
}

type LRUCache struct {
    capacity int
    cache    map[int]*Node
    head     *Node
    tail     *Node
}

func Constructor(capacity int) LRUCache {
    head := &Node{}
    tail := &Node{}
    head.next = tail
    tail.prev = head
    return LRUCache{
        capacity: capacity,
        cache:    make(map[int]*Node),
        head:     head,
        tail:     tail,
    }
}

func (l *LRUCache) Get(key int) int {
    if node, ok := l.cache[key]; ok {
        l.moveToHead(node)
        return node.value
    }
    return -1
}

func (l *LRUCache) Put(key int, value int) {
    if node, ok := l.cache[key]; ok {
        node.value = value
        l.moveToHead(node)
    } else {
        newNode := &Node{key: key, value: value}
        l.cache[key] = newNode
        l.addToHead(newNode)
        if len(l.cache) > l.capacity {
            tail := l.removeTail()
            delete(l.cache, tail.key)
        }
    }
}

func (l *LRUCache) moveToHead(node *Node) {
    l.removeNode(node)
    l.addToHead(node)
}

func (l *LRUCache) removeNode(node *Node) {
    prev := node.prev
    next := node.next
    prev.next = next
    next.prev = prev
}

func (l *LRUCache) addToHead(node *Node) {
    node.prev = l.head
    node.next = l.head.next
    l.head.next.prev = node
    l.head.next = node
}

func (l *LRUCache) removeTail() *Node {
    node := l.tail.prev
    l.removeNode(node)
    return node
}

func main() {
    cache := Constructor(2)
    cache.Put(1, 1)
    cache.Put(2, 2)
    fmt.Println(cache.Get(1)) // 1
    cache.Put(3, 3)           // 淘汰 key=2
    fmt.Println(cache.Get(2)) // -1
    cache.Put(4, 4)           // 淘汰 key=1
    fmt.Println(cache.Get(1)) // -1
    fmt.Println(cache.Get(3)) // 3
    fmt.Println(cache.Get(4)) // 4
}

六、复杂度分析

  • 时间复杂度:O(1),Get 和 Put 都只涉及哈希表查找和链表操作。
  • 空间复杂度:O(capacity),存储固定大小的map和链表节点。

七、工程实践与优化

  1. 1. 线程安全
    在多协程环境中,需使用 sync.Mutex 或 sync.RWMutex 保证安全。
  2. 2. 泛型支持(Go1.18+)
    可以用泛型实现支持任意类型的key/value。
  3. 3. 监控统计
    可增加命中率统计、淘汰计数。

八、应用场景

  • 数据库缓存:Redis内部就支持LRU策略;
  • 浏览器缓存:网页资源加载优化;
  • API限速器:存储用户最近访问记录。

九、总结

  • LRU缓存结合了 哈希表 + 双向链表
  • 关键是 O(1) 时间内完成访问和淘汰
  • 该思想可扩展到 LFU、ARC 等高级缓存策略。

到此这篇关于Go语言中LRU缓存机制的实现的文章就介绍到这了,更多相关Go语言 LRU缓存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • 详解golang执行Linux shell命令完整场景下的使用方法

    详解golang执行Linux shell命令完整场景下的使用方法

    本文主要介绍了golang执行Linux shell命令完整场景下的使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • 详解Go语言中的监视器模式与配置热更新

    详解Go语言中的监视器模式与配置热更新

    这篇文章主要为大家详细介绍了Go语言中的监视器模式与配置热更新的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-03-03
  • Go语言并发之Sync包的6个关键概念总结

    Go语言并发之Sync包的6个关键概念总结

    这篇文章主要为大家详细介绍了Go语言并发中Sync包的6个关键概念,文中的示例代码讲解详细,对我们深入学习Go语言有一定的帮助,需要的可以参考一下
    2023-05-05
  • 浅析Go语言中数组的这些细节

    浅析Go语言中数组的这些细节

    这篇文章主要为大家详细介绍了Go语言中数组一些细节的相关资料,文中的示例代码讲解详细,对我们学习Go语言有一定的帮助,需要的可以了解一下
    2022-11-11
  • 多阶段构建优化Go 程序Docker镜像

    多阶段构建优化Go 程序Docker镜像

    这篇文章主要为大家介绍了多阶段构建优化Go 程序Docker镜像,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • mac下golang安装了windows编译环境后编译变慢

    mac下golang安装了windows编译环境后编译变慢

    这篇文章主要介绍了mac下golang安装了windows编译环境后编译变慢的处理方法,非常的简单,有相同问题的小伙伴可以参考下。
    2015-04-04
  • Golang应用程序性能优化技巧分享

    Golang应用程序性能优化技巧分享

    随着科技的进步,人人都想要快速的应用,这就需要优化您的应用程序性能。本文为大家整理了一些Golang应用程序性能优化的技巧,希望对大家有所帮助
    2023-04-04
  • grpool goroutine池协程管理

    grpool goroutine池协程管理

    这篇文章主要介绍了grpool goroutine池协程管理,goroutine协程非常轻量级,这也是为什么go支持高并发,但是goroutine频繁创建销毁对GC的压力比较大,文章围绕主题展开详细的内容介绍,需要的小伙伴可以参考一下
    2022-06-06
  • golang等待触发事件的实例

    golang等待触发事件的实例

    这篇文章主要介绍了golang等待触发事件的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • go中结构体切片的实现示例

    go中结构体切片的实现示例

    Go语言中的结构体切片是一种结合了结构体和切片特点的数据结构,用于存储和操作多个结构体实例,具有一定的参考价值,感兴趣的可以了解一下
    2024-11-11

最新评论