lru 淘汰算法
lru算法
计算机的内存缓存是有限的,在缓存满了就需要淘汰一部分数据,LRU 算法就是一种常用的淘汰旧数据的算法,LRU 算法的全称是 Least Recently Used,按照最近最少使用的原则来筛选数据,最不常用的数据会被筛选出来,而最近频繁使用的数据会留在缓存中。核心思想 如果数据最近有被访问过,那么将来被访问的几率也更高 。 redis 和 mysql 都有使用 LRU 算法来淘汰冷数据。
LRU 会把所有的数据组织成一个链表。每当有新的数据就插入到链表的头部,当链表中的数据被访问,就把数据移动到链表头部,如果链表满了,就从链表末尾开始淘汰。
C# 实现,LinkedList 作为双向链表删除和插入效率都高,但是查找起来就需要遍历,操作的时间复杂度为 O(n),可以使用一个 Dictionary 来记录 key 在链表中的位置。
public class LruCache
{
private LinkedList<KeyValuePair<int, string>> _list;
private Dictionary<int, LinkedListNode<KeyValuePair<int, string>>> _map;
private int _capacity;
public LruCache(int capacity)
{
_capacity = capacity;
_list = new LinkedList<KeyValuePair<int, string>>();
_map = new Dictionary<int, LinkedListNode<KeyValuePair<int, string>>>();
}
/// <summary>
/// 获取值,如果值存在就把当前值的移动到链表首部。
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string Get(int key)
{
if (!_map.ContainsKey(key)) return string.Empty;
var item = _map[key];
_list.Remove(item);
_map[key] = _list.AddFirst(item.Value);
return item.Value.Value;
}
public void Put(int key, string value)
{
if (_map.ContainsKey(key))
{
// key存在更新值,并且移到首部。
var item = _map[key];
_list.Remove(item);
_map[key] = _list.AddFirst(new KeyValuePair<int, string>(key, value));
}
else
{
// key不存在,判断是否链表还有空间。
if (_list.Count >= _capacity)
{
// 淘汰末尾key
_map.Remove(_list.Last.Value.Key);
_list.RemoveLast();
}
_map[key] = _list.AddFirst(new KeyValuePair<int, string>(key, value));
}
}