Redis 基础篇

NoSql 数据库

1. 入门

1.1 NoSQL

SQL 和 NoSQL

关系型数据库 和 非关系型数据库

区别:

1689181370218

(1)结构化和非结构化

  • SQL关系型数据库是结构化数据,每一张表都有严格的约束信息:字段名、字段数据类型、字段约束等等信息,插入的数据必须遵守这些约束
  • NoSql对数据库格式没有严格约束,往往形式松散,自由。可以是key-value,可以是文档,或者图格式

(2)关联和非关联

  • 关系型:

    1689181273477

  • 非关系型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    {
    id: 1,
    name: "张三",
    orders: [
    {
    id: 1,
    item: {
    id: 10, title: "荣耀6", price: 4999
    }
    },
    {
    id: 2,
    item: {
    id: 20, title: "小米11", price: 3999

    }
    }
    ]
    }

(3)查询方式

1689181323406

(4)事务

  • 传统关系型数据库能满足事务ACID的原则 ,而非关系型数据库往往不支持事务,或者不能严格保证ACID的特性,只能实现基本的一致性。

1.2 Redis 安装-尚硅谷

此处主要参考尚硅谷的教程:

10_redis安装和坑排除_哔哩哔哩_bilibili

1.2.1 安装

  • Redis是基于C编写,所以需要先安装Redis所需的gcc依赖,可以用 gcc -v 进行查看验证
1
yum install -y gcc 

1691222953181

  • 官网下载链接:Download | Redis
  • 这里我用的 xftp7 上传到虚拟机的 /opt 目录下,在该目录下进行解压缩
1
tar -zxvf redis-7.0.12.tar.gz

1695350918966

1695351527333

  • 进入 /opt/redis-7.0.12 目录下

1695350999649

  • 执行 make 命令进行编译安装,出现如下即安装成功
1
make && make install

1695351328595

  • 查看默认安装目录:usr/local/bin,其类似于windows系统的 C:\Program Files,因为能帮我们直接配置到 path 里

1695351654713

redis-benchmark:性能测试工具,服务启动后运行该命令,看看自己本子性能如何

redis-check-aof:修复有问题的 AOF 文件,rdb 和 aof 后面讲

redis-check-rdb:修复有问题的 dump.rdb 文件

redis-cli:客户端,操作入口

redis-sentinel:redis 集群使用

redis-server:Redis 服务器启动命令

1.2.2 备份并修改配置文件

  • /opt/redis-7.0.12 目录下,先备份以下自带的配置文件到新建的 /myredis 文件夹(该文件夹在根目录下)下,不要在原件上改,然后再修改拷贝的 redis7.conf 做初始化设置
1
2
mkdir /myredis
cp redis.conf /myredis/redis7.conf

1695353692381

  • 修改 /myredis 目录下 redis.conf 配置文件做初始化设置,配置完记得重启

    1
    2
    cd /myredis
    vim redis7.conf
    • 默认 daemonize no 改为 daemonize yes

    1695359496997

    • 默认 protected-mode yes 改为 protected-mode no

    1695359731730

    • 默认 bind 127.0.0.1 改为 直接注释掉(默认的只能访问本机IP),否则影响远程连接

    1695359794655

    • 添加 redis 密码 改为 requirepass 你自己设置的密码

    1695359933541

    • 最后输入 :wq! 保存并退出

    补充:Vim 的一些用法

    1695359427097

1.2.3 启动并连接服务

  • 启动服务

    1
    2
    redis-server /myredis/redis7.conf
    ps -ef | grep redis | grep -v grep

    1695360200129

    端口 6379 被占用,说明后台启动成功

    ps 用于查看进程

  • 连接服务

    1
    2
    redis-cli -a thr -p 6379
    ps -ef | grep redis | grep -v grep

    1695360294356

    1695360393425

    在 127.0.0.1:6370 窗口下,执行 ping,若结果为 pong 则表示连接成功

    1695360472722

    1695360521774

  • 补充

    • 退出连接:quit

      1695360614134

    • 若连接时不想看见警告,可用:redis-cli -a thr -p 6379 2>/dev/nul

      该操作是将警告重定向至linux的黑洞文件

    • 若想开机自启,则跳转到 1.3.3

1.2.4 测试 hello world

1
2
set k1 helloworld
get k1

1695361038813

