SpringDataRedis解释

SpringDataRedis简介

  • 项目常见问题思考
    项目首页每天有大量的人访问,对数据库造成很大的访问压力,甚至是瘫痪。那如何解决呢?我们通常的做法有两种:一种是数据缓存、一种是网页静态化。我们今天讨论第一种解决方案。

  • Redis
    redis是一款开源的Key-Value数据库,运行在内存中,由ANSI C编写。企业开发通常采用Redis来实现缓存。同类的产品还有memcache 、memcached 、MongoDB等。

  • Jedis
    Jedis是Redis官方推出的一款面向Java的客户端,提供了很多接口供Java语言调用。可以在Redis官网下载,当然还有一些开源爱好者提供的客户端,如Jredis、SRP等等,推荐使用Jedis。
  • Spring Data Redis
    Spring-data-redis是spring大家族的一部分,提供了在srping应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现。
  • spring-data-redis针对jedis提供了如下功能:
    • 1.连接池自动管理,提供了一个高度封装的“RedisTemplate”类
    • 2.针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
      • ValueOperations:简单K-V操作
      • SetOperations:set类型数据操作
      • ZSetOperations:zset类型数据操作
      • HashOperations:针对map类型的数据操作
      • ListOperations:针对list类型的数据操作

Spring Data Redis入门小Demo

1 准备工作

(1)构建Maven工程 SpringDataRedisDemo
(2)引入Spring相关依赖、引入JUnit依赖 (内容参加其它工程)

    <!-- 集中定义依赖版本号 -->
    <properties>
        <junit.version>4.12</junit.version>
        <spring.version>4.2.4.RELEASE</spring.version>
    </properties>

    <dependencies>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.9</version>
        </dependency>
    </dependencies>

(3)引入Jedis和SpringDataRedis依赖

<!-- 缓存 -->
<dependency> 
          <groupId>redis.clients</groupId> 
          <artifactId>jedis</artifactId> 
          <version>2.8.1</version> 
</dependency> 
<dependency> 
          <groupId>org.springframework.data</groupId> 
          <artifactId>spring-data-redis</artifactId> 
          <version>1.7.2.RELEASE</version> 
</dependency>   

(4)在src/main/resources下创建properties文件夹,建立redis-config.properties

        # 主机
        redis.host=127.0.0.1 
        # 端口号
        redis.port=6379 
        # 密码 一般不需要
        redis.pass= 
        redis.database=0 
        redis.maxIdle=300 
        redis.maxWait=3000 
        redis.testOnBorrow=true 

(5)在src/main/resources下创建spring文件夹 ,创建applicationContext-redis.xml

  <?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" 
  xmlns:context="http://www.springframework.org/schema/context" 
  xmlns:mvc="http://www.springframework.org/schema/mvc" 
  xmlns:cache="http://www.springframework.org/schema/cache"
  xsi:schemaLocation="http://www.springframework.org/schema/beans   
            http://www.springframework.org/schema/beans/spring-beans.xsd   
            http://www.springframework.org/schema/context   
            http://www.springframework.org/schema/context/spring-context.xsd   
            http://www.springframework.org/schema/mvc   
            http://www.springframework.org/schema/mvc/spring-mvc.xsd 
            http://www.springframework.org/schema/cache  
            http://www.springframework.org/schema/cache/spring-cache.xsd">  

   <context:property-placeholder location="classpath*:properties/*.properties" />   

   <!-- redis 相关配置 --> 
   <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">  
     <property name="maxIdle" value="${redis.maxIdle}" />   
     <property name="maxWaitMillis" value="${redis.maxWait}" />  
     <property name="testOnBorrow" value="${redis.testOnBorrow}" />  
   </bean>  

   <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"/>  

   <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">  
        <property name="connectionFactory" ref="JedisConnectionFactory" />  
   </bean>  

</beans>  
  • maxIdle :最大空闲数
  • maxWaitMillis:连接时的最大等待毫秒数
  • testOnBorrow:在提取一个jedis实例时,是否提前进行验证操作;如果为true,则得到的jedis实例均是可用的;

