精华内容
下载资源
问答
  • Java中用Ehcache做缓存处理

    千次阅读 2017-07-29 17:56:22
    Java中用Ehcache做缓存处理

    Java中用Ehcache做缓存处理

    具体创建项目就不多说了。本例是的idea的maven项目中做的测试。

    1 添加依赖

    在pom.xml添加如下的依赖项
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>2.6.6</version>
        </dependency>

    2 添加配置文件ehcache.xml

    ehcache.xml保存在src/main/resources/;内空如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache>
        <!--timeToIdleSeconds 当缓存闲置n秒后销毁 -->
        <!--timeToLiveSeconds 当缓存存活n秒后销毁 -->
        <!--
        缓存配置
        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:内存数量最大时是否清除。
        -->
        <cache name="ehcache001"
               maxEntriesLocalHeap="10000"
               maxEntriesLocalDisk="1000"
               eternal="false"
               overflowToDisk="true"
               diskSpoolBufferSizeMB="20"
               timeToIdleSeconds="300"
               timeToLiveSeconds="600"
               memoryStoreEvictionPolicy="LFU"
               transactionalMode="off" />
        </ehcache>

    3 添加缓存处理类,代码如下:

    import net.sf.ehcache.Cache;
    import net.sf.ehcache.CacheManager;
    import net.sf.ehcache.Element;
    
    import java.net.URL;
    
    /**
     * Created by xxx on 2017/7/29.
     */
    public class EhCacheUtil {
        //ehcache.xml 保存在src/main/resources/
        private static final String path = "/ehcache.xml";
    
        private URL url;
    
        private CacheManager manager;
    
        private static EhCacheUtil ehCache;
    
        private EhCacheUtil(String path) {
            url = getClass().getResource(path);
            manager = CacheManager.create(url);
        }
    
        public static EhCacheUtil getInstance() {
            if (ehCache == null) {
                ehCache = new EhCacheUtil(path);
            }
            return ehCache;
        }
    
        public void put(String cacheName, String key, Object value) {
            Cache cache = manager.getCache(cacheName);
            Element element = new Element(key, value);
            cache.put(element);
        }
    
        public Object get(String cacheName, String key) {
            Cache cache = manager.getCache(cacheName);
            Element element = cache.get(key);
            return element == null ? null : element.getObjectValue();
        }
    
        public Cache get(String cacheName) {
            return manager.getCache(cacheName);
        }
    
        public void remove(String cacheName, String key) {
            Cache cache = manager.getCache(cacheName);
            cache.remove(key);
        }
    }
    

    4 添加测试类代码如下:

    import com.lfb.model.User;
    
    /**
     * Created by xxxon 2017/7/29.
     */
    public class EhcacheTest {
    
        public  static void TestCahce()
        {
            //string测试
            EhCacheUtil.getInstance().put("ehcache001","userEch","test001");
            String val = (String) EhCacheUtil.getInstance().get("ehcache001", "userEch");
            
            //object测试
            User user = new User();
            user.setUserId(1);
            user.setUserName("jack");
            EhCacheUtil.getInstance().put("ehcache001","userJack",user);
    
            User user2=(User) EhCacheUtil.getInstance().get("ehcache001", "userJack");
            String str="1";
            System.out.print(user2);
        }
    }
    

    在入口方法main添加入口
    EhcacheTest.TestCahce();

    至此代码就ok了,运行测试,发现存取都ok

    --- end ---


    展开全文
  • 定时重置缓存,使用公司内部缓存工具可以做到设置超时时间如果缓存为空则调用接口且重新为缓存赋值,但缓存失效时总会存在需要直接访问接口而导致访问太慢,所以我想是不是可以用static对数据内存缓存,思路:用...
  • java web缓存技术

    千次阅读 2017-02-04 11:17:43
    缓存技术在计算机中有关的技术随处可见,不管在硬件还是软件都有着举重轻重的位置,由此可以看到缓存技术的重要性。 —缓存算法— 当然,提到缓存不得不说缓存算法。 最不经常使用算法(LFU):这个缓存算法使用...

    —背景—

    缓存技术在计算机中有关的技术随处可见,不管在硬件还是软件都有着举重轻重的位置,由此可以看到缓存技术的重要性。


    —缓存算法—

    当然,提到缓存不得不说缓存算法。

    最不经常使用算法(LFU):这个缓存算法使用一个计数器来记录条目被访问的频率。通过使用LFU缓存算法,最低访问数的条目首先被移除。这个方法并不经常使用,因为它无法对一个拥有最初高访问率之后长时间没有被访问的条目缓存负责。
    最近最少使用算法(LRU):这个缓存算法将最近使用的条目存放到靠近缓存顶部的位置。当一个新条目被访问时,LRU将它放置到缓存的顶部。当缓存达到极限时,较早之前访问的条目将从缓存底部开始被移除。这里会使用到昂贵的算法,而且它需要记录“年龄位”来精确显示条目是何时被访问的。此外,当一个LRU缓存算法删除某个条目后,“年龄位”将随其他条目发生改变。
    自适应缓存替换算法(ARC):在IBM Almaden研究中心开发,这个缓存算法同时跟踪记录LFU和LRU,以及驱逐缓存条目,来获得可用缓存的最佳使用。
    最近最常使用算法(MRU):这个缓存算法最先移除最近最常使用的条目。一个MRU算法擅长处理一个条目越久,越容易被访问的情况。

    —Java web缓存—

    在Java中最简单和最常见的应该就是static静态的Map缓存方式。


    —缓存技术设计思想—

    典型的空间换时间。


    —缓存—

            缓存就是将静态资源、文件、数据库等会频繁被使用到的数据或者资源存储在系统外。一般我们操作数据库、硬盘文件等资源,我们的做法是每次都从数据库或者硬盘上去获取,这样就会速度很慢,造成性能问题。而解决这一个简单的方法就是:把这些数据缓存到内存里面,每次操作的时候,先到内存里面找,看有没有这些数据,如果有,那么就直接使用,如果没有那么就获取它,并设置到缓存中,下一次访问的时候就可以直接从内存中获取了。


    —实现基本步骤—

    (1)先到缓存里面查找所要查找的想用数据,看看是否存在需要使用的数据
    (2)如果没有找到,那么就创建一个满足要求的数据,然后把这个数据设置回到缓存中,以备下次使用
    (3)如果找到了相应的数据,或者是创建了相应的数据,那就直接使用这个数据。


    —缓存存在的问题—

    缓存数据存放时间问题
    缓存数据和真实数据的同步问题
    缓存的多线程并发的控制


    展开全文
  • Java缓存机制

    万次阅读 多人点赞 2018-08-21 02:53:25
    1 Java缓存 1.1 jvm内置缓存 Java中实现缓存的方式有很多,比如用static hashMap基于内存缓存的jvm内置缓存,简单不实用,保对象的有效性和周期无法控制,容易造成内存急剧上升。常用的有Oscache(主要针对jsp页面...

    1 Java缓存

    1.1 jvm内置缓存

    Java中实现缓存的方式有很多,比如用static hashMap基于内存缓存的jvm内置缓存,简单不实用,保对象的有效性和周期无法控制,容易造成内存急剧上升。常用的有Oscache(主要针对jsp页面),Ehcache(主要针对数据库访问层),Jcache,Jbosscache等等很多

    缺点:容易内存溢出、没有持久化(服务重启后丢失)、线程安全、多个服务器(多个jvm)之间的数据不能共享。

    1.2 java操作eache

    利用spring Boot搭建应用(可参考这里的缓存配置)。

    访问http://localhost:8080/getUser?name=springboot2.2后查询后返回

    {
    id: 35,
    name: "springboot2.2",
    age: 99
    }

    修改数据库中age字段后再次访问http://localhost:8080/getUser?name=springboot2.2后结果不变?代表使用了缓存,成功配置了缓存。

    访问http://localhost:8080/removeCache清除缓存后,再次访问http://localhost:8080/getUser?name=springboot2.2后结果就为修改后的值。

    原理?

    1、当客户端请求数据时,如果服务器端配置了缓存,第一步去缓存里面查找,如果有跳4,没有则往2

    2、发送jdbc请求操作数据库查询数据

    3、将查询到的数据返回给缓存,并保存在缓存中

    4、将从(缓存|数据库)查询到的数据返回给客户端。

    好处?效率高(因为不用建立jdbc连接等)、降低了数据库的压力(不用每次请求都要去数据库查,数据库也会累啊)。

    缺点?从上述也看到了,有可能产生数据不一致的情况,清除缓存可解决。

    和oscache区别? ehcache 主要是对数据库访问的缓存,相同的查询语句只需查询一次数据库,从而提高了查询的速度,使用spring的AOP可以很容易实现这一功能。 oscache 主要是对页面的缓存,可以整页或者指定网页某一部分缓存,同时指定他的过期时间,这样在此时间段里面访问的数据都是一样的。

    2 Redis

    关系型数据库:持久、主外键、编写SQL语句、存放在硬盘。

    非关系型数据库:一般用于缓存、值存放在内存(所以效率是真的高)、key-vakye形式、容易数据丢失(不过很好解决)、有点小类似jvm内置缓存(不过这个更牛嗨,因为可以多个服务器间共享数据)。

    redis?可以持久化mongdb?存储json格式

    2.1 Redis概述

    完全开源免费、最受BSD协议、高性能的key-value费关系型数据库支持持久化、支持key-value(String)\list\set\zert\hash等数据结构的存储、支持备份

    好处?减轻数据库访问的压力。效率高?(访问内存肯定比访问硬盘快,这是常识)

    应用场景?(token生成、session共享、分布式锁、验证码、自增id(订单id))

    2.2 安装redis

    2.2.1 windows安装redis

    1、下载redis

    2、解压redis-latest-windws.zip文件,将start.bat文件拷贝纸redis目录

    3、编辑redis.windows.conf文件,取消requirepass的注释(前面不能有空格),空格然后添加密码比如(123456)保存。以requirepass 123456为例

    4、双击start.bat运行

    5、通过这个客户端工具测试一下

    2.2.2 linux安装redis

    1、下载redis

    2、mkdir -p /usr/local/redis/bin,mkdir -p /usr/local/redis/etc

    3、copy redis-3.0.0.tar.gz到用户目录比如/root

    4、解压tar -zxvf redis-3.0.0.tar.gz

    5、cd redis-3.0.0/后,make一下

    6、进入src目录make install后就安装成功了。

    7、cd cd /root/redis-3.0.0/(redis安装目录)

    8、cp redis.conf /usr/local/redis/etc

    9、cd src

    10、cp mkreleasehdr.sh redis-benchmark redis-check-aof redis-check-dump redis-cli redis-server redis-sentinel /usr/local/redis/bin
    11、修改 redis.conf文件

    daemonize yes --- 修改为yes  后台启动

    requirepass 123456  ----注释取消掉设置账号密码

    ps aux | grep '6379'  --- 查询端口

    kill -15 9886 --- 杀死重置

    kill -9 9886 --- 强制杀死

    service iptables stop 停止防火墙

    12、cd /usr/local/redis/bin

    ./redis-server /usr/local/redis/etc/redis.conf启动服务

    13、./redis-cli -h 127.0.0.1 -p 6379 -a "123456"  --- redis 使用账号密码连接或者windows下的客户端工具进行连接测试

    PING 结果表示成功

    14、停止redis

    redis-cli shutdown  或者 kill redis进程的pid

    15、放开一下端口,好像外部即使注释掉bind 127.0.0.1也不能访问/sbin/iptables -I INPUT -p tcp --dport 6379 -j ACCEPT

    2.3 redis基本数据类型

    2.3.1 字符串

    127.0.0.1:6379> set name raolei
    OK
    127.0.0.1:6379> set itboy www.itboy.com
    OK
    127.0.0.1:6379> get name
    "raolei"
    127.0.0.1:6379> get itboy
    "www.itboy.com"
    

    常用命令:

    编号

    命令

    描述说明

    1

    SET key value

    此命令设置指定键的值。

    2

    GET key

    获取指定键的值。

    3

    GETRANGE key start end

    获取存储在键上的字符串的子字符串。

    4

    GETSET key value

    设置键的字符串值并返回其旧值。

    5

    GETBIT key offset

    返回在键处存储的字符串值中偏移处的位值。

    6

    MGET key1 [key2..]

    获取所有给定键的值

    7

    SETBIT key offset value

    存储在键上的字符串值中设置或清除偏移处的位

    8

    SETEX key seconds value

    使用键和到期时间来设置值

    9

    SETNX key value

    设置键的值,仅当键不存在时

    10

    SETRANGE key offset value

    在指定偏移处开始的键处覆盖字符串的一部分

    11

    STRLEN key

    获取存储在键中的值的长度

    12

    MSET key value [key value …]

    为多个键分别设置它们的值

    13

    MSETNX key value [key value …]

    为多个键分别设置它们的值,仅当键不存在时

    14

    PSETEX key milliseconds value

    设置键的值和到期时间(以毫秒为单位)

    15

    INCR key

    将键的整数值增加1

    16

    INCRBY key increment

    将键的整数值按给定的数值增加

    17

    INCRBYFLOAT key increment

    将键的浮点值按给定的数值增加

    18

    DECR key

    将键的整数值减1

    19

    DECRBY key decrement

    按给定数值减少键的整数值

    20

    APPEND key value

    将指定值附加到键

    2.3.2 list

    127.0.0.1:6379> lpush listkey redis
    (integer) 1
    127.0.0.1:6379> lpush listkey mysql
    (integer) 2
    127.0.0.1:6379> lpush listkey mongdb
    (integer) 3
    127.0.0.1:6379> lpush listkey hbase
    (integer) 4
    127.0.0.1:6379> lrange listkey 0 10
    1) "hbase"
    2) "mongdb"
    3) "mysql"
    4) "redis"
    

    list是简单的字符串列表,按照插入顺序排序,可在头和尾插入,最多包含2^32-1个元素(40多亿)。

    常用命令:

    序号

    命令及描述

    1

    BLPOP key1 [key2 ] timeout 
    移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

    2

    BRPOP key1 [key2 ] timeout 
    移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

    3

    BRPOPLPUSH source destination timeout 
    从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

    4

    LINDEX key index 
    通过索引获取列表中的元素

    5

    LINSERT key BEFORE|AFTER pivot value 
    在列表的元素前或者后插入元素

    6

    LLEN key 
    获取列表长度

    7

    LPOP key 
    移出并获取列表的第一个元素

    8

    LPUSH key value1 [value2] 
    将一个或多个值插入到列表头部

    9

    LPUSHX key value 
    将一个值插入到已存在的列表头部

    10

    LRANGE key start stop 
    获取列表指定范围内的元素

    11

    LREM key count value 
    移除列表元素

    12

    LSET key index value 
    通过索引设置列表元素的值

    13

    LTRIM key start stop 
    对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。

    14

    RPOP key 
    移除并获取列表最后一个元素

    15

    RPOPLPUSH source destination 
    移除列表的最后一个元素,并将该元素添加到另一个列表并返回

    16

    RPUSH key value1 [value2] 
    在列表中添加一个或多个值

    17

    RPUSHX key value 
    为已存在的列表添加值

    2.3.3 set

    Setstring类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。

    哈希表实现。添加,删除,查找的复杂度都是O(1)。每个集合可存储40多亿个成员

    127.0.0.1:6379> sadd setkey redis
    (integer) 1
    127.0.0.1:6379> sadd setkey redis
    (integer) 0
    127.0.0.1:6379> sadd setkey redis
    (integer) 0
    127.0.0.1:6379> sadd setkey redis
    (integer) 0
    127.0.0.1:6379> sadd setkey redis
    (integer) 0
    127.0.0.1:6379> sadd setkey mysql
    (integer) 1
    127.0.0.1:6379> sadd setkey mongdb
    (integer) 1
    127.0.0.1:6379> SMEMBERS setkey
    1) "mysql"
    2) "mongdb"
    3) "redis"
    

    常用命令:

    序号

    命令及描述

    1

    SADD key member1 [member2] 
    向集合添加一个或多个成员

    2

    SCARD key 
    获取集合的成员数

    3

    SDIFF key1 [key2] 
    返回给定所有集合的差集

    4

    SDIFFSTORE destination key1 [key2] 
    返回给定所有集合的差集并存储在 destination

    5

    SINTER key1 [key2] 
    返回给定所有集合的交集

    6

    SINTERSTORE destination key1 [key2] 
    返回给定所有集合的交集并存储在 destination

    7

    SISMEMBER key member 
    判断 member 元素是否是集合 key 的成员

    8

    SMEMBERS key 
    返回集合中的所有成员

    9

    SMOVE source destination member 
    member 元素从 source 集合移动到 destination 集合

    10

    SPOP key 
    移除并返回集合中的一个随机元素

    11

    SRANDMEMBER key [count] 
    返回集合中一个或多个随机数

    12

    SREM key member1 [member2] 
    移除集合中一个或多个成员

    13

    SUNION key1 [key2] 
    返回所有给定集合的并集

    14

    SUNIONSTORE destination key1 [key2] 
    所有给定集合的并集存储在 destination 集合中

    15

    SSCAN key cursor [MATCH pattern] [COUNT count] 
    迭代集合中的元素

    2.3.4 sorted set

    与set一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。

    集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1) 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)

    127.0.0.1:6379> zadd zsetkey 1 redis
    (integer) 1
    127.0.0.1:6379> zadd zsetkey 2 mysql
    (integer) 1
    127.0.0.1:6379> zadd zsetkey 2 mongdb
    (integer) 1
    127.0.0.1:6379> smembers setkey
    1) "mysql"
    2) "mongdb"
    3) "redis"
    

    常用命令:

    序号

    命令及描述

    1

    ZADD key score1 member1 [score2 member2] 
    向有序集合添加一个或多个成员,或者更新已存在成员的分数

    2

    ZCARD key 
    获取有序集合的成员数

    3

    ZCOUNT key min max 
    计算在有序集合中指定区间分数的成员数

    4

    ZINCRBY key increment member 
    有序集合中对指定成员的分数加上增量 increment

    5

    ZINTERSTORE destination numkeys key [key ...] 
    计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key

    6

    ZLEXCOUNT key min max 
    在有序集合中计算指定字典区间内成员数量

    7

    ZRANGE key start stop [WITHSCORES] 
    通过索引区间返回有序集合成指定区间内的成员

    8

    ZRANGEBYLEX key min max [LIMIT offset count] 
    通过字典区间返回有序集合的成员

    9

    ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 
    通过分数返回有序集合指定区间内的成员

    10

    ZRANK key member 
    返回有序集合中指定成员的索引

    11

    ZREM key member [member ...] 
    移除有序集合中的一个或多个成员

    12

    ZREMRANGEBYLEX key min max 
    移除有序集合中给定的字典区间的所有成员

    13

    ZREMRANGEBYRANK key start stop 
    移除有序集合中给定的排名区间的所有成员

    14

    ZREMRANGEBYSCORE key min max 
    移除有序集合中给定的分数区间的所有成员

    15

    ZREVRANGE key start stop [WITHSCORES] 
    返回有序集中指定区间内的成员,通过索引,分数从高到底

    16

    ZREVRANGEBYSCORE key max min [WITHSCORES] 
    返回有序集中指定分数区间内的成员,分数从高到低排序

    17

    ZREVRANK key member 
    返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序

    18

    ZSCORE key member 
    返回有序集中,成员的分数值

    19

    ZUNIONSTORE destination numkeys key [key ...] 
    计算给定的一个或多个有序集的并集,并存储在新的 key

    20

    ZSCAN key cursor [MATCH pattern] [COUNT count] 
    迭代有序集合中的元素(包括元素成员和元素分值)

    2.3.5 hash

    是一个string类型的fieldvalue的映射表(和map差不多,只是兼职都是字符串),hash特别适合用于存储对象。Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)。

    127.0.0.1:6379> hmset hmset name "redis tutorial"
    OK
    127.0.0.1:6379> hmset hmset age 24
    OK
    127.0.0.1:6379> hgetall hmset
    1) "name"
    2) "redis tutorial"
    3) "age"
    4) "24"
    

    序号

    命令及描述

    1

    HDEL key field2 [field2] 
    删除一个或多个哈希表字段

    2

    HEXISTS key field 
    查看哈希表 key 中,指定的字段是否存在。

    3

    HGET key field 
    获取存储在哈希表中指定字段的值。

    4

    HGETALL key 
    获取在哈希表中指定 key 的所有字段和值

    5

    HINCRBY key field increment 
    为哈希表 key 中的指定字段的整数值加上增量 increment

    6

    HINCRBYFLOAT key field increment 
    为哈希表 key 中的指定字段的浮点数值加上增量 increment

    7

    HKEYS key 
    获取所有哈希表中的字段

    8

    HLEN key 
    获取哈希表中字段的数量

    9

    HMGET key field1 [field2] 
    获取所有给定字段的值

    10

    HMSET key field1 value1 [field2 value2 ] 
    同时将多个 field-value (-)对设置到哈希表 key 中。

    11

    HSET key field value 
    将哈希表 key 中的字段 field 的值设为 value

    12

    HSETNX key field value 
    只有在字段 field 不存在时,设置哈希表字段的值。

    13

    HVALS key 
    获取哈希表中所有值

    14

    HSCAN key cursor [MATCH pattern] [COUNT count] 
    迭代哈希表中的键值对。

    redis怎么存放对象?通过将对象序列化为json字符串,存放为字符串形式,之后读取在反序列化为对象,这样很快很快

    2.4 Spring Boot集成redis

    添加依赖:

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--spring2.0集成redis所需common-pool2-->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
                <version>2.4.2</version>
            </dependency>

    配置文件:

    ########################################################
    ###Redis (RedisConfiguration)
    ########################################################
    spring:
      redis:
        database: 0
        host: 192.168.245.134
        port: 6379
        password: 123456
        jedis:
          pool:
            max-idle: 8
            min-idle: 0
            max-active: 8
            max-wait: -1ms
        timeout: 5000ms
    

    service:

    @Service
    public class RedisService {
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        //字符串
        public void setStringKey(String key,String value,Long time){
            setObject(key,value,time);
        }
        public void setStringKey(String key,String value){
            setObject(key,value,null);
        }
    
        //set
        public void setSetKey(String key,Set value){
            setObject(key,value,null);
        }
        //list
        public void setListKey(String key,List value){
            setObject(key,value,null);
        }
    
        public String getStringKey(String key){
            return (String) getObject(key,new String());
        }
        public Set getSetKey(String key){
            return (Set) getObject(key,new HashSet<String>());
        }
        public List getListKey(String key){
            return (List) getObject(key,new ArrayList<String>());
        }
    
    
    
    
        public void setObject(String key,Object value,Long time){
            if(StringUtils.isEmpty(key)||value==null){
                return;
            }
            //字符串类型
            if(value instanceof String){
                String value1= (String) value;
                if(time!=null){
                    stringRedisTemplate.opsForValue().set(key,value1,time,TimeUnit.SECONDS);
                } else{
                    stringRedisTemplate.opsForValue().set(key,value1);
                }
                return;
            }
            //list类型
            else if(value instanceof List){
                List<String> list= (List<String>) value;
                for (String s:list) {
                    stringRedisTemplate.opsForList().leftPush(key,s);
                }
                return;
             }
             //set
             else if(value instanceof Set){
                Set<String> strings= (Set<String>) value;
                for (String s : strings) {
                    stringRedisTemplate.opsForSet().add(key,s);
                }
                return;
            }
            /**
             * .....
             */
        }
    
        public Object getObject(String key,Object object){
            if(StringUtils.isEmpty(key)||object==null){
                return null;
            }
            else if (object instanceof String){
                return stringRedisTemplate.opsForValue().get(key);
            }
            else if(object instanceof List){
                return stringRedisTemplate.opsForList().range(key,0,stringRedisTemplate.opsForList().size(key));
            }
            else if(object instanceof Set){
                return stringRedisTemplate.opsForSet().members(key);
            }
            return null;
        }
    }

    controller:

    @RestController
    public class IndexController {
        @Autowired
        private RedisService redisService;
    
        @RequestMapping("/setString")
        public String setString(@PathParam("key") String key,
                                @PathParam("value") String value){
            redisService.setStringKey(key,value);
            return redisService.getStringKey(key);
        }
        @RequestMapping("/setSet")
        public Set<String> setSet(@PathParam("key") String key,
                          @PathParam("value") String value){
            HashSet<String> strings = new HashSet<>();
            strings.add(value);
            strings.add(value+"1");
            strings.add(value+"2");
            strings.add(value+"3");
            redisService.setSetKey(key,strings);
            return redisService.getSetKey(key);
        }
        @RequestMapping("/setList")
        public List<String> setList(@PathParam("key") String key,
                            @PathParam("value") String value){
            ArrayList<String> strings = new ArrayList<>();
            strings.add(value);
            strings.add(value+"1");
            strings.add(value+"2");
            strings.add(value+"3");
            redisService.setListKey(key,strings);
            return redisService.getListKey(key);
        }
    }

    访问http://localhost:8080/setSet?key=itboySet&value=123456http://localhost:8080/setString?key=itboySet&value=123456,

    http://localhost:8080/setList?key=itboySet&value=123456进行测试一下是否成功!!!!!!!!

    2.5 主从复制和哨兵机制理解

    为什么?数据备份、读写分离、集群、高可用(宕机容错机制)。

    一般情况下,Redis高可用都是一主多从,而不像其他比如Nginx多主多从。

    所谓主从复制,主要是为了减轻单台服务器的压力(比如图中的master),通过多台服务器的冗余来保证高可用(单台宕机容错),实现读写分离、数据备份、集群等。

    如图,其中master可读可写,但是当有客户端连接达到集群时,如果是读操作就从slave从节点中随机选择一台服务器进行响应,如果是写操作,那么操作主服务器。这就是读写分离了不是吗。。。

    问题?主服务器写之后,怎么同步到从服务器?(主从复制搞定,往下看)

    问题?主服务器宕机了,怎么写?通过哨兵机制,哨兵其实就是一个监听器,一直监听这主服务器,如果主服务器挂了,他就会使用投票(随机)从主服务器中选择一台服务器作为主服务器,此乃高可用。

    问题?那如果整个集群挂了呢?有一个东西叫做keepalived监听器(其实就是一个shell写的重启服务的命令脚本),如果监听到某一台服务器挂了,他就会自动重启的(一般30秒内,听说可能不准确)。如果一直启动失败?那就没办法了,他只能发送一封邮件给运维人员了。

    2.5.1 主从复制实现

    原理:通过快照文件(类似mysql的二进制可执行文件),当master有更新时,从服务器slave会实时请求得到该快照文件,进行执行,如果网络问题?别担心过,会重试的。

    过程:

    1:当一个从数据库启动时,会向主数据库发送sync命令,

    2:主数据库接收到sync命令后会开始在后台保存快照(执行rdb操作),并将保存期间接收到的命令缓存起来

    3:当快照完成后,redis会将快照文件和所有缓存的命令发送给从数据库。

    4:从数据库收到后,会载入快照文件并执行收到的缓存的命令。

    对于redis服务器来说,和mysql有很大不同,只要设置好主从服务器之后,主服务器master可读可写,从服务器slave仅可读,不像mysql那样需要分配用户和mycat插件来控制读写分离。

    配置过程:

    1、准备服务器,如上图中三台服务器(192.168.245.134,192.168.245.135,192.168.245.136),选择一台为主服务器master(这台服务器什么也不用做)

    2、所以redis主从复制只需要配置从服务器slave就OK,修改redis.conf配置文件,放开以下两行的注释,添加如下内容。两台从服务器都要修改。

    slaveof 192.168.245.134 6379
    #主服务器的ip和端口号
    
    # If the master is password protected (using the "requirepass" configuration
    # directive below) it is possible to tell the slave to authenticate before
    # starting the replication synchronization process, otherwise the master will
    # refuse the slave request.
    #
    masterauth 123456
    #主服务器的认证密码
    

    3、测试

    #进入master主服务器
    192.168.245.134:6379> info
    #回车后看到如下内容即代表成功
    # Replication
    role:master
    connected_slaves:2 #两台从服务器
    slave0:ip=192.168.245.135,port=6379,state=online,offset=127,lag=1 #一些描述信息
    slave1:ip=192.168.245.136,port=6379,state=online,offset=127,lag=1 #一些描述信息
    
    
    #进入任何一台服务器比如135,同样info以下,看到如下内容即代表成功。
    # Replication
    role:slave  #角色从服务器
    master_host:192.168.245.134  #主服务器ip
    master_port:6379 #主服务端口
    master_link_status:up #状态
    master_last_io_seconds_ago:10
    master_sync_in_progress:0
    
    
    #主服务器
    192.168.245.134:6379> set master "192.168.245.134"
    OK
    192.168.245.134:6379> get master
    "192.168.245.134"
    192.168.245.134:6379> 
    ##可读可写是吧???????
    #########刚刚设置的这条数据从服务器有吗??????############
    192.168.245.135:6379> get master
    "192.168.245.134"
    192.168.245.135:6379> set slave "192.168.245.135"
    (error) READONLY You can't write against a read only slave.
    192.168.245.135:6379> 
    ############哟呵有数据的,主从复制成功,主从间数据同步问题解决,而且不能写,读写分离也搞定了########
    [root@localhost bin]# ./redis-cli -h 192.168.245.136 -p 6379 -a "123456"
    192.168.245.136:6379> ping
    PONG
    192.168.245.136:6379> get master
    "192.168.245.134"
    192.168.245.136:6379> set slave "192.168.245.136"
    (error) READONLY You can't write against a read only slave.
    192.168.245.136:6379> 
    
    ################另一台从服务器也一样######################
    
    

    2.5.2 哨兵机制实现

    原理:哨兵用于管理多个redis服务器,执行以下三个任务:

    1、监控(monitoring):哨兵(sentinel)会不断检查你的master和slave是否运作正常

    2、提醒(notification):当被监控的某个redis出现问题时,哨兵(sentinel)可以通过API向管理员或者其他应用程序发送通知

    3、自动故障迁移(automatic failover):当一个master1不能正常工作时,哨兵会开始一次自动故障迁移操作,他将会失效master1的其中一个slave升级为新的master2,并让失效master1的其他slave的master1改为新的master2。当客户端试图连接失效的master1时,集群也会向客户端返回新的master2地址,使得集群可以使用新的master2代替失效的master1

    哨兵是一个分布式系统,可以在一个架构中运行多个哨兵进程,这些进程使用流言协议(gossipprotocols)来接收关于master是否下线的消息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移以及选择哪个slave作为新的master。

    每个哨兵会向其他哨兵(sentinel)、master、slave定时发送消息,来确认对方是否还活着,如果对方在指定的时间(可配置)内未响应,则暂时认为对方已挂(主观认为宕机,Subjective Down,sdown)

    若哨兵群中的多数sentinel都报告某一个master没响应,系统认为该master彻底死亡(客观真正的宕机,Objective Down,oDwon),通过一定vote算法,从生下的slave节点中选择提升一台为master,然后自动修改相关配置

    虽然哨兵(sentinel) 释出为一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 --sentinel 选项来启动哨兵(sentinel).

    实现:

    这里以上面三台服务器为基础,选择192.168.245.136这台服务器为哨兵(可以选择多台,此处仅一台为例)。

    注意:如果主从复制时,主服务器没有配置masterauth 123456请加上,这个坑了我很久很久,导致哨兵时一直连不上,最后查看日志才搞定,记得先将这个加到主服务器的redis.conf中,然后重启一下

    1、修改redis安装目录下的sentinel.conf

    sentinel monitor mymaster 192.168.245.134 6379 1
    # 主节点名称 主机ip 端口号 选举次数(就是说当有几台sentinel认为master挂了才是真的挂了,因为这里只有一个哨兵,所以为1)
    sentinel down-after-milliseconds mymaster 30
    #就是说多少ms后没有给老子响应,老子就觉得你挂了。,默认30s,这里设置为30ms,本地测试追求实时
    sentinel config-epoch mymaster 1
    #这个数字表示在发生主从复制的时候,比如master1向master2切换时,可以同时有多少个slave能对master2执行同步(也就是复制其实),越多越好?>如果太多了那么大家都去复制了,谁来响应客户端的请求?太少?太少的话,每个都要来一遍,怕是要到天黑哦,根据实际情况吧,这里只有三台所以
    为设为1.
    
    sentinel auth-pass mymaster 123456
    #主服务器密码
    

    2、启动哨兵:nohup ./redis-server ../etc/sentinel.conf --sentinel  2>1 1>nohup.log &

    3、如何停止ps -aux | grep 端口号,kill -9 pid即可

    4、测试?

    #我们首先查看master(134)的info                  master
    role:master
    connected_slaves:2
    slave0:ip=192.168.245.135,port=6379,state=online,offset=5349,lag=1
    slave1:ip=192.168.245.136,port=6379,state=online,offset=5349,lag=0
    
    
    #135的               slave 
    role:slave
    master_host:192.168.245.134
    master_port:6379
    master_link_status:up
    
    
    #136 的             slave 
    role:slave
    master_host:192.168.245.134
    master_port:6379
    master_link_status:up
    
    
    #我现在讲master停掉?       master
    #134
    192.168.245.134:6379> shutdown
    not connected> 
    
    
    #135 的info replication
    192.168.245.135:6379> info replication
    # Replication
    role:slave
    master_host:192.168.245.136
    master_port:6379
    master_link_status:up
    #看到主服务器变为136,
    
    #136?
    192.168.245.136:6379> info replication
    # Replication
    role:master
    connected_slaves:2
    slave0:ip=192.168.245.135,port=6379,state=online,offset=739,lag=0
    slave1:ip=192.168.245.134,port=6379,state=online,offset=739,lag=0
    
    #基本上成功了,最后试试读写分离以及在测试一些其他的,这里不再展示
    
    
    
    

    2.6 数据持久化

    数据持久化:就是将内存中的数据保存到硬盘,redis支持AOF和RDB两种存储方式

    2.6.1 RDB存储

    RDB是指在一个时间点,如果达到所配置的数据修改量,就写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。(二进制文件方式

    有两种保存方式:1、(阻塞)主进程直接拍快照(snapshot),然后阻塞客户端请求写入IO磁盘。2、(非阻塞)当要写入磁盘时,新建(fork)一个子进程,子进程将当前数据库快照写入磁盘,而主进程继续处理客户端请求。

    每次快照持久化都是将内存数据完整写入到磁盘一次,并不 是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。

    优点:使用单独子进程进行持久化,主进程不会进行任何IO操作,保证了redis的高性能

    缺点:RDB需要间隔一段时间进行持久化,而且必须达到相应修改数量,所以如果持久化之间发生故障,会造成数据丢失,常用于数据要求不严谨的时候。

    配置:

    #dbfilename:持久化数据存储在本地的文件
    dbfilename dump.rdb
    #dir:持久化数据存储在本地的路径,如果是在/redis/redis-3.0.6/src下启动的redis-cli,则数据会存储在当前src目录下
    dir ./
    
    ##snapshot触发的时机,save   
     
    ##如下距离上一次持久化已经900s了,如果有大于等于1个变更,才会snapshot 
    save 900 1
    ##对于此值的设置,需要谨慎,评估系统的变更操作密集程度  
    ##可以通过“save “””来关闭snapshot功能  
    
    save 300 10  #表示间隔达到300s时如果更改了10次以上,就snapshot,如果你在10s时已经10次了,立马持久化
    save 60 10000 #同理间隔达到60s时如果更改了10000次以上,就snapshot,如果你在10s时已经10000次了,立马持久化
    
    ##当snapshot时出现错误无法继续时,是否阻塞客户端“变更操作”,“错误”可能因为磁盘已满/磁盘故障/OS级别异常等  
    stop-writes-on-bgsave-error yes  
    ##是否启用rdb文件压缩,默认为“yes”,压缩往往意味着“额外的cpu消耗”,同时也意味这较小的文件尺寸以及较短的网络传输时间  
    rdbcompression yes  
    

    这里我将save 300 10改为save 30 10进行测试一下(首先先停掉哨兵机制,不然宕机后他就重新选一台主的了):

    改好后重启一下服务:

    进行5次更改操作
    192.168.245.136:6379> get name
    "1"
    192.168.245.136:6379> set name 1
    OK
    192.168.245.136:6379> set name 2
    OK
    192.168.245.136:6379> set name 3
    OK
    192.168.245.136:6379> set name 4
    OK
    192.168.245.136:6379> set name 5
    OK
    192.168.245.136:6379> get name
    "5"
    
    #立马kill掉redis进程,一定要kill如果主动关闭服务,他是会进行snapshot的
    [root@localhost bin]# ps -aux | grep 6379
    root       1572  0.1  0.9 140840  9640 ?        Ssl  02:02   0:00 ./redis-server *:6379
    root       1577  0.0  0.5  20160  5184 pts/0    S+   02:02   0:00 ./redis-cli -h 192.168.245.136 -p 6379 -a 123456
    root       1580  0.0  0.0 112676   984 pts/1    R+   02:03   0:00 grep --color=auto 6379
    [root@localhost bin]# kill -9 1572
    
    #重启服务
    [root@localhost bin]# ./redis-server ../etc/redis.conf 
    [root@localhost bin]# ./redis-cli -h 192.168.245.136 -p 6379 -a "123456"
    192.168.245.136:6379> get name
    "1"
    192.168.245.136:6379> 
    
    #可以看到我最后的是“5”,而这里是“1”,数据丢失了##########################
    #更改10次呢?,是成功进行持久化了的,这里不展示了,篇幅过大。
    #而且如果你将dump.rdb文件删除后,达到snapshot条件时,会自动创建一个新的文件,持久化其实就是将该文件备份,下次将那些持久化后的文件再放过来不就达到数据恢复了吗????????????

    2.6.2 AOF存储

    以日志文件方式存储,其实就是将你“操作+数据”指令格式化后追加到操作日志文件的尾部,必须append(已经写入到文件或者即将写入),才会进行数据的实际变更。日志文件保存了历史所有的操作过程;当 server 需要数据恢复时,可以直接 replay 此日志文件,即可还原所有的操作过程。内容是字符串,容易阅读和解析。

    缺点:AOF 文件比 RDB 文件大,且恢复速度慢。

    只会记录变更操作”(例如:set/del ),如果 server 中持续的大量变更操作,将会导致 AOF 文件非常的庞大,意味着 server 失效后,数据恢复的过程将会很长;事实上,一条数据经过多次变更,将会产生多条 AOF 记录,其实只要保存当前的状态,历史的操作记录是可以抛弃的;因为 AOF 持久化模式还伴生了“AOF rewrite”

    因为最多丢失最后一次写入文件的数据,所以很好修复,直接手工更改文件或者重新来一次即可。

    修改redis.conf文件:

    ##此选项为aof功能的开关,默认为“no”,可以通过“yes”来开启aof功能  
    ##只有在“yes”下,aof重写/文件同步等特性才会生效  
    appendonly yes  
    
    ##指定aof文件名称  
    appendfilename appendonly.aof  
    
    ##指定aof操作中文件同步策略,有三个合法值:always everysec no,默认为everysec每秒
    appendfsync everysec  
    ##在aof-rewrite期间,appendfsync是否暂缓文件同步,"no"表示“不暂缓”,“yes”表示“暂缓”,默认为“no”  
    no-appendfsync-on-rewrite no  
    
    ##aof文件rewrite触发的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才会触发rewrite,默认“64mb”,建议“512mb”  
    auto-aof-rewrite-min-size 64mb  
    
    ##相对于“上一次”rewrite,本次rewrite触发时aof文件应该增长的百分比。  
    ##每一次rewrite之后,redis都会记录下此时“新aof”文件的大小(例如A),那么当aof文件增长到A*(1 + p)之后  
    ##触发下一次rewrite,每一次aof记录的添加,都会检测当前aof文件的尺寸。  
    auto-aof-rewrite-percentage 100  
    
    

    重启服务后,根据你启动redis所在目录下出现appendonly.aof文件。

    执行以下操作:

    192.168.245.136:6379> set name 124
    OK
    192.168.245.136:6379> set name 145
    OK
    192.168.245.136:6379> set age 56
    OK
    192.168.245.136:6379> del age
    (integer) 1
    192.168.245.136:6379> get name
    "145"
    
    

    查看aof文件内容:

    [root@localhost bin]# cat appendonly.aof 
    *2
    $6
    SELECT
    $1
    0
    *3
    $3
    set
    $4
    name
    $3
    124
    *3
    $3
    set
    $4
    。。。
    

    查询的是不会出现在里面,而且这个文件是动态的。。看起来挺好的,就是频繁的更改造成文件过大,到时候恢复起来有点太慢了,有人说那么RDB中将时间改快点要求改小点?大哥,人家是全量复制,那岂不是时间都花去复制了,还处理什么请求?这个虽然慢,可是是追加方式啊,将就了。

    2.6.3 redis宕机后,值会失效吗?

    不会,redis默认开启RDB存储,如果是直接关闭服务,那么会自动备份,如果是kill或者断电如果没达到RDB配置的要求则不会持久化,而且这个虽然是非阻塞的,但是毕竟全量复制啊,保证了redis的性能,但是CPU可受不了啊。因此实际情况中,最好采用AOP方式,实时而且快,但是就是容易造成文件过大,恢复困难。

    2.7 redis事务

    Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:

    事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

    事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

    multi 开启事务 exec提交事务。

    序号

    命令及描述

    1

    DISCARD 
    取消事务,放弃执行事务块内的所有命令。

    2

    EXEC 
    执行所有事务块内的命令。

    3

    MULTI 
    标记一个事务块的开始。

    4

    UNWATCH 
    取消 WATCH 命令对所有 key 的监视。

    5

    WATCH key [key ...] 
    监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

    2.8 发布订阅

    Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。Redis 客户端可以订阅任意数量的频道

    下图展示了频道 channel1 以及订阅这个频道的三个客户端 —— client2 client5 client1 之间的关系:

    当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

    例子:

    1、创建订阅频道名为 redisChatSimple

    192.168.245.136:6379> subscribe redsiChatSample
    Reading messages... (press Ctrl-C to quit)
    1) "subscribe"
    2) "redsiChatSample"
    

    2、重新开个客户端,同一频道发布消息

    192.168.245.136:6379> publish redsiChatSample "gogog"
    (integer) 1
    192.168.245.136:6379> publish redsiChatSample "gogog22"
    (integer) 1
    192.168.245.136:6379> 
    

    3、客户端显示如下信息:

    192.168.245.136:6379> subscribe redsiChatSample
    Reading messages... (press Ctrl-C to quit)
    1) "subscribe"
    2) "redsiChatSample"
    3) (integer) 1
    1) "message"
    2) "redsiChatSample"
    3) "gogog"
    1) "message"
    2) "redsiChatSample"
    3) "gogog22"
    

    厉害了!!!

    序号

    命令及描述

    1

    PSUBSCRIBE pattern [pattern ...] 
    订阅一个或多个符合给定模式的频道。

    2

    PUBSUB subcommand [argument [argument ...]] 
    查看订阅与发布系统状态。

    3

    PUBLISH channel message 
    将信息发送到指定的频道。

    4

    PUNSUBSCRIBE [pattern [pattern ...]] 
    退订所有给定模式的频道。

    5

    SUBSCRIBE channel [channel ...] 
    订阅给定的一个或多个频道的信息。

    6

    UNSUBSCRIBE [channel [channel ...]] 
    指退订给定的频道

    展开全文
  • java本地缓存 google guava

    千次阅读 2020-05-29 08:36:22
    本地缓存产生背景: 本地缓存是指在我们本地的物理空间开辟出一片物理空间,专门用来存储...java的本地缓存存储在jvm的堆区的老年 代里,大小大约有几百兆。 本地缓存应用场景: 本地缓存虽然可以在本地缓存一些频繁

    本地缓存产生背景:

    本地缓存是指在我们本地的物理空间开辟出一片物理空间,专门用来存储我们需要向服务器端频繁需要的数据,

    比如前端页面需要频繁的向后台访问某些数据,这时候我们每次都去数据库查找数据再返回给前台就会有很大的

    开销。因为会涉及到磁盘I/O。但是如果我们把这些需要频繁访问的数据放在本地的物理空间,这样在访问的时候

    就可以直接访问服务器缓存的数据。缓存这些数据的区域就是本地缓存。java的本地缓存存储在jvm的堆区的老年

    代里,大小大约有几百兆。

    本地缓存应用场景:

    本地缓存虽然可以在本地缓存一些频繁申请的数据,但是毕竟是本地内存,大小还是需要一定限制的。我们一般

    开发一些小的项目可以牺牲一定的本地内存当作缓存提高访问效率。开发一些比较大的项目本地缓存的数据量和

    支持高并发就肯定不足了。这时候可能就需要分布式集群缓存,比如淘宝双十一的时候,难以估计马云开了多少

    台服务器当作本地缓存和消息队列(接受客户的请求)。

    本地缓存注意事项:

    本地缓存虽然为我们提供了很大的方便,但是我们手动写一套本地缓存的组件还是需要注意很多问题的,数据结构

    的选取,(一般用map结构),处理并发机制,本地缓存的过期,本地缓存在集群的同步问题,当内存不足的时候释放

    本地内存,缓存的移除等。但是一般的第三方插件为我们处理好了这些问题,例如google guava

    看看实现google guava的代码:

    public static void main(String[] args) throws ExecutionException, InterruptedException{
            //缓存接口这里是LoadingCache,LoadingCache在缓存项不存在时可以自动加载缓存
            LoadingCache<Integer,Student> studentCache
                    //CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例
                    = CacheBuilder.newBuilder()
                    //设置并发级别为8,并发级别是指可以同时写缓存的线程数
                    .concurrencyLevel(8)
                    //设置写缓存后8秒钟过期
                    .expireAfterWrite(8, TimeUnit.SECONDS)
                    //设置缓存容器的初始容量为10
                    .initialCapacity(10)
                    //设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
                    .maximumSize(100)
                    //设置要统计缓存的命中率
                    .recordStats()
                    //设置缓存的移除通知
                    .removalListener(new RemovalListener<Object, Object>() {
                        @Override
                        public void onRemoval(RemovalNotification<Object, Object> notification) {
                            System.out.println(notification.getKey() + " was removed, cause is " + notification.getCause());
                        }
                    })
                    //build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
                    .build(
                            new CacheLoader<Integer, Student>() {
                                @Override
                                public Student load(Integer key) throws Exception {
                                    System.out.println("load student " + key);
                                    Student student = new Student();
                                    student.setId(key);
                                    student.setName("name " + key);
                                    return student;
                                }
                            }
                    );
     
            for (int i=0;i<20;i++) {
                //从缓存中得到数据,由于我们没有设置过缓存,所以需要通过CacheLoader加载缓存数据
                Student student = studentCache.get(1);
                System.out.println(student);
                //休眠1秒
                TimeUnit.SECONDS.sleep(1);
            }
     
            System.out.println("cache stats:");
            //最后打印缓存的命中率等情况
            System.out.println(studentCache.stats().toString());
        }
    

    cache stats:
    CacheStats{hitCount=17, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=1348802, evictionCount=2}

    看看到在20此循环中命中次数是17次,未命中3次,这是因为我们设定缓存的过期时间是写入后的8秒,所以20秒内会

    失效两次,另外第一次获取时缓存中也是没有值的,所以才会未命中3次,其他则命中。

    上面的代码来自博文:使用google guava做内存缓存

    展开全文
  • springboot整合redis做缓存

    万次阅读 多人点赞 2018-09-13 13:39:33
    之前的项目中,用到过redis,主要是使用redis做缓存,redis在web开发中使用的场景很多,其中缓存是其中一个很重要的使用场景,之所以用作缓存,得益于redis的读写数据,尤其是在读取数据的时候是直接走内存的,这样...
  • 浅谈JAVA数据缓存

    千次阅读 2013-09-26 06:44:52
    一般是采用HashMap来作为缓存的存储结构,但Java没有全局变量的概念,怎么保证缓存中的数据不被垃圾回收器收集,而造成数据的丢失呢?    请问一般的缓存是怎样保证数据的独立性(即脱离垃圾回收器)的?  一...
  • Java-使用WeakHashMap做缓存

    千次阅读 2013-09-12 15:10:56
    WeakHashMap使用WeakReferenceWeakHashMap的Value。当key的引用被置为null的时候,map的内容会被很快gc。 package comz; import java.io.BufferedReader; import java.io.FileReader; import java.util.ArrayList...
  • JAVA缓存技术

    千次阅读 2017-08-16 13:37:48
    最近再ITEYE上看到关于讨论JAVA缓存技术的帖子比较多,自己不懂,所以上网大概搜了下,找到一篇,暂作保存,后面如果有用到可以参考。此为转贴,帖子来处:...
  • java动态缓存技术:WEB缓存应用

    千次阅读 2013-03-30 21:35:58
     java动态缓存jar包请下载。 源代码:  CacheData.java 存放缓存数据的Bean /**  *   */ package com.cari.web.cache; /**  * @author zsy  *   */ public class CacheData {  ...
  • 现在有这样一个场景,业务层调用远程webservice获取的结果传输到前台但只需要显示几个关键字段,待用户查询完毕后点击提交这些结果全部数据都要保存,在后台如何缓存这个查询结果?有用过在业务层增加一个属性来保存...
  • java实现缓存框架

    千次阅读 2018-07-08 23:06:14
    缓存的原理外存: 外储存器是指除计算机内存及CPU缓存以外的储存器,此类储存器一般断电后仍然能保存数据。常见的外存储器有硬盘、软盘、光盘、U盘等,一般的软件都是安装在外存中(windows系统指的是CDEF盘, Linux...
  • 缓存机制:java缓存的原理

    千次阅读 2017-12-15 19:04:26
     也就是我们经常说的(CDEF盘的大小)外储存器是指除计算机内存及CPU缓存以外的储存器,此类储存器一般断电后仍然能保存数据。常见的外存储器有硬盘、软盘、光盘、U盘等,一般的软件都是安装在外存中 内存: ...
  • JAVA自定义缓存设计方案

    千次阅读 2012-06-12 22:52:15
    缓存有一个缓存基类,其它缓存类都继承基类.去实现一些方法,其它缓存类可以在里面写自己特有的方法.以带到不同的需求.实现基类主要是方便管理缓存. 基类内容如下: import java.util.ArrayList; import java.util....
  • Java缓存的实质

    千次阅读 2010-01-28 20:03:00
    计算机缓存的定义 :缓存是CPU的一部分,它存在于CPU中就此可以说明 CPU中不可能存放大量的数据所以java缓存 不是真正意义上的缓存而且缓存是为了解决CPU速度和内存速度的速度差异问题 内存中被CPU访问最频繁的...
  • java 缓存ByteBuffer

    千次阅读 2019-01-11 11:03:47
    ByteBuffer创建缓存区常用的有两种方式: 1、使用allocate直接创建指定大小的空缓存区 ByteBuffer buffer=ByteBuffer.allocate(64); position: 0 limit: 64 capacity 64 2、使用wrap创建一个具有内容的缓存区 ...
  • Java中如何使用Redis做缓存

    万次阅读 2015-04-07 14:10:31
    基本功能测试  1、程序基本结构    2、主要类 ...import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import redis.clients.je
  • Java缓存技术

    千次阅读 2016-09-20 10:52:49
    原文地址:... 介绍  JNotify:http://jnotify.sourceforge.net/,通过JNI技术,让Java代码可以实时的监控制定文件夹内文件的变动信息,支持Linux/Windows/MacOS; EHCache:http:
  • Java实现本地缓存

    千次阅读 2019-10-05 09:25:53
    项目中我们经常会使用Nosql数据库,如Redis等做缓存。但是对于数据量很小的,访问非常频繁的,我们也可以存在本地缓存中。我将利用concurrentHashMap等集合容器实现一个本地缓存。 1.基于concurrentHashMap的本地...
  • Java常用缓存组件和方法

    千次阅读 2016-08-01 19:47:37
    在我们系统开发中,有许多关于性能提升方面的组件,这里介绍并总结一下; 缓冲(Buffer) ...上面简单的介绍了几个常用的缓存组件,以后会详细的去分析这些东西来对java程序性能进行优化;
  • JAVA Redis缓存实现步骤

    万次阅读 2017-09-10 02:34:02
    根据配置文件配置的key读取缓存,若缓存存在,string转具体类型 例如list 直接返回  b.缓存不存在,操作数据库,读取数据  c.存储key值到缓存中 5:同步缓存  dao中增加删除缓存的方法,你发现...
  • java缓存处理

    千次阅读 2009-11-13 13:54:00
    java缓存技术一(转)看一粒沙中的世界, 一朵野花中的天堂。 把无限握于掌中, 把永恒握于瞬间。——威廉• 布莱克 开始讨论缓存之前,让我们先来讨论讨论另外一个问题:理论和实践.从ahuaxuan接触的程序员来看,有的...
  • java缓存数据同步问题

    2016-03-27 09:05:47
    最近在一个简单的java缓存,线程的主要功能是:查询缓存中是否存在该值,存在则返回,不存在则计算,计算完了将该键值对放到Cache里面。但是这里有个数据同步的问题,可能会有重复计算。即线程1发现不存在该值,去...
  • Java 内存缓存数据(map实现)

    千次阅读 2019-03-21 11:25:44
    方式一:单一容器存储 import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; ... * @Title: WeatherCacheUtils ... * @Description: 常用数据缓存工具 * @a...
  • 基于redis的zSet集合数据缓存实现分页查询 java

    万次阅读 热门讨论 2018-05-24 15:34:20
    需要场景:最近公司要手机页面展示新闻文章数据查询的优化工作,让我提个优化方案。现状是目前手机页面的数据请求系统后台,系统后台然后调用其他系统的接口,返回分页数据到前台展示,这样一来,用户每次下拉到...
  • Java实现LRU缓存

    千次阅读 2018-03-14 13:27:43
    原文地址:https://www.cnblogs.com/lzrabbit/p/3734850.htmlLRU缓存实现(Java) LRU Cache的LinkedHashMap实现LRU Cache的链表+HashMap实现LinkedHashMap的FIFO实现调用示例LRU是Least Recently Used 的缩写,翻译...
  • 使用Java缓存

    千次阅读 2019-06-19 15:41:54
    通过CacheBuilder.removalListener(RemovalListener),你可以声明一个监听器,以便缓存项被移除时一些额外操作。缓存项被移除时,RemovalListener会获取移除通知[RemovalNotification],其中包含移除原因...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 450,135
精华内容 180,054
关键字:

java怎么做缓存

java 订阅