JavaScript实现TwoQueues缓存模型
本文所指TwoQueues缓存模型,是说数据在内存中的缓存模型。
无论何种语言,都可能需要把一部分数据放在内存中,避免重复运算、读取。最常见的场景就是JQuery选择器,有些Dom元素的选取是非常耗时的,我们希望能把这些数据缓存起来,不必每次调用都去重新遍历Dom树。
存就存吧,但总得有个量吧!总不能把所有的历史数据都放在内存中,毕竟目前内存的容量还是相当可怜的,就算内存够大,理论上每个线程分配的内存也是有限制的。
那么问题来了,如何才能高效的把真正有用的数据缓存起来呢?这就涉及到淘汰算法,需要把垃圾数据淘汰掉,才能保住有用的数据。
比较常用的思路有以下几种:
FIFO:就是一个先进先出的队列,最先缓存的数据,最早被淘汰,著名的JQuery框架内部就是用的这种模型。
LRU:双链表结构,每次有新数据存入,直接放在链表头;每次被访问的数据,也转移到链表头,这样一来,链表尾部的数据即是最近没被使用过的,淘汰之。
TwoQueues:FIFO+ LRU,FIFO主要存放初次存入的数据,LRU中存放至少使用过两次的热点数据,此算法命中率高,适应性强,复杂度低。
其他淘汰算法还有很多很多,但实际用的比较多的也就这两种。因为他们本身算法不复杂,容易实现,执行效率高,缓存的命中率在大多数场合也还可以接受。毕竟缓存算法也是需要消耗CPU的,如果太过复杂,虽然命中率有所提高,但得不偿失。试想一下,如果从缓存中取数据,比从原始位置取还消耗时间,要缓存何用?
具体理论就不多说了,网上有的是,我也不怎么懂,今天给大家分享的是JavaScript版的TwoQueues缓存模型。
还是先说说使用方法,很简单。
基本使用方法如下:
var tq = initTwoQueues(10);
tq.set("key", "value");
tq.get("key");
初始化的时候,指定一下缓存容量即可。需要注意的是,由于内部采用FIFO+LRU实现,所以实际容量是指定容量的两倍,上例指定的是10个(键值对),实际上可以存放20个。
容量大小需要根据实际应用场景而定,太小命中率低,太大效率低,物极必反,需要自己衡量。
在开发过程中,为了审查缓存效果如何,可以将缓存池初始化成开发版:
var tq = initTwoQueues(10, true);
tq.hitRatio();
就是在后边加一个参数,直接true就可以了。这样初始化的缓存池,会自动统计命中率,可以通过hitRatio方法获取命中率。如果不加这个参数,hitRatio方法获取的命中率永远为0。
统计命中率肯定要消耗资源,所以生产环境下不建议开启。
是时候分享代码了:
(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));
最后,再次提醒,缓存算法需要和实际应用场景相结合,没有万能算法,合适的才是最好的!
JavaScript实现TwoQueues缓存模型的更多相关文章
- cache4j轻量级java内存缓存框架,实现FIFO、LRU、TwoQueues缓存模型
简介 cache4j是一款轻量级java内存缓存框架,实现FIFO.LRU.TwoQueues缓存模型,使用非常方便. cache4j为java开发者提供一种更加轻便的内存缓存方案,杀鸡焉用EhCac ...
- Backbone.js 为复杂Javascript应用程序提供模型(models)、集合(collections)、视图(views)的结构
Backbone.js 为复杂Javascript应用程序提供模型(models).集合(collections).视图(views)的结构.其中模型用于绑定键值数据和 自定义事件:集合附有可枚举函数 ...
- 一个用于每一天JavaScript示例-使用缓存计算(memoization)为了提高应用程序性能
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
- 写了一个Java的简单缓存模型
缓存操作接口 /** * 缓存操作接口 * * @author xiudong * * @param <T> */ public interface Cache<T> { /* ...
- 基于JVM原理、JMM模型和CPU缓存模型深入理解Java并发编程
许多以Java多线程开发为主题的技术书籍,都会把对Java虚拟机和Java内存模型的讲解,作为讲授Java并发编程开发的主要内容,有的还深入到计算机系统的内存.CPU.缓存等予以说明.实际上,在实际的 ...
- 基于.net的分布式系统限流组件 C# DataGridView绑定List对象时,利用BindingList来实现增删查改 .net中ThreadPool与Task的认识总结 C# 排序技术研究与对比 基于.net的通用内存缓存模型组件 Scala学习笔记:重要语法特性
基于.net的分布式系统限流组件 在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可 ...
- 【Java并发编程】从CPU缓存模型到JMM来理解volatile关键字
目录 并发编程三大特性 原子性 可见性 有序性 CPU缓存模型是什么 高速缓存为何出现? 缓存一致性问题 如何解决缓存不一致 JMM内存模型是什么 JMM的规定 Java对三大特性的保证 原子性 可见 ...
- 【多线程与高并发原理篇:1_cpu多级缓存模型】
1. 背景 现代计算机技术中,cpu的计算速度远远高于主内存的读写速度.为了解决速度不匹配问题,充分利用cpu的性能,在cpu与主内存之间加入了多级缓存,也叫高速缓存,cpu读取数据直接从高速缓存中读 ...
- 6张图为你分析Kafka Producer 消息缓存模型
摘要:发送消息的时候, 当Broker挂掉了,消息体还能写入到消息缓存中吗? 本文分享自华为云社区<图解Kafka Producer 消息缓存模型>,作者:石臻臻的杂货铺. 在阅读本文之前 ...
随机推荐
- CozyRSS开发记录11-够用的RSS源管理
CozyRSS开发记录11-够用的RSS源管理 1.分析需求 先随手画个用例图来看看有哪些参与者会访问我们的源管理: 2.搞一个Controller类 根据前面分析得出的用例图,我们就可以来设计实现一 ...
- SQL Server中的Merge关键字
本文转载地址:http://www.cnblogs.com/CareySon/archive/2012/03/07/2383690.html 简介 Merge关键字是一个神奇的DML关键字.它在SQL ...
- 1.Linux中安装LNMP过程
第一步安装mysql过程 安装包mysql-5.0.22.tar.gz,解压tar -zxvf mysql-5.0.22.tar.gz cd mysql-5.0.22 进行源码安装./configu ...
- 20145223《信息安全系统设计基础》 GDB调试汇编堆栈过程分析
20145223<信息安全系统设计基础> GDB调试汇编堆栈过程分析 分析的c语言源码 生成汇编代码--命令:gcc -g example.c -o example -m32 进入gdb调 ...
- UI设计颜色风格
有关颜色搭配方案: 摘取:http://bbs.9ria.com/thread-395-1-1.html 有关设计,可以参考:http://www.sj33.cn/
- 使用极光推送(www.jpush.cn)向安卓手机推送消息【服务端向客户端主送推送】C#语言
在VisualStudio2010中新建网站JPushAndroid.添加引用json帮助类库Newtonsoft.Json.dll. 在web.config增加appkey和mastersecret ...
- 封装jdbc 单例模式的应用
实现增删该查的jdbc封装 import java.io.IOException; import java.io.InputStream; import java.sql.Connection; im ...
- Jstack Jmap jstat
jstack jmap jstat 代码,这里以这个为例怎样使用jstack诊断Java应用程序故障 public class DeadLock { public static void main(S ...
- Print a Binary Tree in Vertical Order
http://www.geeksforgeeks.org/print-binary-tree-vertical-order/ package algorithms; import java.util. ...
- dom4j解析示例
收藏信息.xml <?xml version="1.0" encoding="GB2312" standalone="no"?> ...