2 值类型操作

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:spring/applicationContext-redis.xml")
public class TestValue {

    //泛型可以不指定
    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    @Test
    public void setValue(){
        //存值
        redisTemplate.boundValueOps("name").set("张三");
    }

    @Test
    public void getValue(){
        String name = redisTemplate.boundValueOps("name").get();
        //如果没有 name 则会返回 null
        System.out.println(name);
    }

    @Test
    public void deleteValue(){
        //移除值
        redisTemplate.delete("name");
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

3 Set类型操作

/**
 * 特点: 无序,不可重复
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:spring/applicationContext-redis.xml")
public class TestSet {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Test
    public void setValue(){
        redisTemplate.boundSetOps("nameset").add("曹操");
        redisTemplate.boundSetOps("nameset").add("刘备");
        redisTemplate.boundSetOps("nameset").add("关羽");
    }

    @Test
    public void getValue(){
        //如果没有 nameset 返回  []
        Set<String> members = redisTemplate.boundSetOps("nameset").members();
        // 没有循序,和存入的循序无关
        // [关羽, 刘备, 曹操] 
        System.out.println(members);
    }

    @Test
    public void removeValue(){
        //移除一个
        redisTemplate.boundSetOps("nameset").remove("刘备");

        //全部移除
        redisTemplate.delete("nameset");

    }
}

4 List类型操作

/**
 * 特点: 有序, 可重复
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:spring/applicationContext-redis.xml")
public class TestList {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 右压栈: 后添加的元素排在后面
     *              先进先出
     */
    @Test
    public void setValueRight(){
        redisTemplate.boundListOps("name").rightPush("张三");
        redisTemplate.boundListOps("name").rightPush("李四");
        redisTemplate.boundListOps("name").rightPush("王五");
        redisTemplate.boundListOps("name").rightPush("赵六");
    }


    /**
     * 右压栈的输入
     */
    @Test
    public void getValue(){
        // [张三, 李四, 王五,赵六]
        List<String> name = redisTemplate.boundListOps("name").range(0, 10);
        System.out.println(name);

    }

    /**
     * 左压栈: 先进后出
     */
    @Test
    public void setValueLeft(){
        redisTemplate.boundListOps("nameLeft").leftPush("关羽");
        redisTemplate.boundListOps("nameLeft").leftPush("张飞");
        redisTemplate.boundListOps("nameLeft").leftPush("诸葛亮");
        redisTemplate.boundListOps("nameLeft").leftPush("刘备");
    }

    /**
     * 左压栈的输入
     */
    @Test
    public void getValueLeft(){
        // [刘备, 诸葛亮, 张飞, 关羽]
        List<String> name = redisTemplate.boundListOps("nameLeft").range(0, 10);
        System.out.println(name);
    }

    /**
     * 按索引位置查询元素
     */
    @Test
    public void searchByIndex(){
        // 关羽
        String index = redisTemplate.boundListOps("nameLeft").index(3);
        System.out.println(index);
        //集合元素个数
        Long size = redisTemplate.boundListOps("nameLeft").size();
        System.out.println(size);
    }

    /**
     * 移除
     */
    @Test
    public void removeByIndex(){
        // 移除元素的个数   移除元素的name
        redisTemplate.boundListOps("nameLeft").remove(1, "诸葛亮");
        redisTemplate.delete("nameLeft");//移除所有值
    }
}

5 Hash类型操作

/**
 * 存取有序,不可重复
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:spring/applicationContext-redis.xml")
public class TestHash {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 存值
     */
    @Test
    public void setValue(){
        redisTemplate.boundHashOps("hash").put("a", "唐僧");
        redisTemplate.boundHashOps("hash").put("b", "八戒");
        redisTemplate.boundHashOps("hash").put("c", "沙和尚");
        redisTemplate.boundHashOps("hash").put("d", "孙悟空");
    }

    /**
     * 获取所有的key
     */
    @Test
    public void getAllKey(){
        //获取所有的key : [a, b, c, d]
        Set<Object> key = redisTemplate.boundHashOps("hash").keys();
        System.out.println(key);
    }

    /**
     * 获取所有的值
     */
    @Test
    public void getAllValue(){
        //获取所有的值
        List<Object> values = redisTemplate.boundHashOps("hash").values();
        System.out.println(values); //[唐僧, 八戒, 沙和尚, 孙悟空]

        //根据key取值
        String str = (String)redisTemplate.boundHashOps("hash").get("c");
        System.out.println(str); // 沙和尚
    }

    /**
     * 移除某个小key的值
     */
    @Test
    public void removeKey(){
        redisTemplate.boundHashOps("hash").delete("c");
    }
}
  •  

在项目中的应用

1 需求分析

项目中首页的广告每次都是从数据库读取, 这样当网站访问量达到高峰时段,对数据库压力很大,并且影响执行效率。我们需要将这部分广告数据缓存起来。

2 读取缓存

2.1 公共组件层

因为缓存对于我们整个的系统来说是通用功能。广告需要用,其它数据可能也会用到,所以我们将配置放在公共组件层中较为合理。

(1. 引入依赖
(2. 创建配置文件

2.2 后端服务实现层中添加对公共组件层的依赖

2.3 后端服务实现层中缓存的实现

思路: 在查询数据时,我们要先从缓存中查询数据,如果缓存中没有再从数据库中查询,
      将从数据库中查询出来的数据存入缓存中.
  • 1
  • 2
    @Autowired
    private RedisTemplate redisTemplate;

        @Override
        public List<TbContent> findByCategoryId(Long categoryId) {

            //从缓存中查询数据
            List<TbContent> list = (List<TbContent>) redisTemplate.boundHashOps("content").get(categoryId);

            if(list == null){
                System.out.println("从数据库中查询数据并放入缓存");
                TbContentExample example = new TbContentExample();
                Criteria criteria = example.createCriteria();
                criteria.andCategoryIdEqualTo(categoryId);//指定分类ID
                criteria.andStatusEqualTo("1");//指定条件有效的
                example.setOrderByClause("sort_order");//排序
                list = contentMapper.selectByExample(example);
                redisTemplate.boundHashOps("content").put(categoryId, list);//放入缓存
            }else{
                System.out.println("从缓存中查询");
            }
            return list;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

2.4 细节

问题: 当我们数据库中的数据发生改变时,从缓存中查询出的数据并没有改变.
解决: 当广告数据发生变更时,需要将缓存数据清除,这样再次查询才能获取最新的数据
我们看一下那些操作使数据库中的数据发生改变:
    (1 增加
         再增加之前我们要清除缓存,然后再增加,这样会重新在数据库中查询一次,那么数据库的
         改变就会同步到缓存中
         contentMapper.insert(content);
         redisTemplate.boundHashOps("content").delete(content.getCategoryId());

    (2 删除
        for(Long id:ids){
        //清除缓存  先清除在删除
        Long categoryId = contentMapper.selectByPrimaryKey(id).getCategoryId();
        redisTemplate.boundHashOps("content").delete(categoryId);

        contentMapper.deleteByPrimaryKey(id);
        }   

    (3 修改
        注意`:  用户可能会修改广告的分类,这样需要把原分类的缓存和新分类的缓存都清除掉。
    @Override
    public void update(TbContent content){
        //查询修改前的分类Id
        Long categoryId = contentMapper.selectByPrimaryKey(content.getId()).getCategoryId();
        redisTemplate.boundHashOps("content").delete(categoryId);


        contentMapper.updateByPrimaryKey(content);
        //如果分类ID发生了修改,清除修改后的分类ID的缓存
        if(categoryId.longValue()!=content.getCategoryId().longValue()){
            redisTemplate.boundHashOps("content").delete(content.getCategoryId());
    }   

}

猜你喜欢

转载自blog.csdn.net/qq_42809504/article/details/82912952