leecode:LRU算法

一、LRU算法介绍

LRU(Least Recently Used)算法是一种常见的页面置换算法,主要用于缓存淘汰策略。其核心思想是基于时间局部性原理:如果数据最近被访问过,那么将来被访问的概率也会更高。因此,LRU算法会优先淘汰最近最少使用的数据。

二、mysql和redis中的使用

ySQL 和 Redis 都采用了 LRU 算法来管理内存中的缓存数据,以提高性能并防止内存溢出。下面是它们如何使用 LRU 算法的:

MySQL 中的 LRU

在 MySQL 中,尤其是在使用 InnoDB 存储引擎时,LRU 算法主要用于管理缓冲池(Buffer Pool)中的数据页。

  • 缓冲池: 缓冲池是 InnoDB 用于缓存数据页的内存区域。当查询需要访问某个数据页时,如果该页已经在缓冲池中,则直接读取;否则,需要从磁盘加载该页到缓冲池中。

  • LRU 列表: 缓冲池中的数据页按访问时间组织在一个 LRU 列表中。最新访问的数据页会被移到列表的头部,最久未被访问的数据页位于尾部。

  • 淘汰策略: 当缓冲池满了,需要腾出空间以加载新的数据页时,InnoDB 会从 LRU 列表的尾部淘汰最近最少使用的页面。

此外,InnoDB 对传统的 LRU 算法进行了优化,引入了一个叫做“midpoint insertion strategy”的策略,这样能更好地适应访问模式,使得频繁访问的数据页在缓冲池中保留更长时间。

Redis 中的 LRU

Redis 是一个内存数据库,LRU 算法用于管理键值对的内存使用。当 Redis 的内存使用达到配置的最大限制时,LRU 算法决定哪些键值对需要被淘汰。

  • 内存淘汰策略: Redis 提供多种内存淘汰策略,其中 volatile-lruallkeys-lru 是基于 LRU 算法的:

    • volatile-lru: 仅对设置了过期时间的键应用 LRU 算法。
    • allkeys-lru: 对所有键应用 LRU 算法。
  • 近似 LRU 算法: Redis 实现的 LRU 是一种近似 LRU,它并不严格记录每个键的精确访问顺序,而是基于随机采样的方式来近似判断哪些键最近最少使用。这种方法在保证性能的同时,能够有效减少计算资源的消耗。

总结而言,MySQL 和 Redis 都通过 LRU 算法来管理内存中缓存的数据,有效提高了系统的性能并避免了内存溢出

三、leecode代码思路与代码实现

为什么使用map+双向链表

使用 map(哈希表)和双向链表来实现 LRU 算法的主要原因是为了在 O(1) 时间复杂度内完成缓存的 查找、插入和删除 操作。这种组合数据结构使得 LRU 缓存既高效又便于操作

go代码

package main

import "fmt"

//思路: 实现双向链表的  addToHead 、Move以及ConstructDoublyList

type Node struct {
	Key  int
	Val  int
	Prev *Node
	Next *Node
}

type DoublyList struct {
	head *Node
	tail *Node
}

func ConstructDoublyList() *DoublyList {
	head := &Node{}
	tail := &Node{}
	head.Next = tail
	tail.Prev = head
	return &DoublyList{head, tail}

}

func (this *DoublyList) AddToHead(node *Node) {

	if node == nil {
		return
	}
	node.Next = this.head.Next
	node.Prev = this.head

	this.head.Next.Prev = node
	this.head.Next = node

}

func (this *DoublyList) Move(node *Node) {

	node.Prev.Next = node.Next
	node.Next.Prev = node.Prev
}

type LRUCache struct {
	doublyList *DoublyList
	cache      map[int]*Node
	capacity   int
}

func Constrctor(capacity int) LRUCache {

	return LRUCache{
		doublyList: ConstructDoublyList(),
		cache:      map[int]*Node{},
		capacity:   capacity,
	}
}

func (this *LRUCache) Get(key int) int {
	var res int
	if node, ok := this.cache[key]; ok {
		res = node.Val
		this.doublyList.Move(node)
		this.doublyList.AddToHead(node)

	} else {
		res = -1
	}
	return res
}

func (this *LRUCache) Put(key int, value int) {

	if node, ok := this.cache[key]; ok {
		node.Val = value
	} else {

		tail := this.doublyList.tail.Prev
		if len(this.cache) == this.capacity {
			delete(this.cache, tail.Key)
			this.doublyList.Move(tail)
		}

		//新加
		newNode := &Node{Key: key, Val: value}
		this.doublyList.AddToHead(newNode)
		this.cache[key] = newNode

	}

}

// 测试
func main() {

	lru := Constrctor(2)
	fmt.Println(lru.Get(1))
	lru.Put(1, 1)
	lru.Put(2, 2)
	lru.Get(1)
	lru.Put(3, 3)
	fmt.Println(lru.Get(1))

}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值