zoukankan      html  css  js  c++  java
  • Spring + MySQL + Mybatis + Redis【二级缓存】

    一、Redis环境

    Redis 官网 :http://redis.io/

    windows下载:https://github.com/dmajkic/redis/downloads

    1、文件解压缩

    2、启动Redis服务器

     

    3、启动Redis客户端

     

    4、测试Redis缓存

    redis-cli.exe -h 127.0.0.1 -p 6379

    set keytest valuestest 根据key、value加入缓存

    get keytest 根据key获取值

    flushall 清空所有缓存

    5、设置Redis密码

    6、conf配置文件

    #是否以后台守护进程运行,默认为no, 取值yes, no 
    daemonize no                   
    
    #pid文件存放路径
    pidfile /var/run/redis.pid    
    
    #配置redis端口,默认6379
    port 6379                    
    
    #绑定ip。默认绑定所有本机ip,一般用在服务器多ip下,可以只监听内网服务器ip,保证服务安全
    bind 127.0.0.1              
    
    #sock文件 
    unixsocket /tmp/redis.sock  
    
    #客户端超时时间,单位秒 
    timeout 300                
    
    #log级别,支持四个级别,debug,notice,verbose,warning 
    loglevel verbose           
    
    #log文件路径
    logfile                    
    
    #log输出到标准设备,logs不写文件,输出到空设备,/deb/null
    logfile stdout               
    
    #保存快照的频率,在多长时间内执行一定数量的写操作时,保存快照的频率,可以设置多个条件。如果都注释掉,则不做内存数据持久化。如果只是把redis只用作cache,不开启持久化功能
    save <seconds> <changes> 
    save 900 1 
    
    #是否使用压缩
    rdbcompression            
    
    #快照数据库名称 
    dbfilename               
    
    #数据库存放路径 
    dir                       
    
    #redis主从 做法  在从上填上主的IP和端口号 主上不用做任何设置
    slaveof <masterip> <masterport>   
    
    #主库服务器口令,如果主服务器未打开requirepass,则不需要此项
    masterauth <master-password>     
    
    #在master服务器挂掉或者同步失败时,从服务器是否继续提供服务
    slave-serve-stale-data yes         
    
    #设置redis服务密码,如果开启,则客户端连接时需要 -a  指定密码,否则操作会提示无权限
    requirepass foobared               
    
    #命令改名,相当于linux  alias,可以用改功能屏蔽一些危险命令
    rename-command                    
    
    #最大连接数;0 表示不限制
    maxclients 128                   
    
    #最大使用内存(分配的内存),推荐生产环境下做相应调整,我们用的是只用来做高速缓存,限制2G。默认情况下,redis会占用可用的所有内存
    maxmemory <bytes>                
    
    #过期策略,提供六种策略  
    maxmemory-policy volatile-lru   
    volatile-lru    //删除过期和lru 的key(默认值) 
    allkeys-lru     //删除lru算法的key  
    volatile-random //随机删除即将过期key  
    allkeys->random //随机删除 
    volatile-ttl    //删除即将过期的  
    noeviction      //永不过期,返回错误 
    
    #是否开启appendonlylog,开启的话每次写操作会记一条log。相当于mysql的binlog;不同的是,每次redis启动都会读此文件构建完整数据。即使删除rdb文件,数据也是安全的 
    appendonly    
    
    #日志文件的名称,默认appendonly.aof
    appendfilename appendonly.aof   
    
    #异步写append file 的策略。类似mysql事物log写方式。三种
    appendfsync                       
    appendfsync always               //同步,每次写都要flush到磁盘,安全,速度慢。 
    appendfsync everysec             //每秒写(默认值,推荐值)同mysql 
    appendfsync no                   //交给操作系统去做flush的动作 
    
    #虚拟内存开关 
    vm-enabled no               
    
    #swap文件,不同redis swap文件不能共享。而且生产环境下,不建议放在tmp目录
    vm-swap-file /tmp/redis.swap     
    
    #vm大小限制。0:不限制,建议60-80% 可用内存大小
    vm-max-memory 0                 
    
    #根据缓存内容大小调整,默认32字节
    vm-page-size 32                
    
    #page数。每 8 page,会占用1字节内存。vm-page-size * vm-pages 等于 swap 文件大小
    vm-pages 134217728              
    
    #vm 最大io线程数。注意: 0 标志禁止使用vm 
    vm-max-threads 4

    二、实现Redis缓存

    1、整体思路

    • 参考Ehcache实现MyBatis二级缓存代码(Maven引用对应jar查阅)
    • 使用Spring管理Redis连接池
    • 模仿EhcacheCache,实现RedisCache

    2、pom.xml中加入Maven依赖

    <!-- spring-redis实现 -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
        <version>1.6.2.RELEASE</version>
    </dependency>
    <!-- redis客户端jar -->
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.8.0</version>
    </dependency> 

    3、引入applicationContext.xml中引入redis配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!-- redis数据源 -->
        <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
            <property name="maxIdle" value="${redis.maxIdle}" />
            <property name="maxTotal" value="${redis.maxActive}" />
            <property name="maxWaitMillis" value="${redis.maxWait}" />
            <property name="testOnBorrow" value="${redis.testOnBorrow}" />
        </bean>
        <!-- Spring-redis连接池管理工厂 -->
        <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
              p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}"  p:pool-config-ref="poolConfig"/>
    <!-- 使用中间类解决RedisCache.jedisConnectionFactory的静态注入,从而使MyBatis实现第三方缓存 --> <bean id="redisCacheTransfer" class="com.hsmdata.springTest.modules.cache.RedisCacheTransfer"> <property name="jedisConnectionFactory" ref="jedisConnectionFactory"/> </bean> </beans>

    4、redis.properties配置文件

    #============================#
    #==== Redis settings ====#
    #============================#
    #redis 服务器 IP
    redis.host=127.0.0.1
    
    #redis 服务器端口
    redis.port=6379
    
    #redis 密码
    redis.pass=
    
    #redis 支持16个数据库(相当于不同用户)可以使不同的应用程序数据彼此分开同时又存储在相同的实例上
    redis.dbIndex=3
    
    #redis 缓存数据过期时间单位秒(3600*12 = 43 200)
    redis.expiration=43200
    
    #控制一个 pool 最多有多少个状态为 idle 的jedis实例
    redis.maxIdle=200
    
    #控制一个 pool 可分配多少个jedis实例
    redis.maxActive=1000
    
    #当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
    redis.maxWait=500
    
    #在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;
    redis.testOnBorrow=true

    5、创建缓存实现类RedisCache

    package com.hsmdata.springTest.modules.cache;
    
    import org.apache.ibatis.cache.Cache;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.data.redis.connection.jedis.JedisConnection;
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import redis.clients.jedis.exceptions.JedisConnectionException;
    
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    /**
     * @author 
     * 2018-04-10 20:50
     * $DESCRIPTION}
     */
    
    public class RedisCache implements Cache
    {
        private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);
    
        private static JedisConnectionFactory jedisConnectionFactory;
    
        private final String id;
    
        /**
         * The {@code ReadWriteLock}.
         */
        private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
        public RedisCache(final String id) {
            if (id == null) {
                throw new IllegalArgumentException("Cache instances require an ID");
            }
            logger.debug("MybatisRedisCache:id=" + id);
            this.id = id;
        }
    
        @Override
        public void clear()
        {
            JedisConnection connection = null;
            try
            {
                connection = jedisConnectionFactory.getConnection();
                connection.flushDb();
                connection.flushAll();
            }
            catch (JedisConnectionException e)
            {
                e.printStackTrace();
            }
            finally
            {
                if (connection != null) {
                    connection.close();
                }
            }
        }
    
        @Override
        public String getId()
        {
            return this.id;
        }
    
        @Override
        public Object getObject(Object key)
        {
            Object result = null;
            JedisConnection connection = null;
            try
            {
                connection = jedisConnectionFactory.getConnection();
                RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
                result = serializer.deserialize(connection.get(serializer.serialize(key)));
            }
            catch (JedisConnectionException e)
            {
                e.printStackTrace();
            }
            finally
            {
                if (connection != null) {
                    connection.close();
                }
            }
            return result;
        }
    
        @Override
        public ReadWriteLock getReadWriteLock()
        {
            return this.readWriteLock;
        }
    
        @Override
        public int getSize()
        {
            int result = 0;
            JedisConnection connection = null;
            try
            {
                connection = jedisConnectionFactory.getConnection();
                result = Integer.valueOf(connection.dbSize().toString());
            }
            catch (JedisConnectionException e)
            {
                e.printStackTrace();
            }
            finally
            {
                if (connection != null) {
                    connection.close();
                }
            }
            return result;
        }
    
        @Override
        public void putObject(Object key, Object value)
        {
            JedisConnection connection = null;
            try
            {
                connection = jedisConnectionFactory.getConnection();
                RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
                connection.set(serializer.serialize(key), serializer.serialize(value));
            }
            catch (JedisConnectionException e)
            {
                e.printStackTrace();
            }
            finally
            {
                if (connection != null) {
                    connection.close();
                }
            }
        }
    
        @Override
        public Object removeObject(Object key)
        {
            JedisConnection connection = null;
            Object result = null;
            try
            {
                connection = jedisConnectionFactory.getConnection();
                RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
                result =connection.expire(serializer.serialize(key), 0);
            }
            catch (JedisConnectionException e)
            {
                e.printStackTrace();
            }
            finally
            {
                if (connection != null) {
                    connection.close();
                }
            }
            return result;
        }
    
        public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
            RedisCache.jedisConnectionFactory = jedisConnectionFactory;
        }
    
    }
    View Code

    6、创建中间类RedisCacheTransfer,完成RedisCache.jedisConnectionFactory的静态注入

    package com.hsmdata.springTest.modules.cache;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    
    /**
     * @author 
     * 2018-04-10 20:52
     * $DESCRIPTION}
     */
    
    public class RedisCacheTransfer {
        @Autowired
        public void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
            RedisCache.setJedisConnectionFactory(jedisConnectionFactory);
        }
    }
    View Code

    7、mapper中加入MyBatis二级缓存

    <mapper namespace="com.hsmdata.springTest.modules.mapper.UserMapper" >
      <!--开启本mapper的二级缓存,隔10秒自动刷新缓存 flushInterval="10000" -->
      <cache type="com.hsmdata.springTest.modules.cache.RedisCache" />

    8、Mybatis全局开启二级缓存

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <settings>
            <!-- 开启二级缓存,默认是false -->
            <setting name="cacheEnabled" value="true"/>
    
            <!--resultMap中的association和collection标签具有延迟加载的功能。-->
            <!--延迟加载的意思是说,在关联查询时,利用延迟加载,先加载主信息。使用关联信息时再去加载关联信息。-->
    
            <!-- lazyLoadingEnabled:延迟加载启动,默认是false
            全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。-->
            <setting name="lazyLoadingEnabled" value="false"/>
    
            <!-- aggressiveLazyLoading:积极的懒加载,false的话按需加载,默认是true
            当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。-->
            <setting name="aggressiveLazyLoading" value="true"/>
    
            <setting name="multipleResultSetsEnabled" value="true"/> 
            <setting name="useColumnLabel" value="true"/> 
            <setting name="useGeneratedKeys" value="true"/>
            <setting name="defaultExecutorType" value="SIMPLE"/> 
            <setting name="defaultStatementTimeout" value="25000"/>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
    </configuration>
    View Code

    9、测试

    package com.springTest.mybatis.cache;
    
    import com.hsmdata.springTest.modules.entity.User;
    import com.hsmdata.springTest.modules.mapper.UserMapper;
    import com.hsmdata.springTest.modules.service.UserService;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * @author 
     * 2018-04-10 15:11
     * $DESCRIPTION}
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration({"classpath:/config/applicationContext.xml", "classpath:/config/spring-servlet.xml"})
    public class CacheTest {
        @Autowired
        private UserService userService;
        @Autowired
        private SqlSessionFactory sqlSessionFactory;
    
        /**
         * 同一个sqlSession
         */
        @Test
        public  void testFirstCache(){
            SqlSession sqlSession=sqlSessionFactory.openSession();
            UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
            User user=userMapper.selectByPrimaryKey(56);
            System.out.println(user);
    
    /*       对sqlsession执行commit操作,也就意味着用户执行了update、delete等操作,那么数据库中的数据势必会发生变化,如果用户请求数据仍然使用之前内存中的数据,那么将读到脏数据。
            所以在执行sqlsession操作后,会清除保存数据的HashMap,用户在发起查询请求时就会重新读取数据并放入一级缓存中了。*/
         //   sqlSession.commit();
    
            user=userMapper.selectByPrimaryKey(56);
            System.out.println(user);
        }
    
        /**
         * 不同的sqlSession
         */
        @Test
        public  void testSecondaryCache(){
            SqlSession sqlSession=sqlSessionFactory.openSession();
            UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
            User user=userMapper.selectByPrimaryKey(56);
            System.out.println(user);
    
    //        即使开启了二级缓存,不同的sqlsession之间的缓存数据也不是想互访就能互访的,必须等到sqlsession关闭了以后,才会把其一级缓存中的数据写入二级缓存。
    //        关闭session
    //        sqlSession.close();
    
    //        通过sqlSessionFactory创建一个新的session
            sqlSession=sqlSessionFactory.openSession();
    //        获取mapper对象
            userMapper=sqlSession.getMapper(UserMapper.class);
    
            user=userMapper.selectByPrimaryKey(56);
            System.out.println(user);
        }
    
        /**
         * 不同的sqlSession
         */
        @Test
        public void testSecondaryCache2() {
           /* User user=new User("cache","123456","cache","","male",20);
            userService.insert(user);*/
    
            User user = userService.get(56);
            System.out.println(user);
    
            User user1 = userService.get(56);
            System.out.println(user1);
        }
    
        @Test
        public void testFirstCache2() {
           /* User user=new User("cache","123456","cache","","male",20);
            userService.insert(user);*/
    
            User user = userService.getTwo(56);
            System.out.println(user);
        }
    
        @Test
        public void testRedisCache(){
            User user=userService.get(55);
            System.out.println(user);
    
            User user2=userService.get(56);
            System.out.println(user2);
    
            User user3=userService.get(55);
            System.out.println(user3);
    
        }
    
    }
     
  • 相关阅读:
    Git: fatal: Pathspec is in submodule
    cnpm不是内部或外部命令 cnpm: command not found
    kubectl top查看k8s pod的cpu , memory使用率情况
    Docker 技巧:删除 Docker 所有镜像
    Docker 快速删除所有容器
    使用Dockerfile文件构建基于centOS系统的nodejs镜像
    CentOS下nodejs最简单的安装方法
    yum安装nodejs 8
    系统空间占用排查 tomcat超大日志catalina.out 删除 与df 状态更新
    用dockerfile构建基于centos系统的jar包的镜像
  • 原文地址:https://www.cnblogs.com/esther-qing/p/8796888.html
Copyright © 2011-2022 走看看