GuavaCache Memcache Ehcache的简单介绍及使用

/ Tec / 1 条评论 / 250浏览
转载请标明出处:
原文首发于:http://www.zhangruibin.com
本文出自RebornChang的博客

说数据存储,抛开数据库持久化存储手段oracle,MySQL等,再抛开非持久化数据库redis等,再刨除磁盘存储,本章简单说下catch里面的主流:GuavaCache Memcache Ehcache。

GuavaCache Memcache Ehcache简介

GuavaCache

1.封装了get、put操作,能够集成数据源。一般我们在业务中操作缓存都会操作缓存和数据源两部分。例如:put数据时,先插入DB再删除原来的缓存,get数据时,先查缓存,命中则返回,没有命中时需要查询DB,再把查询结果放入缓存中。Guava封装了这么多步骤,只需要调用一次get/put方法即可。 2. 是线程安全的缓存,与ConcurrentMap类似,但前者增加了更多的元素失效策略,后者只能显示的移除元素。 3. GuavaCache支持四种回收方式: (1)基于容量回收(Size-based Eviction);(2)基于时间回收(Timed Eviction);(3)基于引用类型的回收Reference-based Eviction);(4)手动回收。 4. 它可以监控加载/命中情况。

Memcache

memcache是一个高性能的分布式的内存对象缓存系统,用于动态Web应用以减轻数据库负担。它通过在内存中缓存数据和对象,来减少读取数据库的次数。从而提高动态、数据库驱动网站速度。

  memcache通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。memcache主要用于分担数据库负的压力,memcache将数据调用到内存中,然后从内存中读取,从而大大提高读取速度。

 EHCache

EHCache是来自sourceforge(http://ehcache.sourceforge.net/) 的开源项目,也是纯Java实现的简单、快速的Cache组件。EHCache支持内存和磁盘的缓存,支持LRU、LFU和FIFO多种淘汰算法,支持分 布式的Cache,可以作为Hibernate的缓存插件。同时它也能提供基于Filter的Cache,该Filter可以缓存响应的内容并采用 Gzip压缩提高响应速度。

缓存优劣的比较点

过期时间

容量规划(重要)

清除策略(重要)

命中率统计

性能比较- Memcache Ehcache

项目 Memcache Ehcache
分布式 不完全,集群默认不实现 支持
集群 可通过客户端实现 支持(默认是异步同步)
持久化 可通过第三方应用实现 支持。持久化到本地硬盘,生成一个.data和.index文件。cache初始化时会自动查找这两个文件,将数据放入cache
效率 高于Memcache
容灾 可通过客户端实现。 支持
缓存数据方式 缓存在memcached server向系统申请的内存中 可以缓存在内存(JVM中),也可以缓存在硬盘。通过CacheManager管理cache。
缓存过期移除策略 LRU LRU(默认),FIFO,LFU
缺点 功能不完善,相对于Ehcache效率低 只适用于java体系,只能用java编写客户端
优点 简洁,灵活,跨平台 效率高,功能强大。

名词解释

缓存移除策略(三种常见):
     FIFO(First In First Out):先进先出算法,即先放入缓存的先被移除;

     LRU(Least Recently Used):最久未使用算法,使用时间距离现在最久的那个被移除;

     LFU(Least Frequently Used):最近最少使用算法,一定时间段内使用次数(频率)最少的那个被移除。
  
Expirations有三种方式:
	1.永不失效:Expirations.noExpiration();
	
    2. TTL(Time To Live):存活期,即从缓存中创建时间点开始直到它到期的一个时间段(不管在这个时间段内有没有访问都将过期);
    例子
    :Expirations.timeToLiveExpiration(Duration.of(10,TimeUnit.MINUTES));
    
    3. TTI(Time To Idle):空闲期,即一个数据多久没被访问将从缓存中移除的时间。
    
    例子:
    Expirations.timeToIdleExpiration(Duration.of(5, TimeUnit.MINUTES));
    
    另外,Ehcache 中还可以通过自行实现Expiry接口来自定义缓存失效策略。

GuavaCache使用

1.maven添加依赖:

		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>23.0</version>
		</dependency>

2.代码层 GuavaCacheTest .java

package com.example.demo.gitBooks.catchAnalysis.guavaCacheAnalysis;

import com.example.entity.User;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/*
* @ClassName GuavaCacheTest
*@Description TODO
*@Author zhangrui
*@Date 14:04 14:04
*@Version 
* */
public class GuavaCacheTest {
    private static  Logger logger = LoggerFactory.getLogger(GuavaCacheTest.class);
    public static void main(String[] args) {
        Cache<Object,Object> cache = CacheBuilder
                .newBuilder()
                //设置并发级别
                .concurrencyLevel(10)
                //设置缓存有效时间,超时的话会被回收
                .expireAfterAccess(10, TimeUnit.MINUTES)
                //设置缓存条数
                .maximumSize(100)
                .build();
        final Integer userId = 10001;
        try {
            //
            User user = (User)cache.get(userId, new Callable<User>() {
                @Override
                public User call() throws Exception {
                    return getUserInfoByUserId(userId);
                }
            });
            System.out.println(user.getName() == null?"没有獲取到了用戶":"獲取到了用戶:"+user);
            logger.info("执行上述成功!!");
        } catch (ExecutionException e) {
            e.printStackTrace();
            logger.info("抛出了异常!!");
        }
    }
    public static User getUserInfoByUserId(Integer userAge) {
        User user1 = new User("张三",  10001);
        User user2 = new User("李四", 10002);
        User user3 = new User("王五", 10003);
        List<User> userList = new ArrayList<>();
        userList.add(user1);
        userList.add(user2);
        userList.add(user3);
        User user = new User();
        for (User userTemp : userList){
            if (userTemp.getAge() == userAge){
                user = userTemp;
            }
        }
        return user;
    }
}

Ehcache 的使用

1.maven添加依赖

		<!--ehcache缓存-->
		<dependency>
			<groupId>org.ehcache</groupId>
			<artifactId>ehcache</artifactId>
			<version>3.3.1</version>
		</dependency>

代码层

EhcacheTest.java

import com.example.entity.User;
import org.ehcache.Cache;
import org.ehcache.PersistentCacheManager;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.CacheManagerConfiguration;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.MemoryUnit;
import org.ehcache.expiry.Duration;
import org.ehcache.expiry.Expirations;

import java.io.File;
import java.util.concurrent.TimeUnit;

/*
* @ClassName EhcacheTest
*@Description TODO 同时初始化堆缓存,堆外缓存,磁盘缓存,
* TODO 其中:heap + offheap 的组合,offheap 就是 Authoritative Tier,heap,就是 Caching Tier;
*TODO 对于heap + offheap + disk 的组合,disk 层就是 Authoritative Tier,heap + offheap 就是 CachingTier。
*@Author zhangrui
*@Date 10:47 10:47
*@Version 
* */
public class EhcacheTest {
    public static void main(String[] args) {
        //声明持久化(persistence)的磁盘位置为g盘
        //TODO 在Linux下需要修改
        CacheManagerConfiguration<PersistentCacheManager> persistentManagerConfig = CacheManagerBuilder
                .persistence(new File("g:\\", "ehcache-test"));
        PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
                .with(persistentManagerConfig).build();
        // 手动初始化
        persistentCacheManager.init();
        // 空间大小 heap < offheap < disk,否则会报错java.lang.IllegalArgumentException
        //第三个参数设置为true,支持持久化
        ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder()
                .heap(10, MemoryUnit.MB)
                .offheap(100, MemoryUnit.MB)
                .disk(500, MemoryUnit.MB, true);
        //写入缓存
        CacheConfiguration<Long, User> config = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, User.class, resource)
                //设置Caching Tier缓存有效时间,因为存入缓存是直接存到比 AuthoritativeTier中,所以,Caching Tier失效也不会影响数据
                .withExpiry(Expirations.timeToLiveExpiration(Duration.of(10, TimeUnit.SECONDS)))
                .build();
        Cache<Long, User> cache = persistentCacheManager.createCache("userInfoCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(config));
        //写入缓存
        cache.put(10001L, new User("张三",  1));
        cache.put(10002L, new User("李四",  2));
        cache.put(10003L, new User("王五",  3));
        //读取缓存,如果要读取的是未存放放的,不会抛出异常,返回为null
        User user1 = new User();
        user1 = cache.get(10001L);
        User user2 = new User();
        user2 = cache.get(10002L);
        User user3 = new User();
        user3 = cache.get(10003L);
        //输出缓存
        System.out.println(user1 == null ? "获取失败,以为没有成功放入缓存,其实是堆溢出清除!!" : "成功的获取了缓存:" + "**************" + user1 + "***********");
        System.out.println("成功的获取了缓存:" + "**************" + user2 + "***********");
        System.out.println(user3 == null ? "获取失败,以为没有成功放入缓存,可能的原因为声明的heap值过小!!" : "成功的获取了缓存:" + "**************" + user3 + "***********");
        // 再程序关闭前,需要手动释放资源
        persistentCacheManager.close();
    }
}

CacheManagerTest.java

import com.example.entity.User;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.EntryUnit;
import org.ehcache.config.units.MemoryUnit;

/*
* @ClassName CacheManagerTest
*@Description TODO 声明的缓存heap大小是一定的,当后续加入缓存区满了之后,再继续加入的话则会遵从栈存储原则,堆底元素出堆消亡
*@Author zhangrui
*@Date 10:47 10:47
*@Version 
* */
public class CacheManagerTest {
    public static void main(String[] args) {
        CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build();
        //手动初始化
        cacheManager.init();
        ResourcePoolsBuilder resourcePoolsBuilder = ResourcePoolsBuilder.heap(2);
        //1.按照存储的条目数进行初始化
        ResourcePoolsBuilder resourcePoolsBuilder1 = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(2, EntryUnit.ENTRIES);
        //2.按照存储的条目数进行初始化的简写方式
        ResourcePoolsBuilder resourcePoolsBuilder2 = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(2);
        //3.按照存储空间大小初始化
        ResourcePoolsBuilder resourcePoolsBuilder3 = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(2, MemoryUnit.GB);
        CacheConfiguration<Integer, User> configuration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, User.class,
                ResourcePoolsBuilder.newResourcePoolsBuilder()
                        .heap(10, MemoryUnit.KB)
                        .offheap(10, MemoryUnit.MB))
                .withSizeOfMaxObjectGraph(3)
                .withSizeOfMaxObjectSize(4, MemoryUnit.KB)
                .build();
        //创建缓存对象
        Cache<Integer, User> cache = cacheManager.createCache("userInfo", CacheConfigurationBuilder.newCacheConfigurationBuilder(configuration));
        //写入缓存
        cache.put(10001, new User("张三",  1));
        cache.put(10002, new User("李四",  2));
        cache.put(10003, new User("王五",3));
        //读取缓存
        User user1 = new User();
        user1 = cache.get(10001);
        User user2 = new User();
        user2 = cache.get(10002);
        User user3 = new User();
        user3 = cache.get(10003);
        //输出缓存
        System.out.println(user1 == null ? "获取失败,以为没有成功放入缓存,其实是堆溢出清除!!" : "成功的获取了缓存:" + "**************" + user1 + "***********");
        System.out.println("成功的获取了缓存:" + "**************" + user2 + "***********");
        System.out.println(user3 == null ? "获取失败,以为没有成功放入缓存,可能的原因为声明的heap值过小!!" : "成功的获取了缓存:" + "**************" + user3 + "***********");
        //再程序关闭前,需要手动释放资源
        //不用手动removeCache,根据打印日志,会自动清除;
        cacheManager.removeCache("userInfo");
        cacheManager.close();
    }
}

PS: 1.memcache.需要配置服务端安装什么的,博主嫌繁琐,就没弄,所以这里没代码展示。 2.本博文代码都已经上传到了博主的GitHub上,代码地址: RebornChang's GitHub 3.本博文部分资源摘自网络,若有雷同,请见谅。 Over!

  1. 大佬,这篇写的挺全面的,点赞,mark。

    回复