完整链接: http://blog.163.com/luowei505050@126/blog/static/11990720620128682726877/
memcache与EHcache的选择
使用缓存的原则就是:尽量用低开销的计算代替高开销的计算。比如直接从数据库查询的开销要远高于内存中的计算返回,网络请求要远高于本地(同一VM中)请求。
Memcache:分布式内存对象缓存系统,占用其他机子的内存。很多互联网,负载均衡三台(以三台为例)web服务器可以共享一台Memcache的资源。传递的信息以键值对的形式存储。传递的数据要实现序列化。
Oscache:页面级缓存(网上强调最多的东西),占用本机的内存资源。可 以选择缓存到硬盘,如存取到硬盘重启服务也可重新获得上次持久化的资源,而如果缓存到内存就不行。一般没必要缓存到硬盘,因为I/O操作也是比较耗资源,和从数据库取往往优势很小。Oscache存取数据的作用域分为application和session两种。
EhCache:Hibernate缓存,DAO缓存,安全性凭证缓存(Acegi),Web缓存,应用持久化和分布式缓存。
EhCache在默认情况下,即在用户未提供自身配置文件ehcache.xml或ehcache-failsafe.xml时,EhCache会依据其自身Jar存档包含的ehcache-failsafe.xml文件所定制的策略来管理缓存。如果用户在classpath下提供了ehcache.xml或ehcache-failsafe.xml文件,那么EhCache将会应用这个文件。如果两个文件同时提供,那么EhCache会使用ehcache.xml文件的配置。EhCache默认内容如下:
<ehcache>
<diskStore path="C:\Acegi6" />
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
缓存有三个作用范围:事务、应用、集群。事务级缓存在session中有效;应用级缓存在多个session中可共享,因此尽可能只在read only型应用中使用,而集群缓存就需要在各个节点上进行缓存同步。
Ehcache中,这些都是在同一个应用的前提条件下的,如果是多个应用在数据层整合,那么任何一个范围都有可能出现问题。尤其是Ehcache,是一个进程内的 (in process)的缓存方案,受Spring管理,每个Web App的缓存相互独立(抛开ClassLoader Share),基本上不可能实现多应用缓存共享。即使使用消息中间件进行监听,也不是一个完美的解决方案。
infoQ里有一篇文章讲述了在java的工作需求中memcached超过了terracotta's Ehcache以及oracle's Coherence 缓存框架, Memcached surpasses EhCache and Coherence in Java Job Demand.
Memcache与Ehcache的一个对比表格:
项目 | Memcache | Ehcache |
分布式 | 不完全,集群默认不实现 | 支持 |
集群 | 可通过客户端实现 | 支持(默认是异步同步) |
持久化 | 可通过第三方应用实现,如sina研发的memcachedb,将cache的数据保存到[url=]Berkerly DB[/url] | 支持。持久化到本地硬盘,生成一个.data和.index文件。cache初始化时会自动查找这两个文件,将数据放入cache |
效率 | 高 | 高于Memcache |
容灾 | 可通过客户端实现。 | 支持 |
缓存数据方式 | 缓存在memcached server向系统申请的内存中 | 可以缓存在内存(JVM中),也可以缓存在硬盘。通过CacheManager管理cache。多个CacheManager可配置在一个JVM内,CacheManager可管理多个cache。 |
缓存过期移除策略 | LRU | [url=]LRU([/url]默认),FIFO,LFU |
缺点 | 功能不完善,相对于Ehcache效率低 | 只适用于java体系,只能用java编写客户端 |
优点 | 简洁,灵活,所有支持socket的语言都能编写其客户端 | 效率高。功能强大。 |
有关Ehcache更详细的信息 http://ehcache.org/documentation或http://192.168.2.159/?p=132 。
根据缓存和应用的耦合程度将其划分为Local Cache和Remote Cache。
Local Cache是指包含在应用之中的缓存组件,如Ehcache, Oscache.
Remote Cache指和应用解耦,在应用之外的缓存组件,如Memcached
Local Cache最大的优点是应用和Cache在同一进程内部,请求缓存非常快速,完全不需要网络开销,所以单应用,不需要集群,或者集群时Cache node不需要互相通知的情况下使用比较合适。
缺点:多个应用程序无法直接共享缓存,应用集群的情况下这个问题就更加明显(好像是废话= =)
本地缓存:从最简单的Map到Ehcache单机版都属于一类。
分布式缓存:分布在不同JVM的Cache可以互相同步与备份,如JBossCache和Oracle那个天价的产品。
集中式缓存:最著名的代表是Memcache,Terracotta其实也属于透明的集中式架构。
Memcached是一种集中式Cache,支持分布式横向扩展。
1.节点均衡的网状(JBoss Tree Cache),利用JGroup的多广播通信机制来同步数据。
2.Maste-Slaves模式(分布式文件系统),由Master来管理slave,如何选择slave,如何迁移数据,都是由Master来完成,但是Master本身也存在单点问题。
小对象的缓存(用户的token,权限信息,资源信息),小的静态资源缓存,SQL结果的缓存。
应对高并发访问的应用,本地缓存采用EHCache,外部共享缓存采用Memcached,两者集成结合使用,参考示例见相关链接及svn checkout ehmem 示例。
Memcached详细探讨
Memcached介绍及分析
Memcached 是一种集中式 Cache,支持分布式横向扩展。通过在内存里维护一个统一的巨大的hash表,Memcached能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。
Memcached服务端本身是单实例的,只是在客户端实现过程中可以根据存储的主键作分区存储,而这个区就是Memcached服务端的一个或者多个实例,如果将客户端也囊括到Memcached中,那么可以部分概念上说是集中式的。其实回顾一下集中式的构架,无非两种情况:1.节点均衡的网状(JBoss Tree Cache),利用JGroup的多播通信机制来同步数据。2.Master-Slaves模式(分布式文件系统),由Master来管理Slave,如何选择Slave,如何迁移数据,都是由Master来完成,但是Master本身也存在单点问题。
Memcached内存分配机制:首先要说明的是Memcached支持最大的存储对象为1M。key的长度小于250字符; 像vmware, xen这类虚拟化技术并不适合运行memcached; Memcached未提供任何安全策略,仅仅通过telnet就可以访问到memcached, Memcached Java客户端代码的人就会了解其实客户端的事情很简单,就是要有一套高性能的 Socket通信框架以及对 Memcached 的私有协议实现的接口。
在基于分布式缓存的应用中,要确保每个缓存中的数据完全的一致是不可能的,总会存在这样那样的问题,即使像memcached,也因为没有commit机制,可能出现一个node上先放入cache,而最后transaction回滚,但其他的cache node已经为其他用户提供了这个数据。
memcached的一些特点:
1、请求由client端进行处理,client端维护着一个memcached服务器列表,根据用户的请求将响应指向不同的memcached服务器;(也就是说,每个缓冲值,在所有服务器中只保持着一份copy,不像ehcache每个服务器中都有)
2、memcached对CPU的要求不高,但对内存要求较高,因此可以与webapp server安装在一起,互补(web app server是CPU要求高,内存要求低)
3、可以动态地,甚至无限地增加memcached服务器的数量
4、
首先memcached是独立的服务器组件,独立于应用系统,从客户端保存和读取对象到memcached是必须通过网络传输,因为网络传输都是二进制的数据,所以所有的对象都必须经过序列化,否则无法存储到memcahced的服务器端。
memcached采用 slab 内存处理机制
slab源于Jeff Bonwick 为 SunOS 操作系统首次引入的一种内存处理机制,SLAB的设计理念是基于对象缓冲的,基本想法是避免重复大量的初始化和清理操作。SLAB主要可以用于频繁分配释放的内存对象。如果是采用系统自带的 malloc/free话,反复地操作会造成大量内存碎片,操作系统将会花费大量的时间去查找连续的内存块来满足malloc 的请求。
Memcached为了提高内存的分配和回收效率,采用了slab和dump分区的概念。Memcached一大优势就是能够充分利用Memory资源,将同机器或者不同机器的Memcached服务端组合成为对客户端看似统一的存储空间,Memcached可以在一台机器上开多个端口作为服务端多个实例,也可以在多台机器上开多个服务实例,而slab就是Memcached的服务端。
以下是对cache的配置示例:
<?xml version="1.0" encoding="UTF-8"?>
<memcached>
<client name="mclient0" compressEnable="true" defaultEncoding="UTF-8" socketpool="pool0">
<!--errorHandler></errorHandler-->
</client>
<client name="mclient1" compressEnable="true" defaultEncoding="UTF-8" socketpool="pool1">
<!--errorHandler></errorHandler-->
</client>
<client name="mclient11" compressEnable="true" defaultEncoding="UTF-8" socketpool="pool11">
<!--errorHandler></errorHandler-->
</client>
<socketpool name="pool0" failover="true" initConn="10" minConn="5" maxConn="250" maintSleep="0"
nagle="false" socketTO="3000" aliveCheck="true">
<servers>192.168.2.159:13000,192.168.2.159:13001,192.168.2.159:13002</servers>
</socketpool>
<socketpool name="pool1" failover="true" initConn="10" minConn="5" maxConn="250" maintSleep="0"
nagle="false" socketTO="3000" aliveCheck="true">
<servers>192.168.2.159:13000</servers>
</socketpool>
<socketpool name="pool11" failover="true" initConn="10" minConn="5" maxConn="250" maintSleep="0"
nagle="false" socketTO="3000" aliveCheck="true">
<servers>192.168.2.159:13000</servers>
</socketpool>
<cluster name="cluster1">
<memCachedClients>mclient1,mclient11</memCachedClients>
</cluster>
</memcached>
可以看到其实pool才是最终连接服务端的配置,看看pool0,它会连接10.2.225.210:13000,10.2.225.210:13001,10.2.225.210:13002这些机器和他们的端口,但是对于使用pool0的mclient0来说它仅仅只是知道有一个叫做mclient0的cache可以保存数据。此时slab就有三个:10.2.225.210:13000和10.2.225.210:13001和10.2.225.210:13002。
当一个key:value要被放入到Memcached中,首先Memcached会根据key的hash算法获取到hash值来选择被分配的slab,然后根据value选择适合的dump区。所谓dump区其实就是根据value的大小来将内存按照存储单元内容大小分页。这个是可以配置Memcached的,例如Memcached将slab中的内存划分成4个dump,第一dump区存储0-50k大小的数据,第二dump区存储50-100k的数据,第三dump区存储100-500k的数据,第四dump区存储500-1000K的数据。那么当key:value需要被写入的时候,很容易定位到value所处的dump,分配内存给value。这种分dump模式简化内存管理,加速了内存回收和分配。但是这里需要注意的几点就是,首先当你的应用场景中保存的数据大小离散度很高,那么就不是很适合Memcached的这种分配模式,容易造成浪费,例如第一dump区已经满了,第二第三dump区都还是只有一个数据,那么第二第三dump区不会被回收,第二第三dump区的空间就浪费了。同时Memcached对于value的大小支持到1M,大于1M的内容不适合Memcached存储。
memcached 中内存分配机制主要理念
1. 先为分配相应的大块内存,再在上面进行无缝小对象填充
2. 懒惰检测机制,Memcached 不花过多的时间在检测各个item对象是否超时,当 get获取数据时,才检查item对象是否应该删除,你不访问,我就不处理。
3. 懒惰删除机制,在 memecached 中删除一个 item对象的时候,并不是从内存中释放,而是单单的进行标记处理,再将其指针放入 slot回收插糟,下次分配的时候直接使用。
memcached的删除机制
memcached内部不会监视记录是否过期,而是在get时查看记录的时间戳,检查记录是否过期, 这种技术被称为lazy(惰性)expiration.因此,memcached不会在过期监视上耗费CPU时间;memcached会优先使用已超时的记录的空间,并使用LRU算法来分配空间,因此当memcached的内存空间不足,就从最近违背使用的记录中搜索,并将空间分配给新的记录;不过在某些情况下LRU机制会造成某些麻烦,如你并不想要淘汰已被缓存过的记录,可以在memcached启动时添加 -M 参数来禁止LRU,但这样在memcached的内存用尽时,memcached会返回错误,是否使用LRU。
memcached的基本设置
启动Memcache的服务器端
# /usr/local/bin/memcached -d -m 10 -u root -l 192.168.0.200 -p 12000 -c 256 -P /tmp/memcached.pid
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 显示帮助
memcache安装及配置
包安装
Debian / Ubuntu
apt-get install memcached
RedHat / Fedora
yum install memcached
源代码安装
tar –zxvf memcached-1.x.x.tar.gz
cd memcached-1.x.x
./configure
make && make test
sudo make install
备注:
1) 如果安装过程中出现依赖包(比如libevent)找不到的情况,请安装相应的依赖包。
2) 如果出现找不到so的情况,通过ln –s /xxx/xxx.so /usr/lib/xxx.so做一个软链接解决。
服务器端配置
使用以下命令进行启动:
memcached –m 64 –l 10.11.11.33 –p 11211 –u xiajinxin
备注:
1)可以通过命令memcache –h来查看所有命令参数
2)可以通过追加参数-vv来进行调试输出
3)可以在同一台机器上启动多个memcached实例(端口不同)
具体可以参考:http://code.google.com/p/memcached/wiki/NewConfiguringServer
集群配置
由于Memcached服务器与服务器之间没有任何通讯,并且不进行任何数据复制备份,所以当任何服务器节点出现故障时,会出现单点故障,如果需要实现HA,则需要通过另外的方式来解决。
通过Magent缓存代理,防止单点现象,缓存代理也可以做备份,通过客户端连接到缓存代理服务器,缓存代理服务器连接缓存连接服务器,缓存代理服务器可以连接多台Memcached机器可以将每台Memcached机器进行数据同步。如果其中一台缓存服务器down机,系统依然可以继续工作,如果其中一台Memcached机器down掉,数据不会丢失并且可以保证数据的完整性。具体可以参考:http://code.google.com/p/memagent/
memcache集群的实现
memcached尽管是"分布式"缓存服务器,但服务器端并没有分布式功能。各个memcached不会互相通信以共享信息。那么,怎样进行分布式呢?这完全取决于客户端的实现。
memcached的分布式
Memcached作为集中式Cache,就存在着集中式的致命问题:单点问题,Memcached支持多Instance分布在多台机器上,仅仅只是解决了数据全部丢失的问题,但是当其中一台机器出错以后,还是会导致部分数据的丢失,一个篮子掉在地上还是会把部分的鸡蛋打破。
因此就需要实现一个备份机制,能够保证Memcached在部分失效以后,数据还能够依然使用,当然大家很多时候都用Cache不命中就去数据源获取的策略,但是在SIP的场景中,如果部分信息找不到就去数据库查找,那么要把SIP弄垮真的是很容易,因此SIP对于Memcached中的数据认为是可信的,因此做Cluster也是必要的。
1.应用传入需要操作的key,通过CacheManager获取配置在Cluster中的客户端。
2.当获得Cache Client以后,执行Cache操作。
3. A.如果是读取操作,当不能命中时去集群其他Cache客户端获取数据,如果获取到数据,尝试写入到本次获得的Cache客户端,并返回结果。(达到数据恢复的作用)
B.如果是更新操作,在本次获取得Cache客户端执行更新操作以后,立即返回,将更新集群其他机器命令提交给客户端的异步更新线程对列去异步执行。(由于如果是根据key来获取Cache,那么异步执行不会影响到此主键的查询操作)
存在的问题:如果是设置了Timeout的数据,那么在丢失以后被复制的过程中就会变成永久有效的内容。
自定义cache接口实现与缓存框架解耦
由于缓存系统有很多,如memcached,OSCache,Ehcache,JbossCache等,其中 OSCache,Ehcache,JbossCache是用java实现的开源的缓存框架,一个大型项目功能模块多,通常都集成了多个缓存系统。每套缓存框架都有各自的优缺点,随着项目的不断发展,更换缓存系统也是很有可能的,所以跟缓存系统的解耦是非常有必要的。
跟缓存进行解耦只需两个工作:
1. 自定义缓存接口
2. 实现各个缓存框架的adapter(适配器模式)。
下面给出一个示意图:
引用缓存策略
使用spring AOP 抽出一个专门缓存切面,切面主要做两件事:
一、拦截类中定义的切入点;
二、进入切入点后,先从相应缓存中取key对应的value,如果取到值,就不执行切入点对应的方法;如果取不到值,则继续执行原来方法。
key:包名类名方法名参数
value:该方法的返回值
windowns下使用
1.安装与启动
2.终端使用memcached
登录到缓存服务器,然后在cmd命令行中键入
查看 Memcached 内部工作状态
访问访问 Memcached :telnet 主机名 端口号
查看总状态: 查看总状态: stats stats
查看某项状态: 查看某项状态: stats curr_connections
选项 说明
add 仅当存储空间中不存在键相同的数据时才保存
replace 仅当存储空间中存在键相同的数据时才保存
set 与 add 和 replace 不同,无论何时都保存
相关链接:
memcached与ehcache使用示例项目:
https://github.com/yangjiandong/pm.git