Redis 基础
Redis 基础篇
NoSql 数据库
1. 入门
1.1 NoSQL
SQL 和 NoSQL
关系型数据库 和 非关系型数据库
区别:
(1)结构化和非结构化
- SQL关系型数据库是结构化数据,每一张表都有严格的约束信息:字段名、字段数据类型、字段约束等等信息,插入的数据必须遵守这些约束
 - NoSql对数据库格式没有严格约束,往往形式松散,自由。可以是key-value,可以是文档,或者图格式
 
(2)关联和非关联
关系型:

非关系型
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)查询方式

(4)事务
- 传统关系型数据库能满足事务ACID的原则 ,而非关系型数据库往往不支持事务,或者不能严格保证ACID的特性,只能实现基本的一致性。
 
1.2 Redis 安装-尚硅谷
此处主要参考尚硅谷的教程:
1.2.1 安装
- Redis是基于C编写,所以需要先安装Redis所需的gcc依赖,可以用 
gcc -v进行查看验证 
1  | yum install -y gcc  | 

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


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

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

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

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  | mkdir /myredis  | 

修改
/myredis目录下redis.conf配置文件做初始化设置,配置完记得重启!1
2cd /myredis
vim redis7.conf- 默认 daemonize no 改为 daemonize yes
 

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

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

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

- 最后输入 
:wq!保存并退出 
补充:Vim 的一些用法

1.2.3 启动并连接服务
启动服务
1
2redis-server /myredis/redis7.conf
ps -ef | grep redis | grep -v grep
端口 6379 被占用,说明后台启动成功
ps 用于查看进程
连接服务
1
2redis-cli -a thr -p 6379
ps -ef | grep redis | grep -v grep

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

补充
退出连接:
quit
若连接时不想看见警告,可用:
redis-cli -a thr -p 6379 2>/dev/nul该操作是将警告重定向至linux的黑洞文件
若想开机自启,则跳转到 1.3.3
1.2.4 测试 hello world
1  | set k1 helloworld  | 

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


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

- 多实例关闭,指定端口关闭
 
1  | redis-cli -p 6379 shutdown  | 
1.2.6 卸载 redis
- 先停止 redis-server 服务
 
1  | redis-cli -a thr shutdown  | 
- 删除 /usr/local/lib 目录下与 redis 相关的文件
 
1  | ls -l /usr/local/bin/redis-*  | 
1.3 Redis 安装启动-黑马
redis的启动方式有很多种,例如:
- 默认启动
 - 指定配置启动
 - 开机自启
 
1.3.1 默认启动
- 安装完成后,在任意目录输入 
redis-server命令即可启动 Redis: 

- 这种启动属于前台启动,会阻塞整个会话窗口,需要重开一个窗口才可以进行连接,窗口关闭或者按下 
CTRL + C则Redis停止,不推荐使用。 
1.3.2 指定配置启动
- 如果要让Redis以后台方式启动,则必须修改Redis配置文件,就在我们之前解压的redis安装包下,叫redis.conf
 - 先备份:
cp redis.conf redis.conf.bck 

- 然后修改redis.conf文件中的一些配置:
 
1  | 允许访问的地址,默认是127.0.0.1,会导致只能在本地访问。修改为0.0.0.0则可以在任意IP访问,生产环境不要设置为0.0.0.0  | 
- Redis的其它常见配置:
 
1  | 监听的端口  | 
- 启动Redis:
 
1  | 进入redis安装目录  | 
- 停止服务:
 
1  | 利用redis-cli来执行 shutdown 命令,即可停止 Redis 服务,  | 
1.3.3 开机自启
这个地方需要跟着做一下
- 首先,新建一个系统服务文件:
 
1  | vi /etc/systemd/system/redis.service  | 
- 内容如下:
 
1  | [Unit]  | 
重点在这一行,
ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-6.2.6/redis.conf前者是安装目录不变,后者需改成配置文件所在位置(由于我是根据尚硅谷的教程做的,所以配置文件所在位置为
/myredis/redis7.conf)
- 然后重载系统服务:
 
1  | systemctl daemon-reload  | 
- 现在,我们可以用下面这组命令来操作redis了:
 
1  | 启动  | 

- 执行下面的命令,可以让redis开机自启:
 
