48

Ehcache、Memcache、Redis三大缓存比较

 5 years ago
source link: http://stor.51cto.com/art/201904/595394.htm?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

最近项目组有用到这三个缓存,去各自的官方看了下,觉得还真的各有千秋!今天特意归纳下各个缓存的优缺点,仅供参考!

Ehcache

在Java项目广泛的使用。它是一个开源的、设计于提高在数据从RDBMS中取出来的高花费、高延迟采取的一种缓存方案。正因为Ehcache具有健壮性(基于java开发)、被认证(具有apache 2.0 license)、充满特色(稍后会详细介绍),所以被用于大型复杂分布式web application的各个节点中。

BJzmaqI.jpg!web

什么特色?

1. 够快

Ehcache的发行有一段时长了,经过几年的努力和不计其数的性能测试,Ehcache终被设计于large, high concurrency systems.

2. 够简单

开发者提供的接口非常简单明了,从Ehcache的搭建到运用运行仅仅需要的是你宝贵的几分钟。其实很多开发者都不知道自己用在用Ehcache,Ehcache被广泛的运用于其他的开源项目

比如:hibernate

3.够袖珍

关于这点的特性,官方给了一个很可爱的名字small foot print ,一般Ehcache的发布版本不会到2M,V 2.2.3 才 668KB。

4. 够轻量

核心程序仅仅依赖slf4j这一个包,没有之一!

5.好扩展

Ehcache提供了对大数据的内存和硬盘的存储,最近版本允许多实例、保存对象高灵活性、提供LRU、LFU、FIFO淘汰算法,基础属性支持热配置、支持的插件多

6.监听器

缓存管理器监听器 (CacheManagerListener)和 缓存监听器(CacheEvenListener),做一些统计或数据一致性广播挺好用的

如何使用?

够简单就是Ehcache的一大特色,自然用起来just so easy!

贴一段基本使用代码

CacheManager manager = CacheManager.newInstance("src/config/ehcache.xml");

Ehcache cache = new Cache("testCache", 5000, false, false, 5, 2);

cacheManager.addCache(cache);

  • name:缓存名称。
  • maxElementsInMemory:缓存最大个数。
  • eternal:对象是否永久有效,一但设置了,timeout将不起作用。
  • timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
  • timeToLiveSeconds:设置对象在失效前允许存活时间,最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时 间无穷大。
  • overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
  • diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
  • maxElementsOnDisk:硬盘最大缓存个数。
  • diskPersistent: 是否缓存虚拟机重启期数 据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
  • diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
  • memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU。你可以设置为 FIFO或是LFU。
  • clearOnFlush:内存数量最大时是否清除。

memcache

memcache 是一种高性能、分布式对象缓存系统,最初设计于缓解动态网站数据库加载数据的延迟性,你可以把它想象成一个大的内存HashTable,就是一个key-value键值缓存。Danga Interactive为了LiveJournal所发展的,以BSD license释放的一套开放源代码软件。

1.依赖

memcache C语言所编写,依赖于最近版本的GCC和libevent。GCC是它的编译器,同事基于libevent做socket io。在安装memcache时保证你的系统同事具备有这两个环境。

2.多线程支持

memcache支持多个cpu同时工作,在memcache安装文件下有个叫threads.txt中特别说明,By default, memcached is compiled as a single-threaded application.默认是单线程编译安装,如果你需要多线程则需要修改./configure --enable-threads,为了支持多核系统,前提是你的系统必须具有多线程工作模式。开启多线程工作的线程数默认是4,如果线程数超过cpu数 容易发生操作死锁的概率。结合自己业务模式选择才能做到物尽其用。

3.高性能

通过libevent完成socket 的通讯,理论上性能的瓶颈落在网卡上。

简单安装:

1.分别把memcached和libevent下载回来,放到 /tmp 目录下:

# cd /tmp  
# wget http://www.danga.com/memcached/dist/memcached-1.2.0.tar.gz  
# wget http://www.monkey.org/~provos/libevent-1.2.tar.gz 

iueyMnU.jpg!web

2.先安装libevent:

# tar zxvf libevent-1.2.tar.gz 
# cd libevent-1.2 
# ./configure -prefix=/usr  
# make (如果遇到提示gcc 没有安装则先安装gcc)  
# make install 