1.2.5 关闭 redis

  • 如果已经在 redis 服务器里,就直接用 shutdown 即可,可用 lsof -i:6379 验证

1695361220609

1695361266946

  • 单实例关闭
1
redis-cli -a thr shutdown

1695361371095

  • 多实例关闭,指定端口关闭
1
redis-cli -p 6379 shutdown

1.2.6 卸载 redis

  • 先停止 redis-server 服务
1
redis-cli -a thr shutdown
  • 删除 /usr/local/lib 目录下与 redis 相关的文件
1
2
ls -l /usr/local/bin/redis-*
rm -rf /usr/local/bin/redis-*

1.3 Redis 安装启动-黑马

基础篇-04.初识Redis-安装Redis及启动的三种方式_哔哩哔哩_bilibili

redis的启动方式有很多种,例如:

  • 默认启动
  • 指定配置启动
  • 开机自启

1.3.1 默认启动

  • 安装完成后,在任意目录输入 redis-server 命令即可启动 Redis:

1695362425115

  • 这种启动属于前台启动,会阻塞整个会话窗口,需要重开一个窗口才可以进行连接,窗口关闭或者按下 CTRL + C 则Redis停止,不推荐使用。

1.3.2 指定配置启动

  • 如果要让Redis以后台方式启动,则必须修改Redis配置文件,就在我们之前解压的redis安装包下,叫redis.conf
  • 先备份:cp redis.conf redis.conf.bck

1695363322243

  • 然后修改redis.conf文件中的一些配置:
1
2
3
4
5
6
# 允许访问的地址,默认是127.0.0.1,会导致只能在本地访问。修改为0.0.0.0则可以在任意IP访问,生产环境不要设置为0.0.0.0
bind 0.0.0.0
# 守护进程,修改为yes后即可后台运行
daemonize yes
# 密码,设置后访问Redis必须输入密码
requirepass thr
  • Redis的其它常见配置:
1
2
3
4
5
6
7
8
9
10
# 监听的端口
port 6379
# 工作目录,默认是当前目录,也就是运行redis-server时的命令,日志、持久化等文件会保存在这个目录
dir .
# 数据库数量,设置为1,代表只使用1个库,默认有16个库,编号0~15
databases 1
# 设置redis能够使用的最大内存
maxmemory 512mb
# 日志文件,默认为空,不记录日志,可以指定日志文件名
logfile "redis.log"
  • 启动Redis:
1
2
3
4
# 进入redis安装目录 
cd /usr/local/src/redis-6.2.6
# 启动
redis-server redis.conf
  • 停止服务:
1
2
3
# 利用redis-cli来执行 shutdown 命令,即可停止 Redis 服务,
# 因为之前配置了密码,因此需要通过 -u 来指定密码
redis-cli -u 123321 shutdown

1.3.3 开机自启

这个地方需要跟着做一下

  • 首先,新建一个系统服务文件:
1
vi /etc/systemd/system/redis.service
  • 内容如下:
1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=redis-server
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-6.2.6/redis.conf
PrivateTmp=true

[Install]
WantedBy=multi-user.target

重点在这一行,ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-6.2.6/redis.conf

前者是安装目录不变,后者需改成配置文件所在位置(由于我是根据尚硅谷的教程做的,所以配置文件所在位置为 /myredis/redis7.conf)

1695363918452

  • 然后重载系统服务:
1
systemctl daemon-reload
  • 现在,我们可以用下面这组命令来操作redis了:
1
2
3
4
5
6
7
8
# 启动
systemctl start redis
# 停止
systemctl stop redis
# 重启
systemctl restart redis
# 查看状态
systemctl status redis

1695364136785

  • 执行下面的命令,可以让redis开机自启:
1
systemctl enable redis

可以通过 ps -ef | grep redis | grep -v grep 查看是否开启 redis

1695364274819

1.4 Redis 客户端

安装完成Redis,我们就可以操作Redis,实现数据的CRUD了。这需要用到Redis客户端,包括:

  • 命令行客户端
  • 图形化桌面客户端
  • 编程客户端

1.4.1 命令行客户端

这个在 1.2.3 有详细图解

  • Redis安装完成后就自带了命令行客户端:redis-cli,使用方式如下:
1
redis-cli [options] [commonds]
  • 其中常见的options有:
    • -h 127.0.0.1:指定要连接的redis节点的IP地址,默认是127.0.0.1
    • -p 6379:指定要连接的redis节点的端口,默认是6379
    • -a 123321:指定redis的访问密码
  • 其中的commonds就是Redis的操作命令,例如:
    • ping:与redis服务端做心跳测试,服务端正常会返回pong
    • 不指定commond时,会进入redis-cli的交互控制台:

1.4.2 图形化桌面客户端

1695364974771

  • 下载安装即可,然后点击连接 redis 服务器

1695365346988

  • 连不上,尝试关闭防火墙后成功连接
1
2
3
4
systemctl status firewalld  # 查看防火墙状态
systemctl stop firewalld # 关闭防火墙
systemctl start firewalld # 启动防火墙
systemctl restart firewalld # 重启防火墙(先停止,再启动)

1695366089558

1695366106875

一共有0-15共16个库

1.4.3 测试一下

  • 命令行客户端
1
2
3
4
5
redis-cli -a thr  # 先进入命令行客户端
set name thr # 添加一个键值对(默认添加在0号库)
set age 20 # 再添加一个键值对
select 0 # 进入0号库
get name # 获取键为 name 的值

1695366260657

  • 图形化客户端

    • 可以看到刚刚添加的数据

    1695366449724

    • 手动在图形化界面也可添加

    1695366506729

    • 同时在命令行中也可以查到

    1695366538405

2. Redis 常见命令

官方文档里的常见命令:Commands | Redis ,可以分组查找相关的命令

也可在命令行里用 help @xxx 进行查找,如,help @string

2.1 Redis 数据结构介绍

  • Redis 是 key-value键值对 的数据库,一般 key 是 String,不过value 数据结构是多种多样的,下面的前五种是比较常见的数据结构:

1695367173429

2.2 Redis 通用命令

在命令行中可以用 help @generic 查询

通过 help [command] 也可以查看一个命令的具体用法

官方文档上还会有使用案例:

1695368153849

通用命令是每种数据类型都可以使用的命令,常见的有:

  • keys:查看符合模板的所有key, 类似模糊查询,效率不高,不建议生产环境设备上使用

    • * 代表 0-多个任意字符
    • ? 代表一个任意字符

    1695367936260

    1695368236909

  • del:删除一个指定的key,可以传多个参数代表删多个key

    1695368385111

    1695368444527

  • exists:判断key是否存在

    1695368617917

    1695368667406

  • expire:给一个key设置有效期,有效期到期时该key会被自动删除,单位秒,可以节省内存空间,经常配合 ttl 使用

    因为 redis 是基于内存存储的,不及时清理的话内存可能会占满,业务中比如:短信验证码保留五分钟

    1695368891911

    1695368950225

  • ttl:查看一个key的剩余有效期 ttl key

    • 返回结果:-2 代表过期或者没有该key,-1 代表有效期永久

    1695369025613

2.3 String 类型

2.3.1 基本介绍

  • String 类型,也是字符串类型,是Redis中最简单的存储类型。其 value 是字符串,不过根据字符串的格式不同,又可以分为3类:
    • String:普通字符串
    • int:整数类型,可以做自增、自减操作
    • float:浮点类型,可以做自增、自减操作

1695369173059

不管是哪种格式,底层都是字节数组形式存储,只不过是编码方式不同。字符串类型的最大空间不能超过521m

2.3.2 常见命令

  • set:添加或者修改已存在的一个 String 类型的键值对
  • get:根据 key 获取 String 类型的 value
  • mset:批量添加多个 String 类型的键值对
  • mget:根据多个 key 获取多个 String 类型的 value

1695369505279

  • incr:让一个整型的 key 自增1

  • incrby:让一个整型的 key 自增并指定步长,例如 incrby num 2, 让num值自增2

  • incrbyfloat:让一个浮点类型的数字自增并指定步长

1695369907767

  • setnx:添加一个 String 类型的键值对,前提是 key 不存在,否则不执行(也就是只有新增效果)

    set xxx xxx nx 效果一样,如 set name thr nx

  • setex:添加一个 String 类型的键值对,并指定有效期

1695370447619

补充:Key 的层级格式

Q:Redis没有类似MySQL中Table的概念,我们该如何区分不同类型的key 呢?

  • 例如,需要存储用户、商品信息到redis,有一个用户id是1,有一个商品id恰好也是1

1695370678474

  • 测试一下:

1695371135855

1695371180765

###2.4 Hash 类型

2.4.1 基本介绍

  • Hash 类型,也叫散列,其 value 是一个无序字典,类似于Java中的 HashMap 结构

  • String 结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便

    1695371633662

  • Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD

    1695371659931

2.4.2 常见命令

  • HSET key field value:添加或者修改 hash 类型 key 的 field 的值
  • HGET key field:获取一个 hash 类型 key 的 field 的值
  • HMSET:批量添加多个 hash 类型 key 的 field 的值
  • HMGET:批量获取多个 hash 类型 key 的 field 的值

1695372680274

1695372409033

  • HGETALL:获取一个 hash 类型的 key 中的所有的 field 和value

  • HKEYS:获取一个 hash 类型的 key 中的所有的 field

  • HVALS:获取一个hash类型key中所有的value 就像Java中values

1695372816971

  • HINCRBY:让一个 hash 类型 key 的字段值自增并指定步长

1695372938291

  • HSETNX添加一个 hash 类型的 key 的 field 值,前提是这个 field 不存在,否则不执行

1695373016912

2.5 List 类型

2.5.1 基本介绍

  • List 类型与 Java 中的 LinkedList类似,可以看做是一个双向链表结构,既可以支持正向检索和也可以支持反向检索
  • 特征也与 LinkedList 类似:
    • 有序
    • 元素可以重复
    • 插入和删除快
    • 查询速度一般
  • 常用来存储一个有序数据,例如:朋友圈点赞列表,评论列表

1695373325551

2.5.2 常见命令

  • LPUSH key element … :向列表左侧插入一个或多个元素
  • LPOP key:移除并返回列表左侧的第一个元素,没有则返回nil
  • RPUSH key element … :向列表右侧插入一个或多个元素
  • RPOP key:移除并返回列表右侧的第一个元素
  • LRANGE key star end:返回一段角标范围内的所有元素
  • BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil

1695377415136

1695377137688

2.5.3 思考题

  • 如何利用List结构模拟一个栈?
    • 入口和出口在同一边
  • 如何利用List结构模拟一个队列?
    • 入口和出口在不同边
  • 如何利用List结构模拟一个阻塞队列?
    • 入口和出口在不同边
    • 出队时采用BLPOP或BRPOP

2.6 Set 类型

2.6.1 基本介绍

  • Set 结构与 Java 中的 HashSet 类似,可以看做是一个value为 null 的HashMap
  • 因为也是一个hash表,因此具备与HashSet类似的特征:
    • 无序
    • 元素不可重复
    • 查找快
    • 支持交集、并集、差集等功能
  • 应用场景:共同好友

2.6.2 常见命令

  • SADD key member … :向set中添加一个或多个元素
  • SREM key member … :移除set中的指定元素
  • SCARD key: 返回set中元素的个数
  • SISMEMBER key member:判断一个元素是否存在于set中
  • SMEMBERS:获取set中的所有元素

1695377849941

1695377780836

  • SINTER key1 key2 … :求key1与key2的交集
  • SDIFF key1 key2……:求key1与key2的差集
  • SUNION key1 key2…:求key1和key2并集和

1695377948875

2.6.3 Set 命令的练习

  • 用 set 集合存储下列数据:

1695378012736

1
2
sadd zs ls wangwu zhaoliu
sadd ls wangwu mazi ergou
  • 用 set 命令实现下列功能
1
2
3
4
5
6
7
scard zs  # 计算张三的好友有几人
sinter zs ls # 计算张三和李四有哪些共同好友
sdiff zs ls # 查询哪些是张三但不是李四的好友
sunion zs ls # 查询张三和李四的好友总共有哪些人
sismember zs ls # 判断李四是否是张三的好友
sismember ls zs # 判断张三是否是李四的好友
srem zs ls # 将李四从张三的好友里移除

2.7 SortedSet 类型

2.7.1 基本介绍

  • SortedSet 是一个可排序的 set 集合,与 Java 中的TreeSet有些类似,但底层数据结构却差别很大
  • SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加 hash 表
    • 可排序
    • 元素不重复
    • 查询速度快(因为有哈希表)
  • 因为 SortedSet 的可排序特性,经常被用来实现排行榜这样的功能

