Oscache与memcached
前言
缓存在互联网平台大量使用以提高性能,降低数据库负担,更快的响应用户的请求。原因主要是缓存的使用场景是性能要求高,实时性或准确性要求稍差。
其他提高性能手段:
1, 数据库方面改革,效果最明显。如主从服务器。数据库水平和垂直拆分(一般用户表不能拆分)。数据库索引,全文索引。数据库是否用事务表,慢查询监控
2, Lucene,蜘蛛的使用,利用文件索引减少查询数据库
3, Web服务器或应用服务器选择及负载均衡。 Tomcat,resin,jboss.,weblogic
4, 图片服务器和web服务器分离,可以是多个。图片标记,多种图片格式,图片名称和用户分离,名称变得无规则
5, Cs,js等的压缩,减少流量,并可能到客户端一部分信息
6, 登陆或瓶颈模块设计的不同。验证码或多步登陆。很多网站长期没改进,可能在对抗性能瓶颈。大规模改版风险很大。在什么时候升级系统
7, 程序角度:架构选择(如Facebook,API,GOOGLE,API),多线程的使用(单实例多线程,主线程子线程),代码算法结构,数据结构
容器概念内置对象:page,session,application
使用Cache,可以缓存网页,提高浏览速度,还可以缓存任何对象和数据,减少对数据库的操作。Session可以存储对象,但是必须使用Session,但是对于Sesion,并不是任何情况下都是需要的;Application可以存储对象,但是生存周期太长,从而决定了不可以存储对象太多;所以有了Cache,它可以不涉及使用Session,又有自己的生存周期,可以比起Application来,相对大量的使用。
在信息系统建设过程中我们通常会遇到这样的问题:
1. 基础数据的变更问题
信息系统中需要处理的基础数据的内容短时间内是不会发生变化的,但是在一个相对长一些的时间里,它却可能是动态增加或者减少的。
举个例子:电子商务中关于送货区域的定义,可能短时间内不会发生变化,但是随着电子商务企业业务的扩大,系统中需要处理的送货区域就可能增加。所以我们的系 统中不得不在每次向客户展示送货区域信息的时候都和数据库(假设送货区域信息保存在数据库中,这也是通常采用的处理方法)进行交互。
2. 统计报表(不仅限于统计报表)的问题
一般来说,统计报表是一个周期性的工作,可能是半个月、一个月或者更长的时间才会需要更新一次,然而统计报表通常是图形显示或者是生成pdf、word、excel等格式的文件,这些图形内容、文件的生成通常需要消耗很多的系统资源,给系统运行造成很大的负担。
1.2 抽象出的缓存使用的共同点
通过比较分析,不难发现这两类问题有一些共同点:
1、被处理的内容短时间不变,所以短时间内可以作为静态内容进行处理
2、在一个不太长的时间内,被处理的内容可能或者必定产生变化,所以必须将他们作为动态内容进行处理
3、实时性要求不高:在合理的时间区段内可以忽略被处理内容变化后带来的影响
4、对这些内容的处理动作比较消耗系统性能,影响系统响应时间
在开源项目的出镜率和关注率比较高的Cache:memcached,JBoss Cache, OSCache,EHCache等。
以下是Cache的基本特性,大部分Cache都有相应的API支持。
¨ 时间记录 数据进入Cache的时间。
¨ timeout过期时间 Cache里面的数据多久过期 (与清除策略的区分,一个缓存快到期突然最后访问了一次,会不会过期,区分点是清除策略的作用前提,理论上没到期也可能被清除出去,到期肯定被清除)
¨ Eviction Policy 清除策略 Cache满了之后,根据什么策略,应该清除哪些数据。
比如,最不经常被访问的数据,最久没有访问到的数据。
¨ 命中率 Cache的数据被选中的比率 (访问多少次,cache使用多少次)
¨ 分级Cache 有些Cache有分级的概念。比如,几乎所有的Cache都支持Region分区的概念。可以指定某一类的数据存放在特定的Region里面。JBoss Cache可以支持更多的级别(有待研究)。
¨ 分布式Cache 分布在不同计算机上的Cache
¨ 锁,事务,数据同步 一些Cache提供了完善的锁,事务支持。
Memcached与Oscache的朴素区分及各自特点
部署不同,memcache一般跟web服务器不在同一主机,当然也可以在同一主机,利用的是资源互补原理。 对内存要求很高,对cpu要求稍低。Oscache与web服务器在同一台。
从作用的层来说,对memcache各层都可以支持。Oscache大多数用于web层,其实也支持biz层,一般不用
Memcache的特点:分布式内存对象缓存系统,占用其他机子的内存。很多互联网,负载均衡三台(以三台为例)web服务器可以共享一台Memcache的资源。传递的信息以键值对的形式存储。传递的数据要实现序列化。
Oscache的特点:页面级缓存(网上强调最多的东西),占用本机的内存资源。可以选择缓存到硬盘,如存取到硬盘重启服务也可重新获得上次持久化的资源,而如果缓存到内存就不行。一般没必要缓存到硬盘(谁愿意刚出了一个监狱,又走进另一个监狱,哈哈),因为I/O操作也是比较耗资源,和从数据库取往往优势很小。Oscache存取数据的作用域分为application和session两种。
参考oscache的帮助资料猜想它的实现原理是把要存储共享的资源的作用域分为应用程序全局共享application(对性能优化作用很明显)和某个用户某次操作某个网站session(用户和web服务器会话)级共享两种。对共享的资源设置过期时间。即当服务器运行时发现在作用域内还有对象,就以类似静态页面的形式(因为缓存了要显示的信息)提供给客户端,当过期了,就需要重查资源(如果是页面里的缓存就需要资源能在jsp里提供)。如果没有过期,但内容改变了,可以通过flush的方式清除老资源以便加载新的资源。Oscache页面级缓存可缓存jsp,需要改变web.xml配置文件,里面可以设定缓存的作用域和过期时间.关于页面级缓存action,资料比较少,新的oscache已经解决了struts 的缓存问题,但最大的问题是MVC模式中, 是先取数据, 取出来后,再放到网页上显示, 如果在网页上用KEY, 应该起不到作用. 因为取出数据在KEY标签之前执行.究其原因主要是因为缓存实际上缓存的是输出信息,即使jsp也是输出信息。不过把jsp当作独立的运行单元一起缓存。 因为毕竟有作用域,那么在action里优势就可以有其他方式解决。这一块在用了memcache后用oscache就没有太大用处.
Oscache的一个特点是在没有过期时,jsp里的运行单元不依赖于输入参数,如果依赖,那么缓存就没有作用了,如果用了可以取数据的memcache时使用场景比较小。当然如果不使用memcache,使用场景就比较大。
Oscache
OSCache有以下特点:
l 缓存任何对象:你可以不受限制的缓存部分jsp页面或HTTP请求,任何java对象都可以缓存。
l 拥有全面的API:OSCache API允许你通过编程的方式来控制所有的OSCache特性。
l 支持永久缓存:缓存能被配置写入硬盘,因此允许在应用服务器的多次生命周期间缓存创建开销昂贵的数据。
l 支持集群:集群缓存数据能被单个的进行参数配置,不需要修改代码。
l 缓存过期:你可以有最大限度的控制缓存对象的过期,包括可插入式的刷新策略(如果默认性能不能满足需要时)。
其实页面级缓存我持怀疑态度,动态性太强或实时性太强不能缓存,或意义不大。
Oscache使用
OSCache是一个基于web应用的组件,他的安装工作主要是对web应用进行配置,大概的步骤如下:
1. 下载、解压缩OSCache
http://www.opensymphony.com/oscache/download.action 下载Oscache的最新版本,这里下载的是OSCache的2.4.1版本。
2. 新建立一个web应用
3. 将oscache-2.4.1.jar放入WEB-INF\lib目录
4. commons-logging.jar、jgroups-all.jar的处理
OSCache 组件用Jakarta Commons Logging来处理日志信息,所以需要commons-logging.jar的支持,将%OSCache_Home%\lib\ commons-logging.jar放入WEB-INF\lib目录
5. 将/etc/oscache.properties放入WEB-INF\classes目录 /etc/META-INF/oscache.tld放入WEB-INF\classes目录
oscache.properties包含了对OSCache运行特征值的设置信息 oscache.tld包含了OSCache提供的标签库的定义内容
6. 修改web.xml文件
在web.xml文件中增加下面的内容,增加对OSCache提供的taglib的支持:
<jsp-config>
<taglib><taglib-uri>oscache</taglib-uri>
<taglib-location>/WEB-INF/classes/oscache.tld
</taglib-location>
</taglib>
</jsp-config>
问题:除了在web.xml里引入tld 没有引入更多实质性的东西,猜测它是如何工作的。如何实现集群的,如何管理组的。如何退出组的。Web.xml是Struts,spring,dwr等必争之地。Hibernate 除外
.oscache.properties 文件配置向导
cache.memory
值为true 或 false ,默认为在内存中作缓存,
如设置为false,那cache只能缓存到数据库或硬盘中,那cache还有什么意义:)
cache.capacity
缓存元素个数
cache.persistence.class
持久化缓存类,如此类打开,则必须设置cache.path信息
cache.cluster 相关
为集群设置信息。
如
cache.cluster.multicast.ip为广播IP地址
cache.cluster.properties为集群属性
实现基础是 jgroups,jms(推)等方式
3.OSCache的基本用法
cache1.jsp 内容如下
<%@ page import="java.util.*" %>
<%@ taglib uri="oscache" prefix="cache" %>
<html>
<body>
没有缓存的日期: <%= new Date() %><p>
<!--自动刷新-->
<cache:cache time="30">
每30秒刷新缓存一次的日期: <%= new Date() %>
</cache:cache>
<!--手动刷新-->
<cache:cache key="testcache">
手动刷新缓存的日期: <%= new Date() %> <p>
</cache:cache>
<a href="cache2.jsp">手动刷新</a>
</body>
</html>
cache2.jsp 执行手动刷新页面如下
<%@ taglib uri="oscache" prefix="cache" %>
<html>
<body>
缓存已刷新...<p>
<cache:flush key="testcache" scope="application"/>
<a href="cache1.jsp">返回</a>
</body>
</html>
你也可以通过下面语句定义Cache的有效范围,如不定义scope,scope默认为Applcation
<cache:cache time="30" scope="session">
...
</cache:cache>
4. 缓存过滤器 CacheFilter
你可以在web.xml中定义缓存过滤器,定义特定资源的缓存。
<filter>
<filter-name>CacheFilter</filter-name>
<filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class>
<init-param>
<param-name>time</param-name>
<param-value>60</param-value>
</init-param>
<init-param>
<param-name>scope</param-name>
<param-value>session</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
上面定义将缓存所有.jsp页面,缓存刷新时间为60秒,缓存作用域为Session
注意,CacheFilter只捕获Http头为200的页面请求,即只对无错误请求作缓存,
而不对其他请求(如500,404,400)作缓存处理
缓存单个文件
修改web.xml,增加如下内容,确定对/cache/cache3.jsp页面进行缓存。
<filter>
<filter-name>CacheFilter</filter-name>
<filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<!-对/testContent.jsp页面内容进行缓存-->
<url-pattern>/cache/cache3.jsp </url-pattern></filter-mapping>
缓存URL pattern
修改web.xml,增加如下内容,确定对*.jsp页面进行缓存。
<filter> <filter-name>CacheFilterStaticContent</filter-name>
<filter-class>com.opensymphony.oscache.web.filter.CacheFilter< /filter-class>
</filter><filter-mapping><filter-name> CacheFilterStaticContent </filter-name>
<!-对所有jsp页面内容进行缓存-->
<url-pattern>/cache-pages/*.jsp</url-pattern></filter-mapping>
自己设定缓存属性
在页面级缓存的情况下,可以通过设置CacheFilter的初始属性来决定缓存的一些特性:time属性设置缓存的时间段,默认为3600秒,可以根据自己的需要设置,而scope属性设置,默认为application,可选项包括application、session
<filter>
<filter-name>CacheFilter</filter-name>
<filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class>
<init-param>
<param-name>time</param-name>
<param-value>600</param-value>
</init-param>
<init-param>
<param-name>scope</param-name>
<param-value>session</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>/cache/cache3.jsp</url-pattern>
</filter-mapping>
总之OSCache缓存归结为4种:
¨ POJO 缓存
¨ HTTP Response 缓存
¨ JSP Tag Library 缓存
¨ O/R Data Access 缓存
1、POJO 缓存
这种方式的缓存直接调用OSCache的API进行,主要用于处理页面内容会根据参数动态改变,可以将参数设置为key值来保存数据:
首先,声明成员变量:
// OSCache Adminitrator instance
private static GeneralCacheAdministrator cacheAdmin = null;
其次,进行初始化:
public RingArtistAction() {
cacheAdmin = new GeneralCacheAdministrator();
}
将POJO进行缓存:
// Cache data key and refresh period
String key = sex + ":" + place;
int refreshPeriod = Constants.getIntegerValue(Constants.OSCACHE_REFRESH_PERIOD).intValue();
try {
// Get from the cache
artists = (Map) cacheAdmin.getFromCache(key, refreshPeriod);
} catch (NeedsRefreshException nre) {
try {
// Get the value (probably from the database)
int count = getArtistCount(sex, place, errors);
artists = getArtistData(sex, place, count, errors);
// Store in the cache
cacheAdmin.putInCache(key, artists);
} catch (Exception ex) {
// We have the current content if we want fail-over.
artists = (Map) nre.getCacheContent();
// It is essential that cancelUpdate is called if the
// cached content is not rebuilt
cacheAdmin.cancelUpdate(key);
ex.printStackTrace();
}
}
2、HTTP Response 缓存
这种方式的缓存用来处理整个页面的内容固定,不会根据参数动态改变:
首先在web.xml中配置CacheFilter:
CacheFilter
com.opensymphony.oscache.web.filter.CacheFilter
time
86400
scope
application
将所有需要缓存的页面加入filter-mapping:
Set Character Encoding
/*
注意,只有返回状态为200(HttpServletResponse.SC_OK)的内容才会被缓存
3、JSP Tag 缓存
JSP Tag缓存主要用于缓存JSP页面的局部内容:
4、O/R Data Access 缓存
请阅读参考资料的内容获取详情。
Hibernate 查询缓存和对象缓存
Oscache的集群是通过jGroups和jms实现。还有个缺点就是不支持传递业务数据,只传递key值,cache中目前仅知道jbosscache支持传递数据。可能是出于安全性和流量方面的考量。
Oscache曾经试验过用标签驱动,第一个加入的是组长,组长退出了再选择一个。只要有参加的必然有一个组长。现在只对删除有明显效果。
Memcached
Memcached是什么
memcached 是以LiveJournal 旗下Danga Interactive 公司的Brad Fitzpatric 为首开发的一款软件。
memcached是高性能的分布式内存缓存服务器。一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、提高可扩展性。
现在已成为 mixi、 hatena、 Facebook、 Vox、LiveJournal等众多服务中提高Web应用扩展性的重要因素。
下图是展示的是 一般情况下memcached的用途
memcached的特征
memcached作为高速运行的分布式缓存服务器,具有以下的特点。
¨ 基于libevent的事件处理
¨ 内置内存存储方式
¨ memcached不互相通信的分布式
协议文档位于memcached的源代码内理
libevent是个程序库,它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能封装成统一的接口。即使对服务器的连接数增加,也能发挥O(1)的性能。 memcached使用这个libevent库,因此能在Linux、BSD、Solaris等操作系统上发挥其高性能。关于事件处理这里就不再详细介绍,可以参考Dan Kegel的The C10K Problem。
libevent: http://www.monkey.org/~provos/libevent/
The C10K Problem: http://www.kegel.com/c10k.html
内置内存存储方式
为了提高性能,memcached中保存的数据都存储在memcached内置的内存存储空间中。由于数据仅存在于内存中,因此重启memcached、重启操作系统会导致全部数据消失。另外,内容容量达到指定值之后,就基于LRU(Least Recently Used)算法自动删除不使用的缓存。 memcached本身是为缓存而设计的服务器,因此并没有过多考虑数据的永久性问题。关于内存存储的详细信息,本连载的第二讲以后前坂会进行介绍,请届时参考。
memcached不互相通信的分布式
memcached尽管是“分布式”缓存服务器,但服务器端并没有分布式功能。各个memcached不会互相通信以共享信息。那么,怎样进行分布式呢?这完全取决于客户端的实现。
下图为memcached的分布式
接下来简单介绍一下memcached的使用方法。
Memcached使用
安装memcached
memcached的安装比较简单,这里稍加说明。
memcached支持许多平台。
¨ Linux
¨ FreeBSD
¨ Solaris (memcached 1.2.5以上版本)
¨ Mac OS X
¨ windows
这里使用windows进行说明。
下载memcached for win32:http://jehiah.cz/projects/memcached-win32/
这里使用的版本是:memcached-1.2.0-win32
解压之后执行memcached.exe.
默认监听端口是:11211
memcached的基本设置:
-p 监听的端口
-l 连接的IP地址, 默认是本机
-d start 启动memcached服务
-d restart 重启memcached服务
-d stop|shutdown 关闭正在运行的memcached服务
-d install 安装memcached服务
-d uninstall 卸载memcached服务
-u 以的身份运行 (仅在以root运行的时候有效)
-m 最大内存使用,单位MB。默认64MB
-M 内存耗尽时返回错误,而不是删除项
-c 最大同时连接数,默认是1024
-f 块大小增长因子,默认是1.25
-n 最小分配空间,key+value+flags默认是48
-h 显示帮助
用客户端连接
许多语言都实现了连接memcached的客户端,其中以Perl、PHP为主。memcached网站上列出的语言有
Perl ,PHP ,Python ,Ruby ,C# ,C/C++ ,Lua,java等等。
memcached客户端API:http://www.danga.com/memcached/apis.bml
这里我们使用java的客户端。选择下载java客户端。java_memcached-release_1.3.2.jar
下面的资源一般是从配置文件读取,文件名没有规定,可自定义
Memcached应用
package cache;
import java.util.Date;
import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;
public class TestMemcached {
protected static MemCachedClient mcc = new MemCachedClient();
static {
String[] servers = { "127.0.0.1:11211" };
Integer[] weights = { new Integer(3) };
// 创建一个实例对象SockIOPool
SockIOPool pool = SockIOPool.getInstance();
// set the servers and the weights
// 设置Memcached Server
pool.setServers(servers);
pool.setWeights(weights);
// set some basic pool settings
// 5 initial, 5 min, and 250 max conns
// and set the max idle time for a conn
// to 6 hours
pool.setInitConn(5);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaxIdle(1000 * 60 * 60 * 6);
// set the sleep for the maint thread
// it will wake up every x seconds and
// maintain the pool size
pool.setMaintSleep(30);
// Tcp的规则就是在发送一个包之前,本地机器会等待远程主机
// 对上一次发送的包的确认信息到来;这个方法就可以关闭套接字的缓存,
// 以至这个包准备好了就发;
pool.setNagle(false);
// 连接建立后对超时的控制
pool.setSocketTO(3000);
// 连接建立时对超时的控制
pool.setSocketConnectTO(0);
// initialize the connection pool
// 初始化一些值并与MemcachedServer段建立连接
pool.initialize();
// lets set some compression on for the client
// compress anything larger than 64k
mcc.setCompressEnable(true);
mcc.setCompressThreshold(64 * 1024);
}
public static void bulidCache() {
// set(key,value,Date) ,Date是一个过期时间,如果想让这个过期时间生效的话,这里传递的new Date(long
// date) 中参数date,需要是个大于或等于1000的值。
// 因为java client的实现源码里是这样实现的 expiry.getTime() / 1000 ,也就是说,如果
// 小于1000的值,除以1000以后都是0,即永不过期
mcc.set("test", "This is a test String", new Date(10000)); // 十秒后过期
}
public static void output() {
// 从cache里取值
String value = (String) mcc.get("test");
System.out.println(value);
}
public static void main(String[] args) {
bulidCache();
output();
}
}
memcached是缓存,所以数据不会永久保存在服务器上,这是向系统中引入memcached的前提。
memcached在数据删除方面有效利用资源数据不会真正从memcached中消失
memcached不会释放已分配的内存。记录超时后,客户端就无法再看见该记录(invisible,透明),其存储空间即可重复使用。
Lazy Expiration
memcached内部不会监视记录是否过期,而是在get时查看记录的时间戳,检查记录是否过期。这种技术被称为lazy(惰性)expiration。因此,memcached不会在过期监视上耗费CPU时间。
LRU:从缓存中有效删除数据的原理
memcached会优先使用已超时的记录的空间,但即使如此,也会发生追加新记录时空间不足的情况,此时就要使用名为 Least Recently Used(LRU)机制来分配空间。顾名思义,这是删除“最近最少使用”的记录的机制。因此,当memcached的内存空间不足时(无法从slab class 获取到新的空间时),就从最近未被使用的记录中搜索,并将其空间分配给新的记录。从缓存的实用角度来看,该模型十分理想。
不过,有些情况下LRU机制反倒会造成麻烦。memcached启动时通过“-M”参数可以禁止LRU,如下所示:
$ memcached -M -m 1024
启动时必须注意的是,小写的“-m”选项是用来指定最大内存大小的。不指定具体数值则使用默认值64MB。
指定“-M”参数启动后,内存用尽时memcached会返回错误。话说回来,memcached毕竟不是存储器,而是缓存,所以推荐使用LRU。
Memcached总结
memcached是一个Client Server结构的远程Cache实现。 Server是用C写的,提供了多种语言的客户端API,包括Java, C#, Ruby, Python, PHP, Perl, C等多种语言。应用程序通过客户端API,从memcached server存取数据。
典型的应用,比如,用memcached作为数据库缓存。
也常有这样的用法,用memcached存放HTTP Session的数据。具体做法是包装Session Interface,截获setAttribute(), getAttribute()方法。
不同计算机上的应用程序通过一个IP地址来访问memcahced Server。
同一个key对应的数据,只存在于一台memcached server的一份内存中。
memcached server也可以部署在多台计算机上。Memcached通过key的hashcode来判断从哪台memcached server上存取数据数据。我们可以看到,同一个key对应的数据,还是只存在于一台memcached server的一份内存中。
所以,memcached不存在数据同步的问题。
memcached由于是远程Cache,要求放到Cache的Key和Value都是Serializable。
它的工作原理,有服务器端, 有客户端,比较容易理解。注意的是互联网环境升级时很少重启memcache服务器端,为的是减少大量的数据库查询。
问题:父类没有实现序列化,子类实现了序列化,会不会出问题?
Memcache的特点:分布式内存对象缓存系统 ;从协议而言:tcp/ip,udp协议
Oscache的特点:页面级缓存.从协议而言。http和tcp/ip.
参考文档
http://yahaitt.javaeye.com/blog/125268
http://www.whalin.com/memcached/
http://your.javaeye.com/blog/125926
http://tech.idv2.com/2008/07/10/memcached-001/
http://iunknown.javaeye.com/blog/41077
http://blog.chinaunix.net/u/26257/showart_464419.html(Memcahe 原理简介)
http://www.javaeye.com/upload/attachment/25116/9bf20cc6-8fe7-37ae-8b23-f77038e4c4cd.png
http://jackyoo8.javaeye.com/blog/195858
http://www.blogjava.net/landy/archive/2006/05/06/44756.html (oscache分析)
http://www.opensymphony.com/oscache/
http://www.blogjava.net/jackstudio/archive/2006/09/08/68569.html
http://marshal.javaeye.com/blog/143835
http://blog.ccidnet.com/blog-htm-itemid-118813-uid-40217-type-blog-do-showone.html (oscache比较全面)