3.测试libevent是否安装成功:

# ls -al /usr/lib | grep libevent 
lrwxrwxrwx 1 root root 21 11?? 12 17:38 libevent-1.2.so.1 -> libevent-1.2.so.1.0.3 
-rwxr-xr-x 1 root root 263546 11?? 12 17:38 libevent-1.2.so.1.0.3 
-rw-r-r- 1 root root 454156 11?? 12 17:38 libevent.a 
-rwxr-xr-x 1 root root 811 11?? 12 17:38 libevent.la 
lrwxrwxrwx 1 root root 21 11?? 12 17:38 libevent.so -> libevent-1.2.so.1.0.3 

还不错,都安装上了。

4.安装memcached,同时需要安装中指定libevent的安装位置:

# cd /tmp  
# tar zxvf memcached-1.2.0.tar.gz  
# cd memcached-1.2.0  
# ./configure -with-libevent=/usr  
# make  
# make install 

如果中间出现报错,请仔细检查错误信息,按照错误信息来配置或者增加相应的库或者路径。

安装完成后会把memcached放到 /usr/local/bin/memcached ,

5.测试是否成功安装memcached:

# ls -al /usr/local/bin/mem*  
-rwxr-xr-x 1 root root 137986 11?? 12 17:39 /usr/local/bin/memcached  
-rwxr-xr-x 1 root root 140179 11?? 12 17:39 /usr/local/bin/memcached-debug 

启动memcache服务

启动Memcached服务:

1.启动Memcache的服务器端:

# /usr/local/bin/memcached -d -m 8096 -u root -l 192.168.77.105 -p 12000 -c 256 -P /tmp/memcached.pid 
  • -d选项是启动一个守护进程,
  • -m是分配给Memcache使用的内存数量,单位是MB,我这里是8096MB,
  • -u是运行Memcache的用户,我这里是root,
  • -l是监听的服务器IP地址,如果有多个地址的话,我这里指定了服务器的IP地址192.168.77.105,
  • -p是设置Memcache监听的端口,我这里设置了12000,最好是1024以上的端口,
  • -c选项是最大运行的并发连接数,默认是1024,我这里设置了256,按照你服务器的负载量来设定,
  • -P是设置保存Memcache的pid文件,我这里是保存在 /tmp/memcached.pid,

2.如果要结束Memcache进程,执行:

# cat /tmp/memcached.pid 或者 ps -aux | grep memcache (找到对应的进程id号)

# kill 进程id号

也可以启动多个守护进程,不过端口不能重复。

memcache 的连接

telnet ip port

注意连接之前需要再memcache服务端把memcache的防火墙规则加上

-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT

重新加载防火墙规则

service iptables restart

OK ,现在应该就可以连上memcache了

在客户端输入stats 查看memcache的状态信息

ArYjim7.jpg!web
  • pid memcache服务器的进程ID
  • uptime 服务器已经运行的秒数
  • time 服务器当前的unix时间戳
  • version memcache版本
  • pointer_size 当前操作系统的指针大小(32位系统一般是32bit)
  • rusage_user 进程的累计用户时间
  • rusage_system 进程的累计系统时间
  • curr_items 服务器当前存储的items数量
  • total_items 从服务器启动以后存储的items总数量
  • bytes 当前服务器存储items占用的字节数
  • curr_connections 当前打开着的连接数
  • total_connections 从服务器启动以后曾经打开过的连接数
  • connection_structures 服务器分配的连接构造数
  • cmd_get get命令 (获取)总请求次数
  • cmd_set set命令 (保存)总请求次数
  • get_hits 总命中次数
  • get_misses 总未命中次数
  • evictions 为获取空闲内存而删除的items数(分配给memcache的空间用满后需要删除旧的items来得到空间分配给新的items)
  • bytes_read 读取字节数(请求字节数)
  • bytes_written 总发送字节数(结果字节数)
  • limit_maxbytes 分配给memcache的内存大小(字节)
  • threads 当前线程数

redis

Redis是 在memcache之后编写的,大家经常把这两者做比较,如果说它是个key-value store 的话但是它具有丰富的数据类型,我想暂时把它叫做缓存数据流中心,就像现在物流中心那样,order、package、store、 classification、distribute、end。现在还很流行的LAMP PHP架构 不知道和 redis+MySQL 或者 redis + MongoDB的性能比较(听群里的人说mongodb分片不稳定)。

