前言
原文作者: Kyle Violet
文章链接: Redis 入门 | Kyle's Blog (cyborg2077.github.io)
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Kyle's Blog!
本文章为在原文基础上,进行稍作修改的文章,版权声明如上。
本文为瑞吉外卖项目的后续内容,在此特别感谢黑马程序员的课程
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 应用场景:缓存、消息队列、任务队列、分布式锁
下载与安装
- 这里我们在 Linux 和 Windows 上都装一下
Windows 安装 Redis
- Windows 版:https://github.com/microsoftarchive/redis/releases
- 直接下载对应版本的
.zip
压缩包,直接解压之后
Linux 安装 Redis
-
Linux 系统安装 Redis 步骤:
-
将 Redis 安装包上传到 Linux
-
解压安装包,改成你自己的 redis 版本,路径我习惯解压到
/usr/local
复制成功tar -zxvf redisVersion.tar.gz -C /usr/local
-
安装 Redis 的依赖环境 gcc,我的云服务器已经装过了,本地的 Linux 还没装
- 这里需要确保能联网,否则会安装失败 用 ping www.baidu.com 就可以检测出来了。如果不能联网,
- 就输入: cd /etc/sysconfig/network-scripts/
- 然后 vim ifcfg-ens33
- 把里面在前面配置的 static(静态 ip,改成 dhcp 动态 ip)
- systemctl restart network
- 即可
然后继续安装
复制成功# 安装依赖环境 yum install gcc-c++
-
进入
/usr/local/redis根目录
,
进行编译
# 进入到根目录 cd /usr/local/redis根目录 # 编译 make
-
进入 redis 的 src 目录,进行安装
# 进入到src目录 cd /usr/local/redis根目录/src # 进行安装 make install
-
服务启动与停止
Linux 启动与停止
-
进入到
/src
目录下,执行
redis-server
即可启动服务,默认端口号为
6379
# 进入到根目录 cd /usr/local/redis根目录/src # 执行redis-server ./redis-server
or
[root@localhost ~]# ps -ef | grep redis root 7695 7286 0 09:58 pts/0 00:00:00 grep --color=auto redis [root@localhost ~]# cd /usr/local/redis-4.0.0/ [root@localhost redis-4.0.0]# src/redis-server ./redis.conf #启动redis服务
Linux 设置后台运行
-
进入到 redis 根目录下,修改配置 redis.conf 文件
# 进入到redis根目录下 cd /usr/local/redis根目录 # 修改配置文件 vim redis.conf
-
找到
daemonize no
字段,将其修改为daemonize yes
- 这里需要用/dae 来找
-
在 redis 根目录以 redis.conf 作为配置文件在后台运行
src/redis-server ./redis.conf
Linux 开启密码校验
-
还是修改 redis.conf 配置文件,找到
requirepass
这行,将其注释去掉,并在后面写上自己的密码 -
然后杀掉原进程再重新启动
-
# 重新启动 src/redis-server ./redis.conf # 登录时同时进行认证 src/redis-cli -h localhost -p 6379 -a 密码
-
修改完毕之后还是杀进程,然后重启服务
Linux 开启远程连接
-
还是修改 redis.conf 配置文件,找到
bind 127.0.0.1
这行,把这行注释掉 -
之后设置防火墙,开启 6379 端口
# 开启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
.\redis-cli.exe -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
类型的Field
和Value
的映射表,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 set
是String
类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据
命令 | 描述 |
---|---|
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 的步骤
- 获取连接
- 执行操作
- 关闭连接
-
在此之前我们需要导入一下 Jedis 的 maven 坐标
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.0</version> </dependency>
-
编写测试类
@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 坐标
<!--Spring Boot-redis的依赖包--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
之后重新设置一下序列化器,防止出现乱码,在 config 包下创建
RedisConfig
配置类
@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 的相关配置
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
类型数据操作@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
类型数据操作
@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:ApexHades
18
Apex -
List
类型数据操作@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
类型数据操作@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
类型数据操作@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
ac
-
通用的数据类型操作
@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
myDatatmp
name
4204000400
stu
myDataSET