Redis-基础

94 阅读14分钟

课程内容

  • Redis入门

  • Redis数据类型

  • Redis常用命令

  • 在Java中操作Redis

1. 前言

1.1 什么是Redis

redis是一款非关系型数据库,redis存储的数据是在内存中的。特点:可以快速读取到数据,因为存储的数据是在内存中的。Redis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件,它是「Remote Dictionary Service」的首字母缩写,也就是「远程字典服务」。

  • 基于内存存储,读写性能高

  • 适合存储热点数据(热点商品、资讯、新闻),一般数据一般变化频率比较低,或者是高并发请求数据

  • 企业应用广泛

1.2 使用Redis能做什么

  • 数据缓存

  • 消息队列

  • 注册中心

  • 发布订阅

2. Redis入门

2.1 Redis简介

Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache, and message broker. 翻译为:Redis是一个开源的内存中的数据结构存储系统,它可以用作:数据库、缓存和消息中间件。

官网:redis.io

Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供的数据是可以达到100000+的QPS(每秒内查询次数)。它存储的value类型比较丰富,也被称为结构化的NoSql数据库。

NoSql(Not Only SQL),不仅仅是SQL,泛指非关系型数据库。NoSql数据库并不是要取代关系型数据库,而是关系型数据库的补充。

关系型数据库(RDBMS):

  • Mysql

  • Oracle

  • DB2

  • SQLServer

非关系型数据库(NoSql):

  • Redis

  • Mongo db

  • MemCached

2.2 Redis下载与安装

2.2.1 Redis下载

Redis安装包分为windows版和Linux版:

下载后得到下面安装包:

2.2.2 Redis安装

1)在Linux中安装Redis

在Linux系统安装Redis步骤:
  1. 将Redis安装包上传到Linux到soft目录
  2. 解压安装包
cd /soft
tar -xvf redis-4.0.0.tar.gz -C /usr/local
  1. 因为已经自带了,可以不安装(安装Redis的依赖环境gcc,命令:yum install gcc-c++)
  2. 进入/usr/local/redis-4.0.0,进行编译,命令:make
cd /usr/local/redis-4.0.0
make
  1. 进入redis的src目录进行安装,命令:make install
cd src
make install

  1. 进入/usr/local/redis-4.0.0 ,把redis.conf文件拷贝到src目录中
# 现在src目录下,复制上一级的文件到当前目录下
cp ../redis.conf .

运行Redis

在src目录下:

./redis-server redis.conf

按ctrl+c退出redis服务器

以后台的方式执行Redis
  1. 修改redis.conf文件,让其在后台启动不要霸屏的方式启动,将配置文件中的daemonize配置项改为yes,默认值为no。用其它的文本编辑器也可以,修改redis.conf文件,136行,将no改成yes
daemonize yes
  1. redis默认是没有密码的,如果你需要有密码,将配置文件中的 # requirepass foobared 配置项取消注释,默认为注释状态。foobared为密码,可以根据情况自己指定。 (选做)
  2. 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
  1. redis的服务默认只是允许本机连接,其他机器默认情况是不被允许连接,如果允许其他机器也能连接linux的reids服务,那么需要修改bind 127.0.0.1  你自己的linux机器的ip地址
69行
bind 127.0.0.1 192.168.xxx.xxx
  1. 启动redis的服务
# 启动
./redis-server redis.conf
# 显示进程
ps -ef | grep redis
  1. 启动客户端去连接服务端测试: 启动客户端的方式:
    1. 方式一(没有密码方式):  在src目录中  ./redis-cli

  1. 方式二(如果存在密码情况): 在src目录中:  ./redis-cli  -h 127.0.0.1   -p 端口号  -a 密码

  1. 退出Redis

# 退出
./redis-cli shutdown
# 查看进程
ps -ef | grep redis

2)在Windows中安装Redis

Redis的Windows版属于绿色软件,直接解压即可使用,解压后目录结构如下:

2.3 Redis服务启动与停止

1)Linux系统中启动和停止Redis

执行Redis服务启动脚本文件redis-server:

通过启动日志可以看到,Redis默认端口号为6379。

Ctrl + C停止Redis服务

通过redis-cli可以连接到本地的Redis服务,默认情况下不需要认证即可连接成功。

退出客户端可以输入exit或者quit命令。

2)Windows系统中启动和停止Redis

在控制台执行redis-server redis.

Ctrl + C停止Redis服务

双击redis-cli.exe即可启动Redis客户端,默认连接的是本地的Redis服务,而且不需要认证即可连接成功。

退出客户端可以输入exit或者quit命令。

2.5 redis客户端程序

在今天的资料里面有redis的图形化界面工具,只需要双击不断下一步即可

连接redis服务器

3. Redis数据类型

3.1 介绍

Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常用的数据类型:

  • 字符串 string

  • 哈希 hash

  • 列表 list

  • 集合 set

  • 有序集合 sorted set / zset

3.2 Redis 5种常用数据类型

解释说明:

字符串(string):普通字符串,常用

哈希(hash):适合存储对象

列表(list):按照插入顺序排序,可以有重复元素

集合(set):无序集合,没有重复元素

有序集合(sorted set / zset):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素

4. Redis常用命令

4.1 字符串string操作命令

Redis 中字符串类型常用命令:

  • SET key value         设置指定key的值

  • GET key                                        获取指定key的值

  • SETEX key seconds value         设置指定key的值,并将 key 的过期时间设为 seconds 秒

  • SETNX key value 只有在 key    不存在时设置 key 的值

更多命令可以参考Redis中文网:www.redis.net.cn

4.2 哈希hash操作命令

Redis hash 是一个string类型的 field 和 value 的映射表,hash特别适合用于存储对象,常用命令:

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

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

  • HDEL key field                       删除存储在哈希表中的指定字段

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

  • HVALS key                              获取哈希表中所有值

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

命令演示

  1. 创建hash类型的键为user,并且添加一个字段为username,值为newboy

  2. 向user中添加字段为age,值为18

  3. 分别得到user中的username和age的字段值

  4. 向user中同时添加多个字段和值,birthday 2018-01-01 sex male

  5. 同时取得多个字段:age 和 sex

  6. 得到user中所有的字段和值

  7. 删除user中的生日和性别字段

4.3 列表list操作命令

Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:

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

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

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

  • LLEN key                                        获取列表长度

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

操作效果

4.4 集合set操作命令

Redis set 是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据,常用命令:

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

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

  • SCARD key                                                  获取集合的成员数

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

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

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

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

效果

创建两个集合:city和cities

4.5 有序集合sorted set操作命令

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

常用命令:

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

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

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

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

操作

  1. 添加键country,分数是10,值是Japan

  2. 添加键country,分数是5,值是USA,添加键country,分数是50,值是Russian

  3. 添加键country,分数是1,值是China,分数是120,值是Korea

  4. 查询country中所有的元素

  5. 查询Japan的索引号(从0开始)

  6. 删除值为USA的元素

  7. 查询country中还有多少个元素

  8. 显示Russian的分数值

4.6 通用命令

Redis中的通用命令,主要是针对key进行操作的相关命令:

  • KEYS pattern  查找所有符合给定模式( pattern)的 key

  • EXISTS key  检查给定 key 是否存在

  • TYPE key  返回 key 所储存的值的类型

  • TTL key  返回给定 key 的剩余生存时间(TTL, time to live),以秒为单位

  • DEL key  该命令用于在 key 存在是删除 key

命令演示

  1. 显示所有的键

  2. 添加字符串man的值为Jack

  3. 从左边添加list的键是women,值是:Rose Mary Kate

  4. 添加一个字符串:person NewBoy

  5. 显示所有的键

  6. 一次删除women和man这两个键

  7. 分别判断person和women是否存在

  8. 分别判断person user myset mylist分别是什么类型

5. 在Java中操作Redis

5.1 介绍

前面我们讲解了Redis的常用命令,这些命令是我们操作Redis的基础,那么我们在java程序中应该如何操作Redis呢?这就需要使用Redis的Java客户端,就如同我们使用JDBC操作MySQL数据库一样。

Redis 的 Java 客户端很多,官方推荐的有三种:

  • Jedis

  • Lettuce

  • Redisson

Spring 对 Redis 客户端进行了整合,提供了 Spring Data Redis,在Spring Boot项目中还提供了对应的Starter,即 spring-boot-starter-data-redis。

5.2 Jedis

Jedis 是 Redis 的 Java 版本的客户端实现。

maven坐标:

<dependencies>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.8.0</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>
</dependencies>

使用 Jedis 操作 Redis 的步骤:

  1. 获取连接

  2. 执行操作

  3. 关闭连接

示例代码:

package com.itheima.test;

import org.junit.Test;
import redis.clients.jedis.Jedis;

public class AppTest {

    @Test
    public void test01(){
        //1. 获取redis连接的客户端对象
        Jedis jedis = new Jedis("192.168.78.128",6379);
        //2. 存储数据
        jedis.set("name","狗娃");
        //3. 获取数据
        System.out.println("姓名:"+ jedis.get("name"));
        //4. 关闭连接
        jedis.close();

    }
}

5.3 Spring Data Redis

5.3.1 介绍

Spring Data Redis 是 Spring 的一部分,提供了在 Spring 应用中通过简单的配置就可以访问 Redis 服务,对 Redis 底层开发包进行了高度封装。在 Spring 项目中,可以使用Spring Data Redis来简化 Redis 操作。

网址:spring.io/projects/sp…

maven坐标:

<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-redis</artifactId>
	<version>2.4.8</version>
</dependency>

Spring Boot提供了对应的Starter,maven坐标:

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

Spring Data Redis中提供了一个高度封装的类:RedisTemplate,针对 Jedis 客户端中大量api进行了归类封装,将同一类型操作封装为Operation接口,具体分类如下:

  1. ValueOperations:string类型,简单K-V操作

  2. SetOperations:set类型数据操作

  3. ZSetOperations:zset类型数据操作

  4. HashOperations:针对hash类型的数据操作

  5. ListOperations:针对list类型的数据操作

5.3.2 环境搭建

第一步: pom.xml

创建maven项目spring-data-redis-demo,配置pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>spring-data-redis-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <!-- 父工程 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.0</version>
    </parent>

    <dependencies>
        <!-- 测试类 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

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

        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.0</version>
            </plugin>
        </plugins>
    </build>

</project>
第二步:编写启动类
package com.itheima;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RedisApplication {

    public static void main(String[] args) {
        SpringApplication.run(RedisApplication.class,args);
    }

}
第三步:配置application.yml
spring:
  redis:
    # 可以指定Linux或Window下Redis服务器地址
    host: 192.168.78.128
    port: 6379
    # password:  有密码你才写
    database: 0 #redis默认有16个数据库, 操作的数据库是哪个
    jedis:
      pool:
        max-active: 10 #最大链接数,连接池中最多有10个
        max-idle: 5   # 最大空闲数,多余的释放
        min-idle: 1   #最小空闲数
        #举个例子:连接池初始化3个链接, 客户拿走2个链接,空闲1个,达到最小的空闲数,必须马上增加
        max-wait: 200ms #连接池最大阻塞等待时间

解释说明:

spring.redis.database:指定使用Redis的哪个数据库,Redis服务启动后默认有16个数据库,编号分别是从0到15。

可以通过修改Redis配置文件来指定数据库的数量。

第四步:提供配置类

springdata_redis默认帮我们创建的RedisTemplate对象是使用jdk的序列号器帮我们键与值存储到redis中,而jdk的序列号器对键与值是采用二进制的方式存储的,所以我们会看到乱码的情况。如果我们需要看得懂,那么需要修改redistemplate使用的序列号器才行。

  • 通过键使用字符串类型的序列化器:StringRedisSerializer

  • 值使用JSON类型的序列化器:GenericJackson2JsonRedisSerializer

  • 但因为在项目中LocalDateTime类型,GenericJackson2JsonRedisSerializer支持不理想,这里选择阿里巴巴的GenericFastJsonRedisSerializer,可以完美支持。

package com.itheima.config;

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        //创建模板对象
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        //设置连接工厂
        redisTemplate.setConnectionFactory(factory);
        //设置键的序列化器:字符串的序列化器StringRedisSerializer
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //设置值的序列化器:使用fastjson进行值的序列化器
        redisTemplate.setValueSerializer(new GenericFastJsonRedisSerializer());
        return redisTemplate;
    }
}

解释说明:

Spring Boot 框架会自动装配 RedisTemplate 对象,但是需要注入RedisConnectionFactory对象,所有这个方法不能省略,默认的key序列化器为JdkSerializationRedisSerializer,导致我们存到Redis中后的数据和原始数据有差别。JdkSerializationRedisSerializer要求实体对象要有序列化接口Serializable。

第五步:提供测试类
package com.itheima.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
public class RedisTest {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
}

5.3.3 操作String类型

set() 添加键和值
get() 获取值
setIfAbsent() 如果不存在这个键就添加
package com.itheima.test;

import com.itheima.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

@SpringBootTest
public class RedisTest {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 使用字符串类型
     */
    @Test
    public void testString() {
        System.out.println("模板对象:" + redisTemplate);
        //1. 获取字符串操作对象
        ValueOperations opsForValue = redisTemplate.opsForValue();
        //2. 添加字符串
        opsForValue.set("book", "人鬼情喂鸟");
        //3. 获取字符串
        String book = (String) opsForValue.get("book");
        System.out.println("书籍:" + book);

        //4. 设置字符串,过期时间15秒
        opsForValue.set("product","娃娃", 15, TimeUnit.SECONDS);
        System.out.println("产品:" + opsForValue.get("product"));

        //5. 如果不存在才添加
        opsForValue.setIfAbsent("book", "西游记");
        //获取字符串
        book = (String) opsForValue.get("book");
        System.out.println("书籍:" + book);

        //6.添加的值是User对象
        User user = new User(100, "孙悟空", LocalDateTime.now());
        opsForValue.set("kong", user);

        user = (User) opsForValue.get("kong");
        System.out.println(user);
    }
}

对象类型的序列化和反序列化

package com.itheima.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private Integer id;
    private String name;
    private LocalDateTime birthday;

}

Redis中内容

5.3.4 操作Hash类型数据

put() 添加键,字段,值
get() 通过键,字段获取值
keys() 获取所有的字段
values() 获取所有的值
entries() 获取所有的键和值
/**
  * 使用hash类型
  */
@Test
public void testHash() {
    //获取hash类型的操作对象
    HashOperations<String, Object, Object> opsForHash = redisTemplate.opsForHash();
    //添加三个值
    opsForHash.put("user", "name", "张三");
    opsForHash.put("user", "age", 20);
    opsForHash.put("user", "sex", true);

    //获取其中一个值
    Object age = opsForHash.get("user", "age");
    System.out.println("年龄:" + age);

    //获取所有的键
    Set<Object> keys = opsForHash.keys("user");
    System.out.println(keys);

    //获取所有的值
    List<Object> values = opsForHash.values("user");
    System.out.println(values);

    //获取所有键和值
    Map<Object, Object> user = opsForHash.entries("user");
    System.out.println(user);
}

5.3.5 操作List类型数据

leftPushAll() 从左边添加多个元素
range() 获取指定范围所有的元素
size() 获取列表的长度
leftPop() 删除最左边的元素
/**
 * 使用list类型
 */
@Test
public void testList() {
    //获取操作列表的对象
    ListOperations<String, Object> opsForList = redisTemplate.opsForList();
    //添加多个元素到列表中
    opsForList.leftPushAll("books", "红楼梦", "黑楼梦", "青楼梦");
    //获取所有的元素
    List<Object> books = opsForList.range("books", 0, -1);
    System.out.println(books);
    //获取元素个数
    Long size = opsForList.size("books");
    System.out.println("长度:" + size);
    //删除最左边的元素
    String book = (String) opsForList.leftPop("books");
    System.out.println("删除的元素是:" + book);
}

5.3.6 操作Set类型数据

add() 添加一个或多个元素
members() 显示所有的成员
remove() 删除其中一个元素
/**
 * 使用set类型
 */
@Test
public void testSet() {
    SetOperations<String, Object> opsForSet = redisTemplate.opsForSet();
    //添加多个元素
    opsForSet.add("persons", "白骨精", "红骨精", "排骨精");
    //获取所有的元素
    Set<Object> persons = opsForSet.members("persons");
    System.out.println(persons);
    //删除其中一个元素
    opsForSet.remove("persons", "排骨精");
}

5.3.7 操作ZSet类型

add() 添加一个或多个元素,同时指定值和分数
range() 获取指定范围的所有元素
incrementScore() 给某个元素加分
remove() 删除指定的元素
/**
 * 使用zset类型
 */
@Test
public void testZset() {
    ZSetOperations<String, Object> opsForZSet = redisTemplate.opsForZSet();
    //添加元素
    opsForZSet.add("cities", "上海", 3000);
    opsForZSet.add("cities", "北京", 1000);
    opsForZSet.add("cities", "广州", 5);
    //获取所有的元素
    Set<Object> cities = opsForZSet.range("cities", 0, -1);
    System.out.println(cities);
    //给北京加500
    opsForZSet.incrementScore("cities", "北京", 500);
    //删除指定的元素
    opsForZSet.remove("cities", "上海");
}

5.3.8 通用操作

keys() 获取指定的键
delete() 删除键和值
hasKey() 判断是否存在某个键
type() 获取值的类型
/**
 * 使用通用方法
 */
@Test
public void testOthers() {
    //使用模板对象的方法
    Set<String> keys = redisTemplate.keys("c*");
    System.out.println(keys);
    //删除指定的键和值
    redisTemplate.delete("company");
    //判断某个值是否存在
    Boolean exists = redisTemplate.hasKey("cities");
    System.out.println("键是否存在:" + exists);
    //获取某个值的类型
    DataType type = redisTemplate.type("country");
    System.out.println("类型:" + type);
}