从零学 spring cloud第4-2课:中间件之Redis

    项目的DEMO代码:https://github.com/heyu52/-spring-cloud
    Redis作为当下最火的缓存服务,在实际的应用中有很多,下面我们就在SpringBoot中看看是如何操作的。
Redis 数据类型

Redis支持五种数据类型:

  • string(字符串) string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。
                    string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。
                    string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。
  • hash(哈希)是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象
  • list(列表)列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
  • set(集合)Set是string类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
  • zset(sorted set:有序集合) zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。

各个数据类型应用场景

类型简介特性场景
String(字符串)二进制安全可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M
Hash(字典)键值对集合,即编程语言中的Map类型适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去)存储、读取、修改用户属性
List(列表)链表(双向链表)增删快,提供了操作某一段元素的API1,最新消息排行等功能(比如朋友圈的时间线) 2,消息队列
Set(集合)哈希表实现,元素不重复1、添加、删除,查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐
Sorted Set(有序集合)将Set中的元素增加一个权重参数score,元素按score有序排列数据插入集合时,已经进行天然排序1、排行榜 2、带权重的消息队列

接下来我们创建项目
在这里插入图片描述
打开项目后,我们查看pom文件,可以看到添加了下面的依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

我们要使用redis,我们就必须先配置服务主机,和连接数据一样的道理,我们先在application.properties中配置服务连接

# Redis 数据库索引(默认为 0)
spring.redis.database=0
# Redis 服务器地址
spring.redis.host=192.168.0.123
# Redis 服务器连接端口
spring.redis.port=6379
# Redis 服务器连接密码(默认为空)
spring.redis.password=123456
# 连接池最大连接数(使用负值表示没有限制) 默认 8
spring.redis.lettuce.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接 默认 8
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接 默认 0
spring.redis.lettuce.pool.min-idle=0
# 从配置也可以看出 Spring Boot 默认支持 Lettuce 连接池。

缓存配置
在这里可以为 Redis 设置一些全局配置,比如配置主键的生产策略 KeyGenerator,如不配置会默认使用参数 名作为主键。新建配置类RedisConfig

package com.csdn.demo.Config;

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
}

这里一定要注意我们使用了注解:@EnableCaching 来开启缓存。
接下来我们测试一下String类型的存和取,新增MyController

package com.csdn.demo.web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {
    @Autowired
    private RedisTemplate redisTemplate;
    
    @RequestMapping("/GetString")
    public String GetStringByKey(String key) {
       return redisTemplate.opsForValue().get(key).toString();
}

    @RequestMapping("/SetStringByKey")
    public void SetStringByKey(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }
}

运行项目,先将值存入Redis ,在浏览器输入:http://127.0.0.1:8080/SetStringByKey?key=csdn&value=20170921
从RedisDesktopManager可以看到
在这里插入图片描述
我们再从程序中把值给读出来,在浏览器输入:http://127.0.0.1:8080/GetString?key=csdn
在这里插入图片描述
这就是最简单的存取操作了。接下来,我们再对其它的类型进行测试。

对象存储
新建model

package com.csdn.demo.model;

import java.io.Serializable;

public class User implements Serializable {

    private static final long serialVersionUID = 1L;
    private Long id;
    private String userName;
    private String regTime;

    public User() {
        super();
    }
    public User(String email, String nickname, String password, String userName, String regTime) {
        super();
        this.userName = userName;
        this.regTime = regTime;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getRegTime() {
        return regTime;
    }

    public void setRegTime(String regTime) {
        this.regTime = regTime;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", regTime='" + regTime + '\'' +
                '}';
    }
}

接下来在控制器中增加测试方法

    @RequestMapping("/SetObjByKey")
    public void SetObjByKey(){
        User user=new User("csdn001","2019");
        ValueOperations<String, User> operations=redisTemplate.opsForValue();
        operations.set("csdn001", user);
    }

    @RequestMapping("/GetObjByKey")
    public User GetObjByKey(){
        ValueOperations<String, User> operations=redisTemplate.opsForValue();
        User user=operations.get("csdn001");
        return user;
    }

运行项目,先将值存入Redis ,在浏览器输入:http://127.0.0.1:8080/SetObjByKey
从RedisDesktopManager可以看到(记得要先Reloda才能查看)
在这里插入图片描述
我们再从程序中把值给读出来,在浏览器输入:http://127.0.0.1:8080/GetObjByKey
在这里插入图片描述
对象和String其实操作起来,没有任何区别。能存数据,就一定能删除数,我们来写一个删除的方法

    @RequestMapping("/DeleteObjByKey")
    public String DeleteObjByKey(){
        redisTemplate.delete("csdn001");
        return "删除成功";
    }

运行项目,在浏览器输入:http://127.0.0.1:8080/DeleteObjByKey
在这里插入图片描述
我们再从程序中把值给读出来,在浏览器输入:http://127.0.0.1:8080/GetObjByKey
在这里插入图片描述
这时候,值就没有了,再可以去RedisDesktopManager进行查看,reload后,数据也不存在了
在这里插入图片描述
让数据自动失效
我们如果想把数据存进去,规定的时间内数据就自动失效,可以设置过期时间

    @RequestMapping("/SetExpireObjByKey")
    public void SetExpireObjByKey(){
        User user=new User("csdn001","2019");
        ValueOperations<String, User> operations=redisTemplate.opsForValue();
        operations.set("csdn001", user,100, TimeUnit.MILLISECONDS);
    }

以上代码就设置了有效期为100ms。

Hash(哈希)

    @RequestMapping("/SetHashObjByKey")
    public void SetHashObjByKey() {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        hash.put("hash001", "field001", "value001");
        hash.put("hash001", "field002", "value002");
        hash.put("hash002", "field001", "value003");
    }

运行项目,在浏览器输入:http://127.0.0.1:8080/SetHashObjByKey
从RedisDesktopManager可以看到(记得要先Reloda才能查看)
在这里插入图片描述
读取数据方法

@RequestMapping("/GetHashObjByKey")
    public String GetHashObjByKey() {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
       return hash.get("hash001", "field001").toString();

    }

我们再从程序中把值给读出来,在浏览器输入:http://127.0.0.1:8080/GetHashObjByKey
在这里插入图片描述
一般我们存储一个键,很自然的就会使用 get/set 去存储,实际上这并不是很好的做法。Redis 存储一个 key 会有一个小内存,不管你存的这个键多小,都不会低于这个内存,因此合理的使用 Hash 可以帮我们节省 很多内存。Hash Set 就在哈希表 Key 中的域(Field)的值设为 value。如果 Key 不存在,一个新的哈希表被创建并进行 HSET 操作;如果域(field)已经存在于哈希表中,旧值将被覆盖。根据上面测试用例发现,Hash set 的时候需要传入三个参数,第一个为 key,第二个为 field,第三个为存储的值。一般情况下 Key 代表一组数据,field 为 key 相关的属性,而 value 就是属性对应的值。

List
我们来存储List再取出来看看

    @RequestMapping("/SetListObjByKey")
    public void SetListObjByKey() {
        ListOperations<String, String> list = redisTemplate.opsForList();
        list.leftPush("list001","1");
        list.leftPush("list001","2");
        list.leftPush("list001","3");

        String value=(String)list.leftPop("list001");
        System.out.println("list value :"+value.toString());

        value=(String)list.rightPop("list001");
        System.out.println("list value :"+value.toString());
    }

在这里插入图片描述
可以看到,从左边取的是最后存进去的数据,从右边取是最早存进去的数据。
我们再看RedisDesktopManager
在这里插入图片描述
只有一个2了。这点是要注意的地方。因为数据被取走了。
我们先把刚才存进去的数据删除掉,并且,代码中将取数据的代码注释掉,执行存数据操作,然后我们查看RedisDesktopManager
在这里插入图片描述
你会发现,数据从上到下是3,2,1。从上到下,理解为从左到右。它还提供一种按范围的读取数据方式


    @RequestMapping("/GetListObjByRange")
    public void GetListObjByRange() {
        ListOperations<String, String> list = redisTemplate.opsForList();
        List<String> values=list.range("list001",1,2);
        for (String v:values)
        {
            System.out.println(v);
        }
    }

在这里插入图片描述
range中的参数分别为key,起始位置,终止位置。从上面可以看到,使用 List 可以轻松的实现一个队列, List 典型的应用场景就是消息队列,可以利用 List 的 Push 操作,将任务存在 List 中,然后工作线程再用 POP 操作将任务取出进行执行。

Set
如果说我们对数据还有多一点的要求,那就是去重了,前面的例子都没有办法自动实现,但是Set却可以做到这点。我们来写一个例子

    @RequestMapping("SetMSetObj")
    public void SetMSetObj() {
        String key="csdnSet001";
        SetOperations<String, String> set = redisTemplate.opsForSet();
        set.add(key,"2");
        set.add(key,"0");
        set.add(key,"6");
        set.add(key,"5");
        set.add(key,"2");
        set.add(key,"3");
        set.add(key,"6");
        set.add(key,"6");
        Set<String> values=set.members(key);

        for (String v:values){
            System.out.println("set value :"+v);
        }
       
    }

运行项目,在浏览器输入:http://127.0.0.1:8080/SetMSetObj
在这里插入图片描述
从RedisDesktopManager可以看到(记得要先Reloda才能查看)
在这里插入图片描述
上面的数据看起来,有个问题是,可能你心中所想,这顺序不对,应该是:2 0 6 5 3,但结果却是 2 6 0 5 3。因为它不是List,不要用List来认为它是应该怎么排序。上面的数据,都被去重了。Set还有更多的应用场景,我们来看看它的关于两个集合比较的问题


    @RequestMapping("/SetDifferenceObj")
    public void SetDifferenceObj() {
        SetOperations<String, String> set = redisTemplate.opsForSet();
        String key1 = "key1";
        String key2 = "key2";
        set.add(key1, "1");
        set.add(key1, "2");
        set.add(key1, "3");
        set.add(key1, "4");
        set.add(key2, "5");
        set.add(key2, "4");

        //第一个集合中的元素在第二个集合中不存在
        Set<String> diffs = set.difference(key1, key2);
        for (String v : diffs) {
            System.out.println("diffs set value :" + v);
        }
    }

在这里插入图片描述
这里输出结是第一个集合中的元素在第二个集合中不存在。如果我们把刚才的两个合起来,会怎么样


    @RequestMapping("/SetUnionObj")
    public void SetUnionObj() {
        SetOperations<String, String> set = redisTemplate.opsForSet();
        String key1 = "key1";
        String key2 = "key2";

        Set<String> alls = set.union(key1, key2);
        for (String v : alls) {
            System.out.println("All value :" + v);
        }
    }

在这里插入图片描述
如果,我们希望,它是有序的,应该怎么做呢?

  @RequestMapping("/SetOrderObj")
    public void SetOrderObj(){
        String key="csdn201907";
        redisTemplate.delete(key);
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
        zset.add(key,"key001",3);
        zset.add(key,"key002",2);
        zset.add(key,"key003",1);
        zset.add(key,"key004",4);

        Set<String> zsets=zset.range(key,0,3);
        for (String v:zsets){
            System.out.println("zset value :"+v);
        }

        Set<String> zsetB=zset.rangeByScore(key,0,3);
        for (String v:zsetB){
            System.out.println("zsetB value :"+v);
        }
    }

在这里插入图片描述
可以从上面结果看出,add最后定义的是顺序,rangeByScore按顺序取。
以上就是对Redis的基本操作了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值