2

LRU

Discards the least recently used items first. This algorithm requires keeping track of what was used when, which is expensive if one wants to make sure the algorithm always discards the least recently used item. General implementations of this technique require keeping "age bits" for cache-lines and track the "Least Recently Used" cache-line based on age-bits. In such an implementation, every time a cache-line is used, the age of all other cache-lines changes.

Lru-example

在上图中,一旦 A B C D 充满所分配的内存块,那么最新出现的 E 将替代最低使用的 A(0),访问顺序为 A -> B -> C -> D -> E -> D -> F。

原理

这里将会出现一个性能瓶颈,也就是在 Cache 满时,淘汰那些不常用的数据,空出空间存储新的数据。假设每一条数据都有一个最后访问时间, 当满额的时候,将要遍历所有元素,才能删除访问时间最小的那个元素,时间复杂度为 $O(1)$,数据量越大,性能越低。

所以选择使用 链表,每访问一次数据,把最新的访问数据放至头部,那尾部的数据就是最旧未访问的数据。 满额时,从链表尾部开始往前删除指定数目的数据,即可解决。

代码实现

class LruCache {
  constructor(maxsize) {
    this._cache = {}; // 缓存
    this._queue = []; // 队列
    this._maxsize = maxsize; // 最大值

    // 如果最大值输入非法 默认无限大
    if (!this._maxsize || !(typeof this._maxsize === 'number') || this._maxsize <= 0) {
      this._maxsize = Infinity;
    }

    // 运行定时器,定时检查过期值
    setInterval(() => {
      this._queue.forEach((el, idx) => {
        const key = el;
        const insertTime = this._cache[key].insertTime;
        const expire = this._cache[key].expire;
        const curTime = +new Date();

        // 如果存在过期时间且超期,移除数据
        if (expire && curTime - insertTime > expire) {
          this._queue.splice(idx--, 1);
          delete this._cache[key];
        }
      });
    }, 1000);
  }

  // 生成唯一索引
  _makeSymbol(key) {
    return Symbol.for(key);
  }

  // 更新队列
  _update(queue, key) {
    // 移除
    queue.forEach((el, idx) => {
      if (el === key) {
        queue.splice(idx, 1);
      }
    });

    // 前置
    queue.unshift(key);
    return queue;
  }

  // 插入数据
  set(key, value, expire) {
    key = this._makeSymbol(key); // 生成唯一索引

    // 如果已经存在该值,则重新赋值
    if (this._cache[key]) {
      this._cache[key] = {
        value,
        expire,
        insertTime: this._cache[key].insertTime
      }
      this._queue = this._update(this._queue, key); // 更新队列
    } else {
      // 如果不存在,则插入
      this._cache[key] = {
        value,
        expire,
        insertTime: +new Date()
      }

      // 索引置前
      this._queue.unshift(key);

      // 超出最大值,截断
      while (this._queue.length > this._maxsize) {
        const item = this._queue.pop(); // 尾截断
        delete this._cache[item]; // 删除
      }
    }
  }

  // 获取数据
  get(key) {
    key = this._makeSymbol(key);

    // 如果存在该值
    if (this._cache[key]) {
      const insertTime = this._cache[key].insertTime; // 插入时间
      const expire = this._cache[key].expire; // 过期时间
      const curTime = +new Date(); // 当前时间

      // 如果不存在过期时间 或 存在过期时间但尚未过期
      if (!expire || (expire && curTime - insertTime < expire)) {
        // 更新队列,前置索引
        this._queue = this._update(this._queue, key);

        return this._cache[key].value;
      } else if (expire && curTime - insertTime > expire) {
        // 已经过期
        this._queue.forEach((el, idx) => {
          if (el === key) {
            this._queue.slice(idx, 1);
            delete this._cache[key];
          }
        })

        return null
      }
    } else {
      return null;
    }
  }
}

同步至我的个人博客:https://blog.trevor.top/item/34


Amigooo
764 声望83 粉丝

自以为历尽沧桑,其实刚蹒跚学步;自以为掌握了竞争的秘密,其实远没有竞争的资格