1  | systemctl enable redis  | 
可以通过
ps -ef | grep redis | grep -v grep查看是否开启 redis
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 图形化桌面客户端
GitHub上的大神编写了Redis的图形化桌面客户端,地址:
https://github.com/uglide/RedisDesktopManager
不过该仓库提供的是RedisDesktopManager的源码,并未提供windows安装包
在下面这个仓库可以找到安装包:
https://github.com/lework/RedisDesktopManager-Windows/releases

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

- 连不上,尝试关闭防火墙后成功连接
 
1  | systemctl status firewalld # 查看防火墙状态  | 


一共有0-15共16个库
1.4.3 测试一下
- 命令行客户端
 
1  | redis-cli -a thr # 先进入命令行客户端  | 

图形化客户端
- 可以看到刚刚添加的数据
 

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

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

2. Redis 常见命令
官方文档里的常见命令:Commands | Redis ,可以分组查找相关的命令
也可在命令行里用
help @xxx进行查找,如,help @string
2.1 Redis 数据结构介绍
- Redis 是 key-value键值对 的数据库,一般 key 是 String,不过value 数据结构是多种多样的,下面的前五种是比较常见的数据结构:
 

2.2 Redis 通用命令
在命令行中可以用
help @generic查询通过
help [command]也可以查看一个命令的具体用法官方文档上还会有使用案例:
通用命令是每种数据类型都可以使用的命令,常见的有:
keys:查看符合模板的所有key, 类似模糊查询,效率不高,不建议生产环境设备上使用
*代表 0-多个任意字符?代表一个任意字符


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


exists:判断key是否存在


expire:给一个key设置有效期,有效期到期时该key会被自动删除,单位秒,可以节省内存空间,经常配合
ttl使用因为 redis 是基于内存存储的,不及时清理的话内存可能会占满,业务中比如:短信验证码保留五分钟


ttl:查看一个key的剩余有效期
ttl key- 返回结果:
-2代表过期或者没有该key,-1代表有效期永久 

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

不管是哪种格式,底层都是字节数组形式存储,只不过是编码方式不同。字符串类型的最大空间不能超过521m
2.3.2 常见命令
- set:添加或者修改已存在的一个 String 类型的键值对
 - get:根据 key 获取 String 类型的 value
 - mset:批量添加多个 String 类型的键值对
 - mget:根据多个 key 获取多个 String 类型的 value
 

incr:让一个整型的 key 自增1
incrby:让一个整型的 key 自增并指定步长,例如
incrby num 2, 让num值自增2incrbyfloat:让一个浮点类型的数字自增并指定步长

setnx:添加一个 String 类型的键值对,前提是 key 不存在,否则不执行(也就是只有新增效果)
跟
set xxx xxx nx效果一样,如set name thr nxsetex:添加一个 String 类型的键值对,并指定有效期

补充:Key 的层级格式
Q:Redis没有类似MySQL中Table的概念,我们该如何区分不同类型的key 呢?
- 例如,需要存储用户、商品信息到redis,有一个用户id是1,有一个商品id恰好也是1
 

- 测试一下:
 


###2.4 Hash 类型
2.4.1 基本介绍
Hash 类型,也叫散列,其 value 是一个无序字典,类似于Java中的 HashMap 结构
String 结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便:

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

2.4.2 常见命令
- HSET key field value:添加或者修改 hash 类型 key 的 field 的值
 - HGET key field:获取一个 hash 类型 key 的 field 的值
 - HMSET:批量添加多个 hash 类型 key 的 field 的值
 - HMGET:批量获取多个 hash 类型 key 的 field 的值
 


HGETALL:获取一个 hash 类型的 key 中的所有的 field 和value
HKEYS:获取一个 hash 类型的 key 中的所有的 field
HVALS:获取一个hash类型key中所有的value 就像Java中values

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

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

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

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


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中的所有元素
 


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

2.6.3 Set 命令的练习
- 用 set 集合存储下列数据:
 

1  | sadd zs ls wangwu zhaoliu  | 
- 用 set 命令实现下列功能
 
1  | scard zs # 计算张三的好友有几人  | 
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 中:
 

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

- 并实现下列功能:
 
1  | zrem stus Tom # 删除 Tom 同学  | 
3. Redis 的 Java 客户端
在Redis官网中提供了各种语言的客户端,地址:
Get started using Redis clients | Redis
其推荐 Java 使用的有:Java guide | Redis
3.1 客户端对比

3.2 Jedis
Jedis 的官网地址:https://github.com/redis/jedis
3.2.1 快速入门
- 先新建一个 maven 工程
 


- 引入依赖
 
1  | <!-- jedis -->  | 

- 建立连接
 
在 test 包下新建一个类,JedisTest
1  | private Jedis jedis;  | 
- 测试
 
1  | // 测试 string  | 
- 释放资源
 
1  | 
  | 
- 成功结果
 


总结:Jedis 使用的步骤:
- 引入依赖
 - 创建 Jedis 对象,引入连接(需要 ip 地址、端口号、密码等)
 - 使用 Jedis ,方法名和 Redis 命令一致
 - 释放资源
 
3.2.2 Jedis 线程池
Jedis 本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用 Jedis 连接池代替 Jedis 的直接连接:
- 创建工具类 
JedisConnectionFactory 
1  | import redis.clients.jedis.Jedis;  | 
代码说明:
- 1)JedisConnectionFacotry:工厂设计模式是实际开发中非常常用的一种设计模式,我们可以使用工厂,去降低代的耦合,比如Spring中的Bean的创建,就用到了工厂设计模式
 - 2)静态代码块:随着类的加载而加载,确保只能执行一次,我们在加载当前工厂类的时候,就可以执行static的操作完成对连接池的初始化
 - 3)最后提供返回连接池中连接的方法.
 
- 修改之前的测试类,重新测试,成功
 
1  | jedis = JedisConnectionFactory.getJedis();  | 

代码说明:
- 我们在完成了使用工厂设计模式来完成代码的编写后,我们可以通过工厂来获得连接,不用去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实现
 

3.3.2 快速入门
SpringBoot 已经提供了对 SpringDataRedis 的支持,使用非常简单,所以我们先创建一个 SpringBoot 项目:
- 引入依赖
 
1  | <!--Redis依赖-->  | 
- 配置文件
 
1  | spring:  | 
这个地方的 pool 咱们选择的是 lettuce 而不是 jedis,因为 spring 默认引入的是 lettuce,如果要用 jedis 还需要自己引下依赖
- 注入 RedisTemplate,编写测试类
 
1  | 
  | 
- 成功结果
 

总结: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 可以 处理的字节,所以我们存进去的name和thr就被当成 java 对象了,而默认的序列化器是 jdk 的序列化器,得到的结果就是如下这样:

缺点
- 可读性差
 - 内存占用较大
 
解决办法:不用默认的方法
- 快捷键 
ctrl + H打开类层级关系模板 

- 所以我们可以自定义 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
public class RedisConfig {
// springboot 会帮我们自动创建工厂, 我们只要注入下就行
// 此处 形参名爆红 需要降低 springboot 的版本
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>- 修改测试类:加个泛型即可
 

- 成功
 

- 编写配置类:
 尝试写入对象
- 先创建一个实体类:
User 
1
2
3
4
5
6
7
public class User {
private String name;
private Integer age;
}- 然后编写测试方法:存储 user 对象
 
1
2
3
4
5
6
7
8
9
void testSavaUser() {
// 写入数据
redisTemplate.opsForValue().set("user:100", new User("hhh", 21));
// 获取数据
User o = (User) redisTemplate.opsForValue().get("user:100");
System.out.println("o = " + o);
}- 查看图形化界面验证结果:
 

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

String 默认提供了一个 StringRedisTemplate 类,他的key和value的序列化默认就是String方式,省去了定义redisTemplate 过程
具体实现:
- 新建测试类:
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
class SpringdataredisStringApplicationTests {
// 注入 StringRedisTemplate
private StringRedisTemplate stringRedisTemplate;
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();
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);
}
}- 成功结果:
 


- 新建测试类:
 
3.3.5 总结:RedisTemplate 的两种序列化实践方案
- 自定义 RedisTemplate
- 修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer
 
 - 使用 StringRedisTemplate 
- 写入Redis时,手动把对象序列化为Json
 - 读取Redis时,手动把读取到的Json反序列化为对象
 
 
3.3.6 补充:RedisTemplate 操作 Hash 类型
- 编写测试方法:
 
1  | 
  | 
- 成功结果:
 