先说说reidis的特性

1. 支持持久化

redis的本地持久化支持两种方式:RDB和AOF。RDB 在redis.conf配置文件里配置持久化触发器,AOF指的是redis没增加一条记录都会保存到持久化文件中(保存的是这条记录的生成命令),如果 不是用redis做DB用的话还会不要开AOF ,数据太庞大了,重启恢复的时候是一个巨大的工程!

2.丰富的数据类型

redis 支持 String 、Lists、sets、sorted sets、hashes 多种数据类型,新浪微博会使用redis做nosql主要也是它具有这些类型,时间排序、职能排序、我的微博、发给我的这些功能List 和 sorted set

的强大操作功能息息相关

3.高性能

这点跟memcache很想象,内存操作的级别是毫秒级的比硬盘操作秒级操作自然高效不少,较少了磁头寻道、数据读取、页面交换这些高开销的操作!这也是NOSQL冒出来的原因吧,应该是高性能

是基于RDBMS的衍生产品,虽然RDBMS也具有缓存结构,但是始终在app层面不是我们想要的那么操控的。

4.replication

redis提供主从复制方案,跟mysql一样增量复制而且复制的实现都很相似,这个复制跟AOF有点类似复制的是新增记录命令,主库新增记录将新增脚本 发送给从库,从库根据脚本生成记录,这个过程非常快,就看网络了,一般主从都是在同一个局域网,所以可以说redis的主从近似及时同步,同事它还支持一 主多从,动态添加从库,从库数量没有限制。 主从库搭建,我觉得还是采用网状模式,如果使用链式(master-slave-slave-slave-slave·····)如果第一个slave出 现宕机重启,首先从master 接收 数据恢复脚本,这个是阻塞的,如果主库数据几TB的情况恢复过程得花上一段时间,在这个过程中其他的slave就无法和主库同步了。

5.更新快

这点好像从我接触到redis到目前为止 已经发了大版本就4个,小版本没算过。redis作者是个非常积极的人,无论是邮件提问还是论坛发帖,他都能及时耐心的为你解答,维护度很高。有人维护的 话,让我们用的也省心和放心。目前作者对redis 的主导开发方向是redis的集群方向。

redis的安装

redis的安装其实还是挺简单的,总的来说就三步:下载tar包,解压tar包,安装。

不过最近我在2.6.7后用centos 5.5 32bit 时碰到一个安装问题,下面我就用图片分享下安装过程碰到的问题,在redis 文件夹内执行make时有个如下的错 undefined reference to '__sync_add_and_fetch_4'

aYnmueq.jpg!web

上网找了了好多最后在 https://github.com/antirez/redis/issues/736 找到解决方案,write CFLAGS= -march=i686 on src/Makefile head!

InQnQ3v.jpg!web

记得要把刚安装失败的文件删除,重新解压新的安装文件,修改Makefile文件,再make安装。就不会发现原来那个错误了

关于redis的一些属性注释和基本类型操作在上一篇redis 的开胃菜有详细的说明,这里就不再重复累赘了(实质是想偷懒 ,哈哈!)

最后,把memcache和redis放在一起不得不会让人想到两者的比较,谁快谁好用啊,群里面已经为这个事打架很久了,我就把我看到的在这里跟大家分享下。

在别人发了一个memcache性能比redis好很多后,redis 作者 antirez 发表了一篇博文,主要是说到如何给redis 和 memcache 做压力测试,文中讲到有个人说许多开源软件都应该丢进厕所,因为他们的压力测试脚本太2了,作者对这个说明了一番。redis vs memcache is definitely an apple to apple comparison。 呵呵,很明确吧,两者的比较是不是有点鸡蛋挑骨头的效果,作者在相同的运行环境做了三次测试取多好的值,得到的结果如下图:

YjYfYbV.jpg!web

