LRU(Least recently used,最近最少使用),数据插入队列中,经常访问的数据单元靠近列表头部,不经常访问的靠近列表尾部。列表数据就像按照时间排序一样。常用来淘汰一些长时间不使用的数据。
1. 算法流程
lru 算法用一个列表也可以实现。只是列表数据需要更新操作,那得先查找数据,列表的查找时间复杂度是 O(n),这是个低效的操作,所以用时间复杂度为 O(1) 的哈希表 std::unordered_map 辅助实现查找。

2. 算法实现
简单实现 LRU 算法的添加,更新和删除最旧数据功能。定时测试相关接口操作。(测试源码放在 github)
2.1. 头文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#pragma once
#include <iostream>
#include <list>
#include <memory>
#include <unordered_map>
class Data {
   public:
    Data() {}
    Data(const Data& d) : m_key(d.m_key), m_value(d.m_value) {}
    Data(const std::string& key, const std::string& value)
        : m_key(key), m_value(value) {}
   public:
    std::string key() const { return m_key; }
    void set_key(const std::string& key) { m_key = key; }
    std::string value() const { return m_value; }
    void set_value(const std::string& value) { m_value = value; }
   private:
    std::string m_key;
    std::string m_value;
};
class Lru {
   public:
    Lru() {}
    virtual ~Lru() {}
    bool insert(const std::string& key, const std::string& value);
    bool update(const std::string& key, const std::string& value);
    bool remove(const std::string& key);
    /* 删除列表末位节点。*/
    bool pop();
    std::shared_ptr<Data> get_data(const std::string& key);
   private:
    /* 数据存储列表。*/
    std::list<std::shared_ptr<Data>> m_list;
    /* 更新哈希表数据,存储节点在列表的位置信息,方便查询。*/
    std::unordered_map<std::string, std::list<std::shared_ptr<Data>>::iterator> m_map;
};
2.2. 源文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/* g++ -g -O0 -W -std=c++11 lru.cpp -o test && ./test */
#include "lru.h"
bool Lru::insert(const std::string& key, const std::string& value) {
    if (key.empty() || value.empty() || (m_map.find(key) != m_map.end())) {
        return false;
    }
    m_list.push_front(std::make_shared<Data>(key, value));
    m_map[key] = m_list.begin();
    std::cout << "insert key: " << key << " value: " << value << std::endl;
    return true;
}
bool Lru::update(const std::string& key, const std::string& value) {
    /* 通过哈希表,查找对应节点在列表的位置。*/
    auto it = m_map.find(key);
    if (it == m_map.end()) {
        return false;
    }
    /*更新数据信息。*/
    auto p = *(it->second);
    p->set_value(value);
    /* 先从列表删除对应节点,然后重新将节点添加到列表头部。*/
    m_list.erase(it->second);
    m_list.push_front(p);
    /* 更新哈希表数据,存储节点在列表的位置信息,方便查询。*/
    m_map[p->key()] = m_list.begin();
    return true;
}
bool Lru::remove(const std::string& key) {
    auto it = m_map.find(key);
    if (it == m_map.end()) {
        return false;
    }
    m_map.erase(key);
    m_list.erase(it->second);
    return true;
}
bool Lru::pop() {
    if (m_list.empty()) {
        return false;
    }
    /* 删除哈希表对应信息。*/
    auto it = m_list.end();
    auto p = *(--it);
    m_map.erase(p->key());
    /* 删除列表对应节点。*/
    m_list.erase(it);
    std::cout << "pop: " << p->key() << " " << p->value() << std::endl;
    return true;
}
std::shared_ptr<Data> Lru::get_data(const std::string& key) {
    auto it = m_map.find(key);
    if (it != m_map.end()) {
        return *(it->second);
    }
    return nullptr;
}
3. redis 近似 lru 算法
redis 数据库 maxmemory 数据淘汰策略,通过采样实现了近似 LRU 的算法,有兴趣的朋友可以参考:
 
            