源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

JavaScript版的TwoQueues缓存模型

  • 时间:2020-08-05 22:04 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:JavaScript版的TwoQueues缓存模型
本文所指TwoQueues缓存模型,是说数据在内存中的缓存模型。      无论何种语言,都可能需要把一部分数据放在内存中,避免重复运算、读取。最常见的场景就是JQuery选择器,有些Dom元素的选取是非常耗时的,我们希望能把这些数据缓存起来,不必每次调用都去重新遍历Dom树。      存就存吧,但总得有个量吧!总不能把所有的历史数据都放在内存中,毕竟目前内存的容量还是相当可怜的,就算内存够大,理论上每个线程分配的内存也是有限制的。      那么问题来了,如何才能高效的把真正有用的数据缓存起来呢?这就涉及到淘汰算法,需要把垃圾数据淘汰掉,才能保住有用的数据。      比较常用的思路有以下几种: [b]         FIFO:就是一个先进先出的队列,最先缓存的数据,最早被淘汰,著名的JQuery框架内部就是用的这种模型。[/b] [b]         LRU:双链表结构,每次有新数据存入,直接放在链表头;每次被访问的数据,也转移到链表头,这样一来,链表尾部的数据即是最近没被使用过的,淘汰之。[/b] [b]         TwoQueues:FIFO+ LRU,FIFO主要存放初次存入的数据,LRU中存放至少使用过两次的热点数据,此算法命中率高,适应性强,复杂度低。[/b]      其他淘汰算法还有很多很多,但实际用的比较多的也就这两种。因为他们本身算法不复杂,容易实现,执行效率高,缓存的命中率在大多数场合也还可以接受。毕竟缓存算法也是需要消耗CPU的,如果太过复杂,虽然命中率有所提高,但得不偿失。试想一下,如果从缓存中取数据,比从原始位置取还消耗时间,要缓存何用?      具体理论就不多说了,网上有的是,我也不怎么懂,今天给大家分享的是JavaScript版的TwoQueues缓存模型。      还是先说说使用方法,很简单。      基本使用方法如下: [/code]  var tq = initTwoQueues(10);  tq.set("key", "value");  tq.get("key"); [/code]      初始化的时候,指定一下缓存容量即可。需要注意的是,由于内部采用FIFO+LRU实现,所以实际容量是指定容量的两倍,上例指定的是10个(键值对),实际上可以存放20个。      容量大小需要根据实际应用场景而定,太小命中率低,太大效率低,物极必反,需要自己衡量。      在开发过程中,为了审查缓存效果如何,可以将缓存池初始化成开发版:
[u]复制代码[/u] 代码如下:
var tq = initTwoQueues(10, true); tq.hitRatio();
  就是在后边加一个参数,直接true就可以了。这样初始化的缓存池,会自动统计命中率,可以通过hitRatio方法获取命中率。如果不加这个参数,hitRatio方法获取的命中率永远为0。   统计命中率肯定要消耗资源,所以生产环境下不建议开启。   是时候分享代码了:
[u]复制代码[/u] 代码如下:
 (function(exports){      /**       * 继承用的纯净类       * @constructor       */      function Fn(){}      Fn.prototype = Elimination.prototype;      /**       * 基于链表的缓存淘汰算法父类       * @param maxLength 缓存容量       * @constructor       */      function Elimination(maxLength){          this.container = {};          this.length = 0;          this.maxLength = maxLength || 30;          this.linkHead = this.buildNode("", "");          this.linkHead.head = true;          this.linkTail = this.buildNode("", "");          this.linkTail.tail = true;          this.linkHead.next = this.linkTail;          this.linkTail.prev = this.linkHead;      }      Elimination.prototype.get = function(key){          throw new Error("This method must be override!");      };      Elimination.prototype.set = function(key, value){          throw new Error("This method must be override!");      };      /**       * 创建链表中的节点       * @param data 节点包含的数据,即缓存数据值       * @param key 节点的唯一标示符,即缓存的键       * @returns {{}}       */      Elimination.prototype.buildNode = function(data, key){          var node = {};          node.data = data;          node.key = key;          node.use = 0;          return node;      };      /**       * 从链表头弹出一个节点       * @returns {*}       */      Elimination.prototype.shift = function(){          var node = null;          if(!this.linkHead.next.tail){              node = this.linkHead.next;              this.linkHead.next = node.next;              node.next.prev = this.linkHead;              delete this.container[node.key];              this.length--;          }          return node;      };      /**       * 从链表头插入一个节点       * @param node 节点对象       * @returns {*}       */      Elimination.prototype.unshift = function(node){          node.next = this.linkHead.next;          this.linkHead.next.prev = node;          this.linkHead.next = node;          node.prev = this.linkHead;          this.container[node.key] = node;          this.length++;          return node;      };      /**       * 从链表尾插入一个节点       * @param node 节点对象       * @returns {*}       */      Elimination.prototype.append = function(node){          this.linkTail.prev.next = node;          node.prev = this.linkTail.prev;          node.next = this.linkTail;          this.linkTail.prev = node;          this.container[node.key] = node;          this.length++;          return node;      };      /**       * 从链表尾弹出一个节点       * @returns {*}       */      Elimination.prototype.pop = function(){          var node = null;          if(!this.linkTail.prev.head){              node = this.linkTail.prev;              node.prev.next = this.linkTail;              this.linkTail.prev = node.prev;              delete this.container[node.key];              this.length--;          }          return node;      };      /**       * 从链表中移除指定节点       * @param node 节点对象       * @returns {*}       */      Elimination.prototype.remove = function(node){          node.prev.next = node.next;          node.next.prev = node.prev;          delete this.container[node.key];          this.length--;          return node;      };      /**       * 节点被访问需要做的处理,具体是把该节点移动到链表头       * @param node       */      Elimination.prototype.use = function(node){          this.remove(node);          this.unshift(node);      };        /**       * LRU缓存淘汰算法实现       * @constructor       */      function LRU(){          Elimination.apply(this, arguments);      }      LRU.prototype = new Fn();      LRU.prototype.get = function(key){          var node = undefined;          node = this.container[key];          if(node){              this.use(node);          }          return node;      };      LRU.prototype.set = function(key, value){          var node = this.buildNode(value, key);          if(this.length === this.maxLength){              this.pop();          }          this.unshift(node);      };        /**       * FIFO缓存淘汰算法实现       * @constructor       */      function FIFO(){          Elimination.apply(this, arguments);      }      FIFO.prototype = new Fn();      FIFO.prototype.get = function(key){          var node = undefined;          node = this.container[key];          return node;      };      FIFO.prototype.set = function(key, value){          var node = this.buildNode(value, key);          if(this.length === this.maxLength){              this.shift();          }          this.append(node);      };        /**       * LRU、FIFO算法封装,成为新的twoqueues缓存淘汰算法       * @param maxLength       * @constructor       */      function Agent(maxLength){          this.getCount = 0;          this.hitCount = 0;          this.lir = new FIFO(maxLength);          this.hir = new LRU(maxLength);      }      Agent.prototype.get = function(key){          var node = undefined;          node = this.lir.get(key);          if(node){              node.use++;              if(node.use >= 2){                  this.lir.remove(node);                  this.hir.set(node.key, node.data);              }          }else{              node = this.hir.get(key);          }          return node;      };      Agent.prototype.getx = function(key){          var node = undefined;          this.getCount++;          node = this.get(key);          if(node){              this.hitCount++;          }          return node;      };      Agent.prototype.set = function(key, value){          var node = null;          node = this.lir.container[key] || this.hir.container[key];          if(node){              node.data = value;          }else{              this.lir.set(key, value);          }      };      /**       * 获取命中率       * @returns {*}       */      Agent.prototype.hitRatio = function(){          var ret = this.getCount;          if(ret){              ret = this.hitCount / this.getCount;          }          return ret;      };      /**       * 对外接口       * @param maxLength 缓存容量       * @param dev 是否为开发环境,开发环境会统计命中率,反之不会       * @returns {{get, set: Function, hitRatio: Function}}       */      exports.initTwoQueues = function(maxLength, dev){          var api = new Agent(maxLength);          return {              get: (function(){                  if(dev){                      return function(key){                          var ret = api.getx(key);                          return ret && ret.data;                      };                  }else{                      return function(key){                          var ret = api.get(key);                          return ret && ret.data;                      };                  }              }()),              set: function(){                  api.set.apply(api, arguments);              },              hitRatio: function(){                  return api.hitRatio.apply(api, arguments);              }          };      };    }(this));
     最后,再次提醒,缓存算法需要和实际应用场景相结合,没有万能算法,合适的才是最好的!
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部