2.7.2 常见命令

  • ZADD key score member:添加一个或多个元素到sorted set ,如果已经存在则更新其score值
  • ZREM key member:删除sorted set中的一个指定元素
  • ZSCORE key member:获取sorted set中的指定元素的score值
  • ZRANK key member:获取sorted set 中的指定元素的排名
  • ZCARD key:获取sorted set中的元素个数
  • ZCOUNT key min max:统计score值在给定范围内的所有元素的个数
  • ZINCRBY key increment member:让sorted set中的指定元素自增,步长为指定的increment值
  • ZRANGE key min max:按照score排序后,获取指定排名范围内的元素
  • ZRANGEBYSCORE key min max:按照score排序后,获取指定score范围内的元素
  • ZDIFF、ZINTER、ZUNION:求差集、交集、并集

注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可,例如:

  • 升序获取sorted set 中的指定元素的排名:ZRANK key member
  • 降序获取sorted set 中的指定元素的排名:ZREVRANK key memeber

2.7.3 SortedSet 命令的练习

  • 将下列学生得分存入 redis 的 SortedSet 中:

1695379109599

1
zadd stus 85 jack 89 Lucy 82 Rose 95 Tom 78 Jerry 92 Amy 76 Miles

1695379258090

  • 并实现下列功能:
1
2
3
4
5
6
7
zrem stus Tom  # 删除 Tom 同学
zscore stus Amy # 获取 Amy 同学的分数
zrank stus Rose # 获取 Rose 同学的排名(排名从0开始算)
zcount stus 0 80 # 查询 80 分以下的同学的个数(查数量)
zincrby stus 2 Amy # 给 Amy 同学加2分
zrevrange stus 0 2 # 查出成绩前三名的同学(注意是倒序查)
zrangebyscore stus 0 80 # 查出成绩 80 分以下的所有同学(查具体的元素)

3. Redis 的 Java 客户端

在Redis官网中提供了各种语言的客户端,地址:

Get started using Redis clients | Redis

其推荐 Java 使用的有:Java guide | Redis

3.1 客户端对比

1695380417163

3.2 Jedis

Jedis 的官网地址:https://github.com/redis/jedis

3.2.1 快速入门

  • 先新建一个 maven 工程

1695441717499

1695381155370

  • 引入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.8.0</version>
</dependency>

<!-- 单元测试 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>

1695381242709

  • 建立连接

在 test 包下新建一个类,JedisTest

1695382494024

1
2
3
4
5
6
7
8
9
10
11
private Jedis jedis;

@BeforeEach
void setUp(){
// 1. 建立连接, 注意改成自己虚拟机的ip地址
jedis = new Jedis("192.168.3.185",6379);
// 2. 设置密码
jedis.auth("thr");
// 3. 选择库
jedis.select(0);
}
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 测试 string
@Test
void testString(){
// 插入数据,方法名就是 redis 的命令名称
String result = jedis.set("name", "小明");
System.out.println("result= " + result);

// 获取数据(快捷方式.var)
String name = jedis.get("name");
System.out.println("name= " + name); // 快捷键 soutv
}

// 测试 hash
@Test
void testHash() {
// 插入 hash 数据
jedis.hset("user:1", "name", "Jack");
jedis.hset("user:1", "age", "21");

// 获取数据
Map<String, String> map = jedis.hgetAll("user:1");
System.out.println(map);
}
  • 释放资源
1
2
3
4
5
6
7
@AfterEach
void tearDown(){
// 释放资源
if(jedis != null){
jedis.close();
}
}
  • 成功结果

1695383541679

1695441495504

总结:Jedis 使用的步骤:

  • 引入依赖
  • 创建 Jedis 对象,引入连接(需要 ip 地址、端口号、密码等)
  • 使用 Jedis ,方法名和 Redis 命令一致
  • 释放资源

3.2.2 Jedis 线程池

Jedis 本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用 Jedis 连接池代替 Jedis 的直接连接

  • 创建工具类 JedisConnectionFactory
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
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisConnectionFactory {
private static final JedisPool JedisPool;

static {
// 配置连接池
JedisPoolConfig poolConfig = new JedisPoolConfig();

// 最大连接数
poolConfig.setMaxTotal(8);
// 最大空闲连接数
poolConfig.setMaxIdle(8);
// 最小空闲连接
poolConfig.setMinIdle(0);
// 最长等待时间 ms
poolConfig.setMaxWaitMillis(1000);

// 创建连接池对象
JedisPool = new JedisPool(poolConfig,
"192.168.3.185", 6379, 1000, "thr");
}

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

代码说明:

  • 1)JedisConnectionFacotry:工厂设计模式是实际开发中非常常用的一种设计模式,我们可以使用工厂,去降低代的耦合,比如Spring中的Bean的创建,就用到了工厂设计模式
  • 2)静态代码块:随着类的加载而加载,确保只能执行一次,我们在加载当前工厂类的时候,就可以执行static的操作完成对连接池的初始化
  • 3)最后提供返回连接池中连接的方法.
  • 修改之前的测试类,重新测试,成功
1
jedis = JedisConnectionFactory.getJedis();

1695442811145

代码说明:

  • 我们在完成了使用工厂设计模式来完成代码的编写后,我们可以通过工厂来获得连接,不用去new对象,减低耦合,且使用的还是连接池对象
  • 当我们使用连接池后,当我们关闭连接其实并不是关闭,而是将 Jedis 归还给连接池

3.3 SpringDataRedis(重点)

3.3.1 基本介绍

SpringData 是 Spring 中数据操作的模块,包含对各种数据库的集成,其中对 Redis 的集成模块就叫做 SpringDataRedis,官网地址:https://spring.io/projects/spring-data-redis

  • 提供了对不同Redis客户端的整合(Lettuce和Jedis)
  • 提供了RedisTemplate统一API来操作Redis
  • 支持Redis的发布订阅模型
  • 支持Redis哨兵和Redis集群
  • 支持基于Lettuce的响应式编程(Lettuce之前是在es那里有)
  • 支持基于JDK.JSON.字符串.Spring对象的数据序列化及反序列化
  • 支持基于Redis的JDKCollection实现

1695443407565

3.3.2 快速入门

SpringBoot 已经提供了对 SpringDataRedis 的支持,使用非常简单,所以我们先创建一个 SpringBoot 项目:

1695443673871

1695443718891

1695443736688

  • 引入依赖
1
2
3
4
5
6
7
8
9
10
11
<!--Redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!--连接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
  • 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
spring:
redis:
host: 192.168.3.185
port: 6379
password: thr
database: 0
lettuce:
pool:
max-active: 8 #最大连接数
max-idle: 8 #最大空闲连接
min-idle: 0 #最小空闲连接
max-wait: 100 #连接等待时间

这个地方的 pool 咱们选择的是 lettuce 而不是 jedis,因为 spring 默认引入的是 lettuce,如果要用 jedis 还需要自己引下依赖

1695444387182

  • 注入 RedisTemplate,编写测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@SpringBootTest
class SpringdataredisDemoApplicationTests {
// 注入 RedisTemplate
// 自动装配爆红的话将注解 @Autowired 改成 @Resource 或者 @Autowired(required = false)
@Resource
private RedisTemplate redisTemplate;

@Test
void testString() {
// 写一条 string 数据
redisTemplate.opsForValue().set("name", "thr");

// 获取 string 数据
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = " + name); // name = thr
}
}
  • 成功结果

1695444912647

总结:SpringDataRedis 的使用步骤

  • 引入依赖
  • 在 yaml 文件里配置 Redis 信息
  • 注入 RedisTemplate

3.3.3 RedisTemplate 的 RedisSerializer

  • Q:根据前面的快速入门,我们去图形化界面做验证检查的时候会发现一些问题:多了一个这样的 key:\xac\xed\x00\x05t\x00\x04name

    原因在序列化?前面使用的 redisTemplate.opsForValue().set("name", "thr"); 这里面 redisTemplate 的 set 方法,操作的参数并不是字符串,而是 Object 类型,接收任何类型的对象,并将其转成 redis 可以 处理的字节,所以我们存进去的 namethr 就被当成 java 对象了,而默认的序列化器是 jdk 的序列化器,得到的结果就是如下这样:

    1695445303647

    1695446187379

  • 缺点

    • 可读性差
    • 内存占用较大
  • 解决办法:不用默认的方法

    • 快捷键 ctrl + H 打开类层级关系模板

    1695446493818

    • 所以我们可以自定义 RedisTemplate 的序列化方式来解决这个问题
  • 详细解决过程

    • 编写配置类:RedisConfig
    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
    @Configuration
    public class RedisConfig {
    // springboot 会帮我们自动创建工厂, 我们只要注入下就行
    // 此处 形参名爆红 需要降低 springboot 的版本
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
    // 创建 RedisTemplate 对象
    RedisTemplate<String, Object> template = new RedisTemplate<>();

    // 设置连接工厂
    template.setConnectionFactory(connectionFactory);

    // 创建 JSON 序列化工具
    GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();

    // 设置 key 的序列化
    template.setKeySerializer(RedisSerializer.string());
    template.setHashKeySerializer(RedisSerializer.string());

    // 设置 value 的序列化
    template.setValueSerializer(jsonRedisSerializer);
    template.setHashValueSerializer(jsonRedisSerializer);

    // 返回
    return template;
    }
    }
    • 引入 Jackson 依赖

    因为这里我们没有用到 springmvc 所以需要引入下这个依赖,平常开发中 springmvc 会自带这个依赖

    1
    2
    3
    4
    5
    <!-- Jackson依赖 -->
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    </dependency>
    • 修改测试类:加个泛型即可

    1695447344514

    • 成功

    1695447615550

  • 尝试写入对象

    • 先创建一个实体类:User
    1
    2
    3
    4
    5
    6
    7
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
    private String name;
    private Integer age;
    }
    • 然后编写测试方法:存储 user 对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Test
    void testSavaUser() {
    // 写入数据
    redisTemplate.opsForValue().set("user:100", new User("hhh", 21));

    // 获取数据
    User o = (User) redisTemplate.opsForValue().get("user:100");
    System.out.println("o = " + o);
    }
    • 查看图形化界面验证结果:

    1695448067814

3.3.4 StringRedisTemplate

  • 问题:尽管 Json 的自动序列化方式可以满足我们的需求,但仍然存在一些问题,为了在反序列化时知道对象的类型,JSON 序列化器会将类的 class 类型写入 json 结果中,存入 Redis,会带来额外的内存开销,如上 3.3.3 的结果
  • 解决:因此为了节省内存空间,我们并不会使用 JSON 序列化器来处理value,而是统一使用 String 序列化器,要求只能存储 String 类型的 key 和 value。当需要存储 Java 对象时,手动完成对象的序列化和反序列化

1695448480856

String 默认提供了一个 StringRedisTemplate 类,他的key和value的序列化默认就是String方式,省去了定义redisTemplate 过程

1695448590148

  • 具体实现

    • 新建测试类:SpringdataredisStringApplicationTests
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    @SpringBootTest
    class SpringdataredisStringApplicationTests {
    // 注入 StringRedisTemplate
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Test
    void testString() {
    // 写一条 string 数据
    stringRedisTemplate.opsForValue().set("name", "thr");

    // 获取 string 数据
    Object name = stringRedisTemplate.opsForValue().get("name");
    System.out.println("name = " + name); // name = thr
    }

    // json 的序列化工具, 也可以用 fastjson
    private static final ObjectMapper mapper = new ObjectMapper();

    @Test
    void testSavaUser() throws JsonProcessingException {
    // 创建对象
    User user = new User("thr", 21);

    // 手动序列化
    String json = mapper.writeValueAsString(user);

    // 写入数据
    stringRedisTemplate.opsForValue().set("user:200", json);

    // 获取数据
    String jsonUser = stringRedisTemplate.opsForValue().get("user:200");

    // 手动反序列化
    User user1 = mapper.readValue(jsonUser, User.class);
    System.out.println("user1 = " + user1);
    }
    }
    • 成功结果:

    1695449143031

    1695449157968

3.3.5 总结:RedisTemplate 的两种序列化实践方案

  • 自定义 RedisTemplate
    • 修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer
  • 使用 StringRedisTemplate
    • 写入Redis时,手动把对象序列化为Json
    • 读取Redis时,手动把读取到的Json反序列化为对象

3.3.6 补充:RedisTemplate 操作 Hash 类型

  • 编写测试方法
1
2
3
4
5
6
7
8
9
10
@Test
void testHash() {
// 存入数据
stringRedisTemplate.opsForHash().put("user:400", "name", "thr");
stringRedisTemplate.opsForHash().put("user:400", "age", "21");

// 获取数据, 此处用 entries 方法获取所有键值对拼接的 map
Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:400");
System.out.println("entries = " + entries);
}
  • 成功结果

1695449600304

1695449585760