需要申明的是此次测试在单核心处理的过程的数据,memcache是支持多核心多线程操作的(默认没开)所以在默认情况下上图具有参考意义,若然则 memcache快于redis。那为什么redis不支持多线程多核心处理呢?作者也发表了一下自己的看法,首先是多线程不变于bug的修复,其实是不 易软件的扩展,还有数据一致性问题因为redis所有的操作都是原子操作,作者用到一个词nightmare 噩梦,呵呵! 当然不支持多线程操作,肯定也有他的弊端的比如性能想必必然差,作者从2.2版本后专注redis cluster的方向开发来缓解其性能上的弊端,说白了就是纵向不行,横向提高。

应用场景:

ehcache直接在jvm虚拟机中缓存,速度快,效率高;但是缓存共享麻烦,集群分布式应用不方便。

redis是通过socket访问到缓存服务,效率比ecache低,比数据库要快很多,处理集群和分布式缓存方便,有成熟的方案。

如果是单个应用或者对缓存访问要求很高的应用,用ehcache。

如果是大型系统,存在缓存共享、分布式部署、缓存内容很大的,建议用redis。

补充下:ehcache也有缓存共享方案,不过是通过RMI或者Jgroup多播方式进行广播缓存通知更新,缓存共享复杂,维护不方便;简单的共享可以,但是涉及到缓存恢复,大数据缓存,则不合适redis和memcached相比的独特之处:

1、redis可以用来做存储(storage),而memcached是用来做缓存(cache)这个特点主要因为其有持久化功能

2、redis中存储的数据有多种结构,而memcached存储的数据只有一种类型“字符串”

第二种理解:

第一:两者之间的介绍

Redis:属于独立的运行程序,需要单独安装后,使用JAVA中的Jedis来操纵。因为它是独立,所以如果你写个单元测试程序,放一些数据在Redis中,然后又写一个程序去拿数据,那么是可以拿到这个数据的。,

ehcache:与Redis明显不同,它与java程序是绑在一起的,java程序活着,它就活着。譬如,写一个独立程序放数据,再写一个独立程序拿数据,那么是拿不到数据的。只能在独立程序中才能拿到数据。

第二:使用及各种配置:

两者都可以集群:

1.Redis可以做主从来集群,例如,在A电脑上装个Redis,作为主库;在其他电脑上装Redis,作为从库;这样主库拥有读和写的功能,而从库只拥有读的功能。每次主库的数据都会同步到从库中。

1.默认方式启动

Linux下使用Redis  
安装:从官网上下载tar.gz格式的包,然后使用tar zxvf redis-2.8.24.tar.gz命令解压,然后进入Redis文件夹目录下的src目录,使用make编译一下  
    
1.开启:进入/usr/local/redis-3.2.1/src  
然后./redis-server  

2.如果我们想修改端口,设置密码:那么得修改配置文件的redis.conf

port 6379 //端口修改

requirepass redis123 //设置密码 redis123为密码

配置主从:主库的配置文件不用修改,从库的配置文件需要修改,因为从库需要绑定主库,以便可以获取主库的数据

slaveof 192.168.1.100 6379 //主库的IP地址和端口号

masterauth redis123 //主库设定的密码

3.要让配置文件的属性生效,那么启动的redis的时候,要将配置文件加上去

进入/usr/local/redis-3.2.1/src

然后 ./redis-server redis.conf

那么将成功的启动redis,如果没有加入配置的话,按照普通方式启动的话,端口仍然还是6379.

4.客户端连接远程的Redis

第一步:在远程端处设置密码:config set requirepass 123 //123为密码

第二步:可以在客户端登录 redis-cli.exe -h 114.215.125.42 -p 6379

第三步:认证:auth 123 //123为密码

本地端设置密码后,要使用密码登录;如果Redis重启的话,密码需要重新设置

5.主从配置后,为保证主库写的能力,一般不在主库做持久化,而是在从库做持久化:

主库配置:

将save注释,不使用rdb

# save 900 1

# save 300 10

# save 60 10000

appendonly no 不使用aof

从库配置:

save 900 1

save 300 10

save 60 10000

appendonly yes

这样做的优缺点:

优点:保证了主库写的能力。

缺点:主库挂掉后,重启主库,然后进行第一次写的动作后,主库会先生成rdb文件,然后传输给从库,从而覆盖掉从库原先的rdb文件,造成数据丢失。但是第二次写的时候,主库会以快照方式直接传数据给从库,不会重新生成rdb文件。

解决方案:先复制从库中的数据到主库后,再启动主库。

使用:

引入jedis包

<dependency>   
        <groupId>redis.clients</groupId>   
        <artifactId>jedis</artifactId>   
        <version>2.7.3</version>   
    </dependency>   

简单的写个类玩玩吧

public class RedisMain {   
           
           
        public static void main(String [] str)   
        {   
               
             Jedis jedis = new Jedis("114.215.125.42",6379);   
             jedis.auth("123");     //密码认证   
              System.out.println("Connection to server sucessfully");   
              //查看服务是否运行   
              jedis.set("user","namess");   
             // System.out.println("Server is running: "+jedis.ping());   
              System.out.println(jedis.get("user").toString());   
              jedis.set("user","name");   
              System.out.println(jedis.get("user"));   
                 
       
        }   
       
    }   

Redis完毕

下面说说Ehcache:

Ehcache的使用:

1.首先引入包

<dependency>   
               <groupId>net.sf.ehcache</groupId>   
               <artifactId>ehcache-core</artifactId>   
               <version>2.6.6</version>   
           </dependency>   
        
           <dependency>   
               <groupId>org.slf4j</groupId>   
               <artifactId>slf4j-log4j12</artifactId>   
               <version>1.6.6</version>   
           </dependency>   

2.创建一个ehcache.xml文件,里面配置cache的信息,这个配置是包含了集群的配置:与192.168.93.129:40001的 机器集群了:Ip为192.168.93.129机子的配置要将rmiUrls对应的数据改为这个配置文件的机子的IP地址,和对应的缓存名字

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
       xsi:noNamespaceSchemaLocation="ehcache.xsd">   
        <cacheManagerPeerProviderFactory    
       class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"   
    properties="peerDiscovery=manual,rmiUrls=//192.168.93.129:40001/demoCache"/>           <!--另一台机子的ip缓存信息-->   
    <cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"   
    properties="hostName=localhost,port=40001,socketTimeoutMillis=2000" />                  <!--hostName代表本机子的ip-->      
      <diskStore path="java.io.tmpdir"/>   
      <defaultCache   
        maxElementsInMemory="10000"   
        maxElementsOnDisk="0"   
        eternal="true"   
        overflowToDisk="true"   
        diskPersistent="false"   
        timeToIdleSeconds="0"   
        timeToLiveSeconds="0"   
        diskSpoolBufferSizeMB="50"   
        diskExpiryThreadIntervalSeconds="120"   
        memoryStoreEvictionPolicy="LFU"   
        >   
         <cacheEventListenerFactory     
                    class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>                 
        </defaultCache>   
      <cache name="demoCache"   
        maxElementsInMemory="100"   
        maxElementsOnDisk="0"   
        eternal="false"   
        overflowToDisk="false"   
        diskPersistent="false"   
        timeToIdleSeconds="119"   
        timeToLiveSeconds="119"   
        diskSpoolBufferSizeMB="50"   
        diskExpiryThreadIntervalSeconds="120"   
        memoryStoreEvictionPolicy="FIFO"   
        >   
        <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>          <!--监听这个cache-->   
        </cache>   
    </ehcache>   

配置完后写代码:

放数据:

@RequestMapping("/testehcache.do")   
        public void testehcache(HttpServletResponse response) throws IOException   
        {   
            URL url = getClass().getResource("ehcache.xml");    
             CacheManager singletonmanager = CacheManager.create(url);   
            Cache cache = singletonmanager.getCache("demoCache");   
            //使用缓存   
            Element element = new Element("key1", "value1");   
            cache.put(element);   
            cache.put(new Element("key2", "value2"));   
              
            response.getWriter().println("我存放了数据");   
        }   

拿数据:

@RequestMapping("/getcache.do")   
    public void getcache(HttpServletResponse response) throws IOException   
    {   
        CacheManager singletonmanager = CacheManager.create();    
        Cache cache = singletonmanager.getCache("demoCache");   
        String one=cache.get("key1").getObjectValue().toString();   
        String two=cache.get("key2").getObjectValue().toString();   
        response.getWriter().println(one+two);   
    }  

配置集群后,A机器放数据,在B机器上能拿到数据,B机器放数据,A机器也可以拿到数据


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK