文章目录

1 Redis简介

Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value的NoSQL(Not Only SQL)数据库。

SQL (Struct Query Lanauge 结构化的查询语言) 引申含义 RDB产品,传统的关系型数据库,存储格式化的表格数据。
Not Only SQL 不仅仅只有关系型数据库 引申含义:非关系型数据库。存储半格式化和非格式化的数据,如k-v、json、xml

1.1 关系型数据库的特点

关系型数据库特点:

  • 存储结构化数据
  • 强事务
  • 在磁盘中存储数据
  • 数据量到达一定量级,查询性能就会出现瓶颈
  • 支持SQL

1.2 Redis的特点

Redis的特点:

  • 存储非结构化的数据:k-v方式存储数据
  • 弱事务,但一般能保证最终一致
  • 在内存中存储数据,但能自动持久化
  • 查询性能非常好
  • 不支持SQL,需要使用特定的命令
  • 支持集群、数据分片,扩展容易

Redis就是一个在内存中,存储k-v格式数据,支持自动持久化的NoSQL型数据库。

1.3 Redis开发中的应用

典型应用:

  • 需要持久化,但对事务要求不高的数据:购物车
  • 充当应用中的缓存
  • 对tomcat集群的session进行管理

2 Redis的安装

1. 上传 redis-3.x.x.tar.gz 到linux虚拟机的 /opt 文件夹

2. 安装gcc

   [root@localhost ~]# yum install -y gcc

3. 解压缩 redis-3.x.x.tar.gz

   [root@localhost opt]# tar xzvf redis-3.2.9.tar.gz

4. 进入到redis根目录,进行编译、安装

   [root@localhost opt]# cd redis-3.2.9
   [root@localhost redis-3.2.9]# make
   [root@localhost redis-3.2.9]# make install

5. 将 redis-3.x.x/redis.conf 复制到 /etc/redis/目录下

   [root@localhost redis-3.2.9]# mkdir -p /etc/redis/
   [root@localhost redis-3.2.9]# cp redis.conf /etc/redis/

6. 启动redis

   [root@localhost redis-3.2.9]# redis-server /etc/redis/redis.conf 
   6255:M 08 Jun 00:19:28.368 * Increased maximum number of open files to 10032 (it was originally set to 1024).
                   _._                                                  
              _.-``__ ''-._                                             
         _.-``    `.  `_.  ''-._           Redis 3.2.9 (00000000/0) 64 bit
     .-`` .-```.  ```\/    _.,_ ''-._                                   
    (    '      ,       .-`  | `,    )     Running in standalone mode
    |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
    |    `-._   `._    /     _.-'    |     PID: 6255
     `-._    `-._  `-./  _.-'    _.-'                                   
    |`-._`-._    `-.__.-'    _.-'_.-'|                                  
    |    `-._`-._        _.-'_.-'    |           http://redis.io        
     `-._    `-._`-.__.-'_.-'    _.-'                                   
    |`-._`-._    `-.__.-'    _.-'_.-'|                                  
    |    `-._`-._        _.-'_.-'    |                                  
     `-._    `-._`-.__.-'_.-'    _.-'                                   
         `-._    `-.__.-'    _.-'                                     
             `-._        _.-'                                       
                 `-.__.-'                                               

3 Redis客户端的使用

在xshell中复制一个连接虚拟机的窗口。

1. 打开客户端

   [root@localhost ~]# redis-cli 
   127.0.0.1:6379> 

2. 关闭redis数据库

   127.0.0.1:6379> shutdown
   not connected> 

3. 关闭客户端

   not connected> exit
   [root@localhost ~]# 

4. 让数字自增和自减

   set age 20  //添加一个键age值为20
   incr age   //让age值自增 age就变成了21
   decr age   //让age值自减 age又变成了20

5. redis存储数据的有效时长

   set age 20
   expire age 60  //让age的有效时长为60秒,超过60秒该数据就失效了。
   ttl age    //查看该键age还有多长有效时长,单位是秒。-1永久有效。-2失效。
   persist age  //让age重新永久有效

String类型数据的操作

Redis是k-v数据格式的数据库,v有多种类型,下面演示v为字符串的操作。

1. 增

   127.0.0.1:6379> set name zhangsan
   OK
   127.0.0.1:6379> get age
   "18"

2. 查

   查看所有的key
   127.0.0.1:6379> keys *
   1) "age"
   2) "name"
   127.0.0.1:6379> get name
   "zhangsan"
   127.0.0.1:6379> get age
   "18"

3. 改

   127.0.0.1:6379> set name lisi
   OK
   127.0.0.1:6379> set age 16
   OK
   127.0.0.1:6379> get name
   "lisi"
   127.0.0.1:6379> get age
   "16"

4. 删

   127.0.0.1:6379> del name
   (integer) 1
   127.0.0.1:6379> del age
   (integer) 1
   127.0.0.1:6379> get name
   (nil)
   127.0.0.1:6379> get age
   (nil)
   127.0.0.1:6379> flushdb  
   OK  清除所有数据

注意:

  • 字符串外的单引号或双引号可以省略
  • 命令结束直接回车,不要添加分号

4 Java操作Redis

和在Java中操作关系型数据库类似,也可以在Java中操作Redis数据库。Jedis是Redis官方推荐的Java连接开发工具,通过Jedis操作Redis数据库。

准备工作:关闭linux虚拟机防火墙

systemctl stop firewalld

编辑 /etc/redis/redis.conf

bind 127.0.0.1 改为如下所示:
bind 127.0.0.1 redis服务器的ip
比如:
bind 127.0.0.1 192.168.84.141

重启redis服务让配置文件生效。

对jedis进行测试

  1. 导入依赖


    redis.clients
    jedis
    2.9.0

  2. 编码
    @Test
    public void testSet(){
    //创建连接
    // ip为redis所在机器的地址
    // 端口号为redis的启动时显示的服务端口
    Jedis jedis = new Jedis(“192.168.84.141”,6379);
    //执行操作
    jedis.set(“name”, “zhangsan”);
    jedis.set(“age”, “18”);
    //释放资源
    jedis.close();
    }

    @Test
    public void testGet(){
        //创建连接
        Jedis jedis = new Jedis("192.168.84.141", 6379);
        //执行操作
        String name = jedis.get("name");
        System.out.println("name = " + name);
        String age = jedis.get("age");
        System.out.println("age = " + age);
        //释放资源
        jedis.close();
    }
    
    @Test
    public void testDel(){
        //创建连接
        Jedis jedis = new Jedis("192.168.84.141", 6379);
        //执行操作
        jedis.del("name");
        jedis.del("age");
        //释放资源
        jedis.close();
    }
    

5 JedisUtils工具类

为提升Jedis操作Redis的性能,可以使用JedisPool连接池来管理jedis对象。

JedisPool的使用:

@Test
public void testPool(){
    //创建连接池的配置对象
    JedisPoolConfig config = new JedisPoolConfig();
    //从连接池获取连接时,是否检测连接的有效性
    config.setTestOnBorrow(true);
    //可用连接实例的最大数目,默认值为8
    config.setMaxTotal(100);
    //最大空闲连接数, 默认8个
    config.setMaxIdle(10);
    //最小空闲连接数,默认8个
    config.setMinIdle(8);
    //没有空闲连接时,最大的等待毫秒数
    config.setMaxWaitMillis(60000);

    //创建连接池
    JedisPool pool = new JedisPool(config, "192.168.44.128", 6379);
    //获取连接
    Jedis jedis = pool.getResource();
    //执行操作 
    jedis.set("name", "zhangsan");
    String name = jedis.get("name");
    System.out.println("name = " + name);

    //释放资源
    jedis.close();
}

redis.properties:抽取配置参数

# 最大空闲数量
redis.maxIdle=50
# 最小空闲数量
redis.minIdle=10
# 最大数量
redis.maxTotal=500
# 建立连接最大等待时间,单位毫秒
redis.maxWaitMillis=30000
# 从连接池中获取连接时,是否检查连接的可用性
redis.testOnBorrow=true

# redis机器ip
redis.hostName=192.168.146.20
# redis端口号
redis.port=6379

工具类:

JedisUtils.java:抽取共性操作

public class JedisUtils {
    private static JedisPool pool;
    static{
        //读取redis.properties文件
        InputStream in = JedisUtils.class.getResourceAsStream("/redis.properties");
        Properties env = new Properties();
        try {
            env.load(in);
            in.close();
        }catch(IOException e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        JedisPoolConfig config = new JedisPoolConfig();
        String maxIdle = env.getProperty("redis.maxIdle");
        if (maxIdle != null) {
            config.setMaxIdle(Integer.parseInt(maxIdle));
        }
        String minIdle = env.getProperty("redis.minIdle");
        if(minIdle != null){
            config.setMinIdle(Integer.parseInt(minIdle));
        }
        String maxTotal = env.getProperty("redis.maxTotal");
        if(maxTotal != null){
            config.setMaxTotal(Integer.parseInt(maxTotal));
        }
        String maxWaitMillis = env.getProperty("redis.maxWaitMillis");
        if (maxWaitMillis != null) {
            config.setMaxWaitMillis(Long.parseLong(maxWaitMillis));
        }
        String testOnBorrow = env.getProperty("redis.testOnBorrow");
        if(testOnBorrow != null){
            config.setTestOnBorrow(Boolean.parseBoolean(testOnBorrow));
        }

        String hostName = env.getProperty("redis.hostName");
        String port = env.getProperty("redis.port");

        pool = new JedisPool(config, hostName, Integer.parseInt(port == null ? "6379" : port));
    }

    public static Jedis getJedis(){
        return pool.getResource();
    }

    public static void close(Jedis jedis){
        if(jedis != null){
            jedis.close();
        }
    }
}

5.1 使用jedis工具类实现手机验证码功能(生成6位随机验证码案例)

//生成六位的手机随机验证码
public static String randomCode(){
    Random random=new Random();
    StringBuilder sb=new StringBuilder();
    for(int i=0;i<6;i++) {
        int randomNum = random.nextInt(10);
        sb.append(randomNum);
    }
    return sb.toString();
}

/**
     * 把验证码放入到redis里面,存储60秒,验证是否超过3次。
     * @param phone 手机号码
     */
public static void saveCode(String phone){
    String code = randomCode();//随机手机验证码
    //redis里面存两个键,一个键verifyCode150098776766:code  值是验证码
    //另一个键 verifyCode150098776766:count 今日该手机发送验证码的次数
    String codeKey="verifyCode"+phone+":code";
    String countKey="verifyCode"+phone+":count";
    //获取到数据库连接
    Jedis jedis=JedisUtil.getJedis();
    String countValue = jedis.get(countKey);
    //第一次发送验证码
    if(countValue==null){
        jedis.setex(codeKey,60,code);//设值的同时添加有效时长
        jedis.set(countKey,"1");//表示第一次发出验证码
    }else if(Integer.parseInt(countValue)<3){
        jedis.setex(codeKey,60,code);//设值的同时添加有效时长
        jedis.incr(countKey);
    }else{
        System.out.println("今日验证码次数达到上限3次。不可以在发出验证码了");
    }
    jedis.close();
}

/**
     * 比对验证码
     * @param phone 用户手机号
     * @param userCode 用户输入的验证码
     */
public static void compareCode(String phone,String userCode){
    String codeKey="verifyCode"+phone+":code";
    Jedis jedis=JedisUtil.getJedis();
    String realCode = jedis.get(codeKey);
    if(userCode.equals(realCode)){
        System.out.println("验证通过!!");
    }else{
        System.out.println("验证失败!!!");
    }
    jedis.close();
}

6 序列化和反序列化

对象序列化:将对象的状态信息持久保存的过程。

对象反序列化:根据对象的状态信息恢复对象的过程。

在Redis中有2种常用的方式:字节数组和json串

6.1字节数组

添加依赖:

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.8.1</version>
</dependency>

@Test
public void testJDKSerializer(){
    User user = new User(1, "zhangsan",18,10000.0,new Date());
    //使用commons-lang3中的工具将对象转换为字节数组
    byte[] bs = SerializationUtils.serialize(user);

    Jedis jedis = JedisUtils.getJedis();
    jedis.set("u".getBytes(), bs);

    byte[] bs2 = jedis.get("u".getBytes());
    //使用commons-lang3中的工具将为字节数组转换为对象
    User user2 = SerializationUtils.deserialize(bs2);
    System.out.println("user2 = " + user2);

    JedisUtils.close(jedis);
}

注意:序列化的类型,必须实现Serializable接口

6.2 json串

添加依赖:

<!-- 引入jackson依赖-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.3</version>
</dependency>

@Test
public void testJsonSerializer() throws JsonProcessingException {
    User user = new User(1, "zhangsan",18,10000.0,new Date());
    //使用JSON工具将对象转换为json字符串
    ObjectMapper mapper = new ObjectMapper();
    String userJson = mapper.writeValueAsString(user);

    Jedis jedis = JedisUtils.getJedis();
    jedis.set("u", userJson);

    String userJson2 = jedis.get("u");

    //使用JSON工具将json字符串转换为对象
    User user2 = mapper.readValue(userJson2, User.class);
    System.out.println("user2 = " + user2);

    JedisUtils.close(jedis);

}

因为json格式支持多种编程语法,便于数据在不同的编程语言中传递,实战中json格式更为常见。

7 Redis管理Tomcat集群下的Session

Tomcat集群下session数据丢失的问题:

session是由tomcat创建管理的,集群中tomcat服务器相互独立,session不能共享。

nginx分发同1个用户的多次请求到达不同tomcat服务器时,就会发生session数据的问题。

解决方案:

1. ip黏着

将nginx的负载均衡策略设置为ip_hash.
缺点:1 负载可能不均衡
2 如果tomcat宕机,session的数还是会丢失
2. 借助于Redis数据库完成集群间的session共享

tomcat新建session后,将session以sessionId=session对象的形式写入到redis中。
用户再次发起请求会携带sessionId, 在任何一个tomcat服务器中都可以通过sessionId从redis中得到session。

2 使用Redis完成tomcat集群的session共享

  1. tomcat中添加依赖
    tomcat/lib下添加:
    commons-logging-1.2.jar
    commons-pool2-2.4.2.jar
    jedis-2.9.0.jar
    tomcat-cluster-redis-session-manager-3.0.1.jar

  2. 在tomcat/conf下添加redis-data-cache.properties,并配置redis信息
    #redis.hosts=reids服务ip以及端口号redis.hosts=192.168.232.107:6379#- redis password#redis.password=lolaage_cache 有密码写密码,没有注释就可以

  3. 配置tomcat/conf的context.xml

  4. 重启tomcat,重新测试

8 Redis中的数据结构

在这里插入图片描述

传统键值存储是关联字符串值到字符串键,但是 Redis 的值不仅仅局限于简单字符串,还可以持有更复杂的数据结构。下面列的是 Redis 支持的所有数据结构,后面将逐一介绍:

  • String(字符串)
  • List(列表)
  • Set(集合)
  • Hash(哈希,键值对集合)
  • SortedSet(zset,排序集合)

8.1 String(字符串)

应用场景:粉丝数,投票数

Redis中的String,除了字符串外,还可以存:储数字以及二进制数据。

增:

127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> set age 18
OK
批量添加
127.0.0.1:6379> mset score 100.0 sex M
OK

删:

127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)

改:

数字自增
127.0.0.1:6379> incr age
(integer) 19
指定自增的数值
127.0.0.1:6379> incrby age 2
(integer) 21
数字自减
127.0.0.1:6379> decr age
(integer) 20
指定自减的数值
127.0.0.1:6379> decrby age 2
(integer) 18

查:

127.0.0.1:6379> get age
"18"
批量获取
127.0.0.1:6379> mget score sex
1) "100.0"
2) "M"
127.0.0.1:6379> set name zhangsan
OK
获取值的长度
127.0.0.1:6379> strlen name
(integer) 5

8.2 List(列表)

应用场景:排队。队列

增:

向mylist列表尾部添加多个数据
127.0.0.1:6379> rpush mylist xiao1hei xiao2hei xiao3hei
(integer) 3
向mylist列表头部添加多个数据
127.0.0.1:6379> lpush mylist xiao0hei xiao-1hei
(integer) 5

查:

127.0.0.1:6379> llen mylist
(integer) 5
127.0.0.1:6379> lrange mylist 0 4
1) "xiao-1hei"
2) "xiao0hei"
3) "xiao1hei"
4) "xiao2hei"
5) "xiao3hei"
查看全部,固定写法
lrange mylist 0 -1 

127.0.0.1:6379> lindex mylist 2
"xiao-1hei"

删:

127.0.0.1:6379> lpop mylist
"xiao-1hei"
127.0.0.1:6379> rpop mylist
"xiao3hei"

//lrem list count value
//从list中删除count个value,
//count=0删除全部 count>0从左开始删除 count<0从右开始删除
127.0.0.1:6379> rpush mylist xiao0hei xiao0hei xiao0hei
(integer) 6
127.0.0.1:6379> lrem mylist 3 xiao0hei
(integer) 3

改:

127.0.0.1:6379> lset mylist 0 xiaoheihei
OK
127.0.0.1:6379> lindex mylist 0
"xiaoheihei"

8.3 Set(集合)

应用场景:共同好友

增:

127.0.0.1:6379> sadd myset a b
(integer) 2
127.0.0.1:6379> sadd myset b c d
(integer) 2

查:

127.0.0.1:6379> smembers myset
1) "d"
2) "c"
3) "b"
4) "a"

删:

删除元素
127.0.0.1:6379> srem myset a
(integer) 1
127.0.0.1:6379> sismember myset a
(integer) 0

集合间的运算:

127.0.0.1:6379> sadd myset1 a b c
(integer) 3
127.0.0.1:6379> sadd myset2 b d e
(integer) 3
127.0.0.1:6379> sadd myset3 c d f
(integer) 3

myset1: a b c
myset2: b d e
myset3: c d f
获取多个集合间的差集
127.0.0.1:6379> sdiff myset1 myset2
1) "c"
2) "a"
127.0.0.1:6379> sdiff myset2 myset1
1) "d"
2) "e"
127.0.0.1:6379> sdiff myset1 myset2 myset3
1) "a"
获取多个集合间的交集
127.0.0.1:6379> sinter myset1 myset2
1) "b"
127.0.0.1:6379> sinter myset2 myset3
1) "d"
127.0.0.1:6379> sinter myset1 myset2 myset3
(empty list or set)
获取多个集合间的并集
127.0.0.1:6379> sunion myset1 myset2 myset3
1) "d"
2) "b"
3) "e"
4) "f"
5) "c"
6) "a"

8.4 Hash(键值对集合)

应用场景:存储有关联的数据,对象的另一种存放。

增:

127.0.0.1:6379> hset myhash name zhangsan
(integer) 1
127.0.0.1:6379> hmset myhash age 18 sex m
OK

不存在某个key时才添加
127.0.0.1:6379> hsetnx myhash name lisi
(integer) 0
127.0.0.1:6379> hsetnx myhash phone 18530031576
(integer) 1

查:

127.0.0.1:6379> hget myhash name
"zhangsan"
127.0.0.1:6379> hmget myhash name age sex
1) "zhangsan"
2) "18"
3) "m"

删:

127.0.0.1:6379> hdel myhash phone
(integer) 1
127.0.0.1:6379> hget myhash phone
(nil)

8.5 SortedSet(ZSet 排序集合)

应用场景:排行榜

增:

127.0.0.1:6379> zadd mysortset 18 zhaoxs 20 fanmw 22 liuyh 30 wangmj
(integer) 4

查:

获取特定元素的分数
127.0.0.1:6379> zscore mysortset zhaoxs
"18"
获取排序后指定下标范围的元素
127.0.0.1:6379> zrange mysortset 0 2
1) "zhaoxs"
2) "fanmw"
3) "lisih"
获取所有元素
127.0.0.1:6379> zrange mysortset 0 -1
1) "zhaoxs"
2) "fanmw"
3) "lisih"
4) "wangmj"
按照分数大小范围获取元素
127.0.0.1:6379> zrangebyscore mysortset 20 22
1) "fanmw"
2) "lisih"
按照分数大小范围获取元素,并进行过滤
127.0.0.1:6379> zrangebyscore mysortset 16 30
1) "zhaoxs"
2) "fanmw"
3) "lisih"
4) "wangmj"
127.0.0.1:6379> zrangebyscore mysortset 16 30 limit 2 2
1) "lisih"
2) "wangmj"

删:

127.0.0.1:6379> zrem mysortset wangmj
(integer) 1
127.0.0.1:6379> zscore mysortset wangmj
(nil)

改:

修改分数
127.0.0.1:6379> zincrby mysortset 2 zhaoxs
"20"
127.0.0.1:6379> zscore mysortset zhaoxs
"20"

注意:redis根据数据类型不同将命令分成不同的组,可以通过help命令获取帮助。

help @string

help @list

help @set

help @hash

help @sorted_set

flushdb:清除数据库里面所有的数据

9 Redis中设置key的过期时间

Redis中可以设置数据的过期时间,一旦过期自动删除数据。

1. 设置过期时间

   127.0.0.1:6379> set name ok
   //设置10s后过期,expire单位秒
   127.0.0.1:6379> expire name 10 
   //设置10s后过期,pexpire 单位毫秒
   127.0.0.1:6379> pexpire age 10000
   (integer) 1

2. 查看剩余时间

   查看剩余存活时长,单位秒
   127.0.0.1:6379> ttl name
   (integer) 7
   查看剩余存活时长,单位毫秒
   127.0.0.1:6379> pttl name
   (integer) 4006

3. 取消过期

   127.0.0.1:6379> set age 18
   OK
   127.0.0.1:6379> expire age 20
   (integer) 1
   127.0.0.1:6379> ttl age
   (integer) 15
   取消过期
   127.0.0.1:6379> persist age
   (integer) 1
   ttl返回-1表示没有设置过期时间,返回-2表示数据不存在
   127.0.0.1:6379> ttl age
   (integer) -1
   127.0.0.1:6379> get age
   "18"

应用:手机验证码、黑名单、缓存

10 Springboot操作Redis

Spring的spring-data-redis模块中封装了RedisTemplate对象来进行对Redis的各种操作,它支持所有的Redis原生的api。提供了连接池自动管理、高度统一的Api、灵活的定制化,简化Java操作Redis的编码工作。

10.1 第1个RedisTemplate示例

  1. 导入依赖

    org.springframework.boot
    spring-boot-starter-web


    org.springframework.boot
    spring-boot-starter-data-redis

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.9.0</version>
    </dependency>
    
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!--springboot做单元测试的依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    
  2. 配置
    application.yml
    spring:
    redis:
    host: 192.168.232.107
    port: 6379
    jedis:
    pool:
    max-active: 500
    max-idle: 50
    min-idle: 10
    max-wait: 30000

  3. 入口类:配置RedisTemplate
    @Configuration
    public class RedisConfig{
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
    RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory);

            StringRedisSerializer stringRedisSerializer= new StringRedisSerializer();
           GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
            redisTemplate.setKeySerializer(stringRedisSerializer);
            redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
            redisTemplate.setHashKeySerializer(stringRedisSerializer);
            redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
            return redisTemplate;
        }  
    }
    
  4. 编码
    @RuntWith(SpringRunner.class)
    @SpringBootTest(classes=RedisDay02Application.class)
    public class RedisTemplateTest{
    @Autowired
    //获取RedisTemplate工具
    private RedisTemplate<String,Object> redisTemplate;
    @Test
    public void testValueSet(){

            //获取操作特定数据类型的operations对象
            ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
            //通过operations对象操作数据
            valueOperations.set("name","zhangsan");
            valueOperations.set("age",18, Duration.ofSeconds(60));
        }
    
        @Test
        public void testValueGet(){
    
            ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
            Object name = valueOperations.get("name");
            Object age = valueOperations.get("age");
            System.out.println("name = " + name);
            System.out.println("age = " + age);
        }
    }
    

10.2 RedisTemplate的常用api

spring-data-redis针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operations接口。根据Redis的不同数据类型,定义了如下的operations接口:

  • ValueOperations:提供了对String类型的操作
  • ListOperations :提供了对List类型的数据操作
  • SetOperations:提供了对Set类型的数据操作
  • HashOperations:提供了对Map类型的数据操作
  • ZSetOprations:提供了对ZSet类型的数据操作

3.2.1 ValueOperations

  1. 添加:set
    ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    valueOperations.set(“name”,“zhangsan”);//添加
    valueOperations.set(“age”,18, Duration.ofSeconds(60));//添加有时效的数据
    Map<String, Object> map = new HashMap<>();
    map.put(“score”,100.0);
    map.put(“sex”,“男”);
    valueOperations.multiSet(map);//批量添加

  2. 删除:delete
    redisTemplate.delete(“name”);

  3. 修改:set、increment、decremnt
    ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    valueOperations.set(“age”,18);
    valueOperations.increment(“age”);//自增
    valueOperations.increment(“age”,10);//加10
    valueOperations.decrement(“age”);//自减
    valueOperations.decrement(“age”,10);//减10

  4. 查询:get、mget
    ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    Object name = valueOperations.get(“name”);
    Object age = valueOperations.get(“age”);
    System.out.println("name = " + name);
    System.out.println("age = " + age);

    //批量查询
    Collection<String> keys = new ArrayList<String>(0);
    keys.add("score");
    keys.add("sex");
    List<Object> values = valueOperations.multiGet(keys);
    

3.2.2 ListOperations

  1. 添加:push
    ListOperations<String, Object> listOperations = redisTemplate.opsForList();
    listOperations.leftPush(“list”,“1”);
    listOperations.leftPushAll(“list”,“2,”,“3”);

    listOperations.rightPush("list","2");
    listOperations.rightPushAll("list","3","4");
    
  2. 删除:pop
    ListOperations<String, Object> listOperations = redisTemplate.opsForList();
    Object leftPop = listOperations.leftPop(“list”);
    Object rightPop = listOperations.rightPop(“list”);
    System.out.println("leftPop = " + leftPop);
    System.out.println("rightPop = " + rightPop);

  3. 修改:set
    ListOperations<String, Object> listOperations = redisTemplate.opsForList();
    listOperations.set(“list”,1,“new value”);

  4. 查询:index、size、range
    ListOperations<String, Object> listOperations = redisTemplate.opsForList();
    Object value = listOperations.index(“list”, 0);
    Long size = listOperations.size(“list”);
    List list = listOperations.range(“list”, 0, -1);

3.2.3 SetOperations

  1. 添加:add
    SetOperations<String, Object> setOperations = redisTemplate.opsForSet();
    setOperations.add(“set”, “xiao1hei”, “xiao2hei”);

  2. 删除:remove
    SetOperations<String, Object> setOperations = redisTemplate.opsForSet();
    setOperations.remove(“set”, “xiao1hei”);

  3. 集合运算:difference、union、intersect
    SetOperations<String,Object> setOperations = redisTemplate.opsForSet();
    setOperations.add(“set1”,1,2,3,4);
    setOperations.add(“set2”,2,3,4,5);
    setOperations.add(“set3”,3,4,5,6);

    Set<Object> difference = setOperations.difference("set1", "set2");//set1-set2
    Collection<String> keys = new ArrayList<String>();
    keys.add("set1");
    keys.add("set2");
    keys.add("set3");
    Set<Object> union = setOperations.union(keys);//set1 + set2 + set3
    Set<Object> intersect = setOperations.intersect(keys);//求交集
    

3.2.4 HashOperations

  1. 添加:put、putAll
    HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
    hashOperations.put(“map”,“k1”,“v1”);
    Map<String, String> values = new HashMap<>();
    values.put(“k2”, “v2”);
    values.put(“k3”, “v3”);
    hashOperations.putAll(“map”,values);

  2. 删除:
    HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
    hashOperations.delete(“map”,“k1”,“k2”);

  3. 修改:
    HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
    hashOperations.put(“map”,“age”,18);
    hashOperations.increment(“map”,“age”,10);

  4. 查询:
    HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
    Object v1 = hashOperations.get(“map”, “k1”);
    Collection keys = new ArrayList<>();
    keys.add(“k2”);
    keys.add(“k3”);
    List values = hashOperations.multiGet(“map”, keys);
    Boolean hasKey = hashOperations.hasKey(“map”, “k1”);
    Long size = hashOperations.size(“map”);

3.2.5 ZSetOperations

  1. 添加:
    ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
    zSetOperations.add(“zset”,“zhangsan”,100);
    zSetOperations.add(“zset”,“lisi”,60);

  2. 删除:
    zSetOperations.remove(“zset”,“zhangsan”,“lisi”);
    zSetOperations.removeRange(“zset”,0,2);
    zSetOperations.removeRangeByScore(“zset”,50,99.0);

  3. 修改:
    zSetOperations.incrementScore(“zset”, “lisi”, 10.0);

  4. 查询:
    Set zset1 = zSetOperations.range(“zset”, 0, -1);
    Set zset2 = zSetOperations.rangeByScore(“zset”, 40, 100);

     Set<ZSetOperations.TypedTuple<Object>> scores = zSetOperations.rangeWithScores("zset", 0, -1);
    for (ZSetOperations.TypedTuple e:scores) {
        System.out.println(e.getValue()+" "+e.getScore());
    }
    
    Set<ZSetOperations.TypedTuple<Object>> scores2 = zSetOperations.rangeByScoreWithScores("zset", 40, 100);
    for (ZSetOperations.TypedTuple e:scores2) {
        System.out.println(e.getValue()+" "+e.getScore());
    } 
    

11 Redis缓存

11.1 Redis缓存引言

缓存:对于数据库中的热点数据的备份,便于访问,提高应用性能。

单机架构的缓存方案

在这里插入图片描述

EhCache直接将数据缓存在JVM中,速度快,效率高。但是在分布式集群环境下,缓存管理非常麻烦,比如:当修改了一条数据后,必须通知到缓存了该数据的所有缓存。
在这里插入图片描述
解决方案:Redis缓存

Redis基于内存操作,性能远超关系型数据库,可以代替EhCache充当缓存。单独部署Redis服务,应用通过网络和Redis通信,虽效率略低于EhCache,但处理集群分布式缓存有成熟的方案。
在这里插入图片描述

11.2 Redis缓存的使用

Redis缓存的设计思路:

  • 在执行查询时,先查询redis缓存,如果有数据则不在调用dao直接返回;如果查不到,才调用dao,并将数据保存到缓存
  • 在执行增删改后,需要清空缓存,避免脏数据
    在这里插入图片描述
    为避免操作缓存的代码和原始业务代码耦合,不能将操作缓存的代码硬编码到业务方法中。可以将操作Redis缓存的代码定义成Advice(增强),使用AOP的方式动态增强。

4.2.1实战中Spring Boot的Redis缓存配置

事实上,在Springboot中配置缓存无需手写增强类,Springboot对缓存的AOP方式进行了抽取封装,内置了一套开箱即用的缓存机制。

注解:Spring内置了缓存注解

  • Cacheable:用在方法上,执行方法前先查询缓存,如果缓存有数据,直接返回;如果缓存中没有数据,则执行查询方法,并将结果保存到缓存中
  • CacheEvict:用在方法上,可以在方法执行前或后删除缓存

增强类: Spring内置了RedisCacheManager缓存增强类

具体步骤:

  1. 引入依赖。

    org.springframework.boot
    spring-boot-starter-data-redis
    2.1.4.RELEASE

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.9.0</version>
    </dependency>
    
  2. 在目标方法上面使用缓存。
    @CacheEvict(value=“NewsServiceImpl”,allEntries = true,beforeInvocation = true)
    @Cacheable(value=“NewsServiceImpl”,key="#root.methodName+#id")

  3. 在Application启动类上面添加开启缓存注解。
    @EnableCaching//开启缓存
    public class Application {}

  4. 在配置类中对redis连接进行管理。
    //配置缓存管理器
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory){

            RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
    
            return new RedisCacheManager(redisCacheWriter,getRedisCacheConfiguration(20),
                    getRedisCacheConfigurationMap());
        }
    
        private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap(){
            Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
            //以NewsServiceImpl开头的缓存要存储100秒
            redisCacheConfigurationMap.put("NewsServiceImpl", getRedisCacheConfiguration(100));
            return redisCacheConfigurationMap;
        }
        //RedisCacheConfiguration 用于负责Redis的缓存配置
        private RedisCacheConfiguration getRedisCacheConfiguration(int seconds){
            RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
            GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
            return redisCacheConfiguration
                    .serializeValuesWith(
                            RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)
                    )
                    .entryTtl(Duration.ofSeconds(seconds));
        }
    
  5. 在application.yml中对redis服务器进行配置。
    spring:
    redis:
    lettuce:
    pool:
    max-active: 500
    max-idle: 50
    min-idle: 10
    max-wait: 30000
    host: 192.168.232.107
    port: 6379

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