本文为瑞吉外卖项目的后续内容,在此特别感谢黑马程序员的课程

Redis入门

  • Redis是一个基于内存的key-value结构数据库

    • 基于内存存储,读写性能高
    • 适合存储热点数据(热点商品、咨询、新闻)
  • 官网:https://redis.io/

Redis简介

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

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

关系型数据库(RDBMS):MySQL、Oracl、DB2、SQLServer
非关系型数据库(NoSql):Redis、Mongo DB、MemCached

  • Redis应用场景:缓存、消息队列、任务队列、分布式锁

下载与安装

Windows安装Redis

Linux安装Redis

  • Linux系统安装Redis步骤:
    1. 将Redis安装包上传到Linux
    2. 解压安装包,改成你自己的redis版本,路径我习惯解压到/usr/local
    1
    tar -zxvf redisVersion.tar.gz -C /usr/local
    1. 安装Redis的依赖环境gcc,我的云服务器已经装过了,本地的Linux还没装
    1
    2
    ## 安装依赖环境
    yum install gcc-c++
    1. 进入/usr/local/redis根目录,进行编译
    1
    2
    3
    4
    5
    ## 进入到根目录
    cd /usr/local/redis根目录

    ## 编译
    make
    1. 进入redis的src目录,进行安装
    1
    2
    3
    4
    ## 进入到src目录
    cd /usr/local/redis根目录/src
    ## 进行安装
    make install

服务启动与停止

Linux启动与停止

  • 进入到/src目录下,执行redis-server即可启动服务,默认端口号为6379
1
2
3
4
5
## 进入到根目录
cd /usr/local/redis根目录/src

## 执行redis-server
./redis-server

Linux设置后台运行

  • 进入到redis根目录下,修改配置redis.conf文件
1
2
3
4
5
## 进入到redis根目录下
cd /usr/local/redis根目录

## 修改配置文件
vim redis.conf
  • 找到daemonize on字段,将其修改为daemonize yes
  • 在redis根目录以redis.conf作为配置文件在后台运行
1
src/redis-server ./redis.conf

Linux开启密码校验

  • 还是修改redis.conf配置文件,找到requirepass这行,将其注释去掉,并在后面写上自己的密码
  • 然后杀掉原进程再重新启动
1
2
3
4
5
## 重新启动
src/redis-server ./redis.conf

## 登录时同时进行认证
src/redis-cli -h localhost -p 6379 -a 密码
  • 修改完毕之后还是杀进程,然后重启服务

Linux开启远程连接

  • 还是修改redis.conf配置文件,找到bind 127.0.0.1这行,把这行注释掉
  • 之后设置防火墙,开启6379端口
1
2
3
4
5
6
7
8
## 开启6379端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent

## 设置立即生效
firewall-cmd --reload

## 查看开放的端口
firewall-cmd --zone=public --list-ports


云服务器开放端口,需要进入后台手动开启,通过命令行开启的端口无效,查看开放的端口确实有6379,但是实际上是没开放的,我昨天因为这个,连不上MySQL(3306端口没开,我以为开了),找了俩小时的原因

  • 最后在Windows的redis根目录下,按住Shift+右键打开PowerShell窗口,连接Linux的Redis
1
.\redis-cli.ext -h 服务器地址 -p 6379 -a 密码

Redis数据类型

介绍

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

  • 字符串:String
  • 哈希:Hash
  • 列表:List
  • 集合:Set
  • 有序集合:Sorted Set

字符串(String)常用命令

命令 描述
SET key value 设置指定key的值
GET key 获取指定key的值
SETEX key seconds value 设置指定key的值,并将key的过期时间设为seconds秒
SETNX key value 只有在key不存在时设置key的值

哈希(Hash)常用命令

Redis Hash是一个String类型的FieldValue的映射表,Hash特别适合用于存储对象

命令 描述
HSET key field value 将哈希表key 中的字段field的值设为value
HGET key field 获取存储在哈希表中指定字段的值
HDEL key field 删除存储在哈希表中的指定字段
HKEYS key 获取哈希表中所有字段
HVALS key 获取哈希表中所有值
HGETALL key 获取在哈希表中指定key的所有字段和值

列表(List)常用命令

Redis List是简单的字符串列表,按照插入顺序排序

命令 描述
LPUSH key value1 [value2] 将一个或多个值插入到列表头部
LRANGE key start stop 获取列表指定范围内的元素
RPOP key 移除并获取列表最后一个元素
LLEN key 获取列表长度
BRPOP key1 [key2] timeout 移出并获取列表的最后一个元素
如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止

集合(Set)常用命令

Redis setString类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据

命令 描述
SADD key member1 [member2] 向集合添加一个或多个成员
SMEMBERS key 返回集合中的所有成员
SCARD key 获取集合的成员数
SINTER key1 [key2] 返回给定所有集合的交集
SUNION key1 [key2] 返回所有给定集合的并集
SDIFF key1 [key2] 返回给定所有集合的差集
SREM key member1 [member2] 移除集合中一个或多个成员

有序集合(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 …] 移除有序集合中的一个或多个成员

通用命令

命令 描述
KEYs pattern 查找所有符合给定模式(pattern)的key
EXISTs key 检查给定key是否存在
TYPE key 返回key所储存的值的类型
TTL key 返回给定key的剩余生存时间(TTL, time to live),以秒为单位
DEL key 该命令用于在key存在是删除key

更多详细的命令可以查看官方文档:https://www.redis.net.cn/order/

在Java中使用Redis

简介

  • Redis的Java客户端有很多,官方推荐的有三种
    • Jedis
    • Lettuce
    • Redisson
  • Spring对Redis客户端进行了整合,提供了SpringDataRedis,在Spring Boot项目中还提供了对应的Starter,即spring-boot-starter-data-redis

Jedis

  • 使用Jedis的步骤
    1. 获取连接
    2. 执行操作
    3. 关闭连接
  • 在此之前我们需要导入一下Jedis的maven坐标
1
2
3
4
5
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
  • 编写测试类
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
@SpringBootTest
class RedisTestApplicationTests {

@Test
void contextLoads() {
//1. 获取连接
Jedis jedis = new Jedis("localhost", 6379);
//2. 执行具体操作
jedis.set("name", "Hades");

jedis.hset("stu", "name", "Jerry");
jedis.hset("stu", "age", "18");
jedis.hset("stu", "num", "4204000400");

Map<String, String> map = jedis.hgetAll("stu");
Set<String> keySet = map.keySet();
for (String key : keySet) {
String value = map.get(key);
System.out.println(key + ":" + value);
}
String name = jedis.get("name");
System.out.println(name);
//3. 关闭连接
jedis.close();
}

}
  • 输出结果

num:4204000400
name:Jerry
age:18
Hades

  • Jedis我们了解一下即可,大多数情况下我们还是用SpringDataRedis的

Spring Data Redis

  • SpringBoot项目中,可以使用SpringDataRedis来简化Redis(常用)

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

    • ValueOperations:简单K-V操作
    • SetOperations:set类型数据操作
    • ZSetOperations:zset类型数据操作
    • HashOperations:针对map类型的数据操作
    • ListOperations:针对list类型的数据操作
  • 使用SpringDataRedis,我们首先需要导入它的maven坐标

1
2
3
4
5
<!--Spring Boot-redis的依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 之后重新设置一下序列化器,防止出现乱码,在config包下创建RedisConfig配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {

RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();

//默认的Key序列化器为:JdkSerializationRedisSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());

redisTemplate.setConnectionFactory(connectionFactory);

return redisTemplate;
}
}
  • 随后配置一下连接redis的相关配置
1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
redis:
host: localhost
port: 6379
#password: root
database: 0 #操作的是0号数据库
jedis:
#Redis连接池配置
pool:
max-active: 8 #最大连接数
max-wait: 1ms #连接池最大阻塞等待时间
max-idle: 4 #连接池中的最大空闲连接
min-idle: 0 #连接池中的最小空闲连接
  • String类型数据操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
void stringTest() {
//获取对象
ValueOperations valueOperations = redisTemplate.opsForValue();
//设置name为Hades
valueOperations.set("name","Hades");
String name = (String) valueOperations.get("name");
System.out.println(name);
//设置age为9527,有效时间10秒
valueOperations.set("age", "9527", 10, TimeUnit.SECONDS);
String age = (String) valueOperations.get("age");
System.out.println(age);
//如果不存在,则设置name为Kyle
Boolean aBoolean = valueOperations.setIfAbsent("name", "Kyle");
System.out.println(aBoolean);
}

输出结果如下,由于name已经存在,故Kyle设置失败,最后返回false,10秒过后,我们再去redis中get name,则输出nil,表示不存在

Hades
9527
false

  • Hash类型数据操作
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
@Test
void hashTest() {
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.put("4204000400", "name", "Hades");
hashOperations.put("4204000400", "age", "18");
hashOperations.put("4204000400", "hobby", "Apex");
//获取map集合
Map<String, String> map = hashOperations.entries("4204000400");
Set<String> keySet = map.keySet();
for (String hashKey : keySet) {
System.out.println(hashKey + ":" + map.get(hashKey));
}
System.out.println("$$$$$$$$$$$$$$$");
//只获取keys
Set<String> keys = hashOperations.keys("4204000400");
for (String key : keys) {
System.out.println(key);
}
System.out.println("$$$$$$$$$$$$$$$");
//只获取values
List<String> values = hashOperations.values("4204000400");
for (String value : values) {
System.out.println(value);
}
}

输出结果如下

name:Hades
age:18
hobby:Apex
$$$$$$$$$$$$$$$
name
age
hobby
$$$$$$$$$$$$$$$
Hades
18
Apex

  • List类型数据操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
void listTest() {
ListOperations listOperations = redisTemplate.opsForList();
//存数据
listOperations.leftPush("testData", "A");
listOperations.leftPushAll("testData", "B", "C", "D");
List<String> testDatas = listOperations.range("testData", 0, -1);
//遍历
for (String tableData : testDatas) {
System.out.print(tableData + " ");
}
System.out.println();
//获取当前list长度,用于遍历
Long size = listOperations.size("testData");
int value = size.intValue();
//遍历输出并删除
for (int i = 0; i < value; i++) {
System.out.print(listOperations.leftPop("testData") + " ");
}
//最后输出一下当前list长度
System.out.println();
System.out.println(listOperations.size("testData"));
}

输出结果如下

D C B A
D C B A
0

  • Set类型数据操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
void setTest() {
SetOperations setOperations = redisTemplate.opsForSet();
//存数据,这里存了两个a
setOperations.add("tmp", "a", "b", "c", "d", "a");
遍历输出
Set<String> tmpData = setOperations.members("tmp");
for (String value : tmpData) {
System.out.print(value + " ");
}
System.out.println();
System.out.println("$$$$$$$$$$$$$$$$$$$");
//删除bc
setOperations.remove("tmp", "b", "c");
//再次遍历输出
tmpData = setOperations.members("tmp");
for (String value : tmpData) {
System.out.print(value + " ");
}
}

输出结果如下,符合预期

d b c a
$$$$$$$$$$$$$$$$$$$
d a

  • ZSet类型数据操作
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
@Test
void zsetTest() {
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
//存值
zSetOperations.add("myZset", "a", 0.0);
zSetOperations.add("myZset", "b", 1.0);
zSetOperations.add("myZset", "c", 2.0);
zSetOperations.add("myZset", "a", 3.0);
//取值
Set<String> myZset = zSetOperations.range("myZset", 0, -1);
for (String s : myZset) {
System.out.println(s);
}
//修改分数
zSetOperations.incrementScore("myZset", "b", 4.0);
//取值
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$");
myZset = zSetOperations.range("myZset", 0, -1);
for (String s : myZset) {
System.out.println(s);
}
//删除成员
zSetOperations.remove("myZset", "a", "b");
//取值
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$");
myZset = zSetOperations.range("myZset", 0, -1);
for (String s : myZset) {
System.out.println(s);
}
}

输出结果如下
b
c
a
$$$$$$$$$$$$$$$$$$$$$$$$$
c
a
b
$$$$$$$$$$$$$$$$$$$$$$$$$
c

  • 通用的数据类型操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Test
void commonTest() {
//查看所有key
Set<String> keys = redisTemplate.keys("*");
for (String key : keys) {
System.out.println(key);
}
//查看是否存在指定key
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$");
System.out.println(redisTemplate.hasKey("Random"));
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$");
//删除指定key,并再次查看
redisTemplate.delete("myZset");
keys = redisTemplate.keys("*");
for (String key : keys) {
System.out.println(key);
}
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$");
//输出指定key的类型
System.out.println(redisTemplate.type("tmp"));
}

输出结果如下

tmp
name
4204000400
stu
myData
$$$$$$$$$$$$$$$$$$$$$$$$$
false
$$$$$$$$$$$$$$$$$$$$$$$$$
tmp
name
4204000400
stu
myData
$$$$$$$$$$$$$$$$$$$$$$$$$
SET