简介
Redis,Remote Dictionary Server,远程词典服务器,一个基于内存的键值型NoSQL数据库。
特点:
- 键值(key-value)型,value支持多种数据结构。
- 单线程,每个命令具备原子性。
虽然从6版本开始,已经支持多线程,但是这里的多线程仅仅针对网络请求,在核心的命令执行,依旧是单线程。 - 低延迟,速度快。
原因:基于内存、IO多路复用、良好的编码,其中最主要的原因是基于内存。 - 支持数据持久化。
Redis会定期将数据从内存持久化到磁盘 - 支持主从集群、分片集群。
安装
本文讨论的是在Linux上安装Redis,一般也都是在Linux服务器上安装Redis,而且Redis官方没有提供Windows的安装包,我们在网上看到的一些Windows的安装包,并不是Redis官方的。
gcc依赖
Redis是基于C语言编写的,因此首先需要安装Redis所需要的gcc依赖。
1 | yum install -y gcc tcl |
解压安装包
可以通过官网下载安装包,然后上传至Linux服务器。或者直接在Linux服务器上通过wget的方式下载。
1 | wget https://github.com/redis/redis/archive/7.2.3.tar.gz |
下载完成后,可以把redis压缩包,移动到一个合适的目录(例如/usr/local/src),redis会被安装在我们解压目录的bin中。
解压缩:
1 | tar -xzf redis-7.2.3.tar.gz |
解压后,进入redis目录:
1 | cd redis-7.2.3/ |
编译并安装:
1 | make && make install |
此时,我们会发现在解压包下,多了一个目录bin,redis就被安装在该目录下。
此外,redis还会在/usr/local/bin/目录下新建"快捷方式"。因为,/usr/local/bin/目录已经默认配置到环境变量,所以可以在任意目录下运行这些命令。
redis-cli:是redis提供的命令行客户端。redis-server:是redis的服务端启动脚本。redis-sentinel:是redis的哨兵启动脚本.
启动
Redis的启动方式有很多种,例如:
- 默认启动
- 指定配置启动
- 系统服务启动
默认启动
安装完成后,在任意目录输入redis-server命令即可启动Redis。
示例代码:
1 | redis-server |
运行结果:
1 | 24186:C 28 Nov 2023 22:20:06.412 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo |
这种启动属于前台启动,会阻塞整个会话窗口,窗口关闭或者按下CTRL + C则Redis停止。不推荐使用。
指定配置启动
如果要让Redis以"后台"方式启动,可以修改Redis配置文件。
配置文件
配置文件路径:/usr/local/src/redis-7.2.3/redis.conf。
启动Redis
指定配置文件,启动Redis。
1 | redis-server /usr/local/src/redis-7.2.3/redis.conf |
启动之后,我们敲入redis-cli命令,然后ping一下:
1 | redis-cli |
因为我们上文设置了密码,所以需要先认证:
1 | redis-cli |
系统服务启动
新建一个系统服务文件:
1 | vim /etc/systemd/system/redis.service |
内容如下:
1 | [Unit] |
重载系统服务:
1 | systemctl daemon-reload |
这样,便加入了系统服务。
- 启动:
systemctl start redis。 - 停止:
systemctl stop redis。 - 重启:
systemctl restart redis。 - 查看状态:
systemctl status redis。 - 开机自启:
systemctl enable redis。
命令行客户端
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举例:
- 选择0号库:
select 0 - 关闭Redis服务端:
shutdown
注意,该命令是关闭Redis服务端。
图形化客户端
图形化客户端有很多,例如:
- Redis官方的
RedisInsight
下载地址:https://redis.com/redis-enterprise/redis-insight/ - DataGrip
问题解决
卸载
- 停止redis服务器
- 删除
/usr/local/bin1
2
3cd /usr/local/bin
ll redis*
rm -f redis* - 删除解压后的文件目录和所有文件
1
2cd /usr/local/src/
rm -rf redis-7.0.7 - 如果我们配置了加入系统服务,需要把相关的文件(
/etc/systemd/system/redis.service)也删掉。 - 如果我们还配置了开机启动,还需要先关闭开机启动。
Key的层级
Redis没有类似MySQL中的Table的概念,我们该如何区分不同类型的key呢?
例如,需要存储用户、商品信息到redis,有一个用户id是1,有一个商品id恰好也是1
Redis的key允许有多个单词形成层级结构,多个单词之间用:隔开,格式如下:
1 | 项目名:业务名:类型:id |
例如我们的项目名称叫kaka,有user和product两种不同类型的数据,我们可以这样定义key:
- user相关的key:
kaka:user:1 - product相关的key:
kaka:product:1
这个格式并非固定,也可以根据自己的需求来删除或添加词条。
数据结构
分类
Redis的数据结构有很多种,常见的是8种。
5种基本类型:
String1
hello world
Hash1
{name: "Jack", age: 21}
List1
[A -> B -> C -> C]
Set1
{A, B, C}
SortedSet1
{A: 1, B: 2, C: 3}
3种特殊类型:
GEO1
{A:(120.3, 30.5)}
BitMap1
0110110101110101011
HyperLog1
0110110101110101011
除了上述8种,还有很多,例如消息队列等。
帮助文档
在Redis的官网可以查看不同数据结构的操作命令
也可以通过命令查看帮助。
help命令,示例代码:
1 | help |
运行结果:
1 | redis-cli 7.2.3 |
查看keys命令的帮助,示例代码:
1 | help keys |
运行结果:
1 | KEYS pattern |
通用命令
String类型
String类型,也就是字符串类型,其value是字符串。
根据字符串的格式不同,又可以分为3类:
string:普通字符串。int:整数类型,可以做自增、自减操作。float:浮点类型,可以做自增、自减操作。
String类型的常见命令:
SET,添加或者修改已经存在的一个String类型的键值对。GET,根据key获取String类型的value。MSET,批量添加多个String类型的键值对。MGET,根据多个key获取多个String类型的value。INCR,让一个整型的key自增1。DECR,让一个整型的key自减1。INCRBY,让一个整型的key自增并指定步长。例如:incrby num 2,让num值自增2。INCRBYFLOAT,让一个浮点类型的数字自增并指定步长。SETNX,添加一个String类型的键值对,前提是这个key不存在,否则不执行。SETEX,添加一个String类型的键值对,并且指定有效期。
在SET命令中,有许多可用选项可以修改命令的行为。
1 | SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX] |
EX seconds,设置指定的终止时间,以秒为单位。PX milliseconds,设置指定的终止时间,以毫秒为单位。NX,仅在 不存在 的情况下设置key。XX,仅在 已存在 的情况下设置key。
例如,仅在不存在情况下,设置键coo,有效期为60秒:
1 | SET coo redis EX 60 NX |
如果Value是一个Java对象,例如一个User对象,则可以将对象序列化为JSON字符串后存储:
| KEY | VALUE |
|---|---|
| kaka:user:1 | {“id”:1, “name”: “Jack”, “age”: 21} |
| kaka:product:1 | {“id”:1, “name”: “小米11”, “price”: 4999} |
示例代码:
1 | set kaka:user:1 '{"id":1, "name": "Jack", "age": 21}' |
但是这个方法并不好,下文的Hash类型是一种更好的方法。
Hash类型
Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构。
String结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便。

Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD。value,分为了两部分,一部分是field,一部分是value

Hash的常见命令有:
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。HINCRBY,让一个hash类型key的字段值自增并指定步长。HSETNX,添加一个hash类型的key的field值,前提是这个field不存在,否则不执行。
可以一次执行多个field的值,示例代码:
1 | HSET kaka:user:1 name Lee age 23 sex male |
List类型
Redis中的List类型与Java中的LinkedList类似,可以看作是一个双向链表结构,既可以支持正向检索和也可以支持反向检索。
特征也与LinkedList类似:
- 有序
- 元素可以重复
- 插入和删除快
- 查询速度一般
List的常见命令有:
LPUSH key element ...,向列表左侧插入一个或多个元素。LPOP key,移除并返回列表左侧的第一个元素,没有则返回nil。RPUSH key element ...,向列表右侧插入一个或多个元素。RPOP key,移除并返回列表右侧的第一个元素。LRANGE key star end,返回一段角标范围内的所有元素。BLPOP和BRPOP,与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil。

使用方法:
- 如何利用List结构模拟一个栈?
入口和出口在同一边 - 如何利用List结构模拟一个队列?
入口和出口在不同边 - 如何利用List结构模拟一个阻塞队列?
入口和出口在不同边,出队时采用BLPOP或BRPOP
Set类型
Redis的Set结构与Java中的HashSet类似,可以看做是一个value为null的HashMap。
其特点有:
- 无序
- 元素不可重复
- 查找快
- 支持交集、并集、差集等功能
Set的常见命令有:
SADD key member ...,向set中添加一个或多个元素。SREM key member ...,移除set中的指定元素。SCARD key,返回set中元素的个数。SISMEMBER key member,判断一个元素是否存在于set中。SMEMBERS,获取set中的所有元素。SINTER key1 key2 ...,求key1与key2的交集。
SortedSet类型
Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。
SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加hash表。
SortedSet具备下列特性:
- 可排序
- 元素不重复
- 查询速度快
SortedSet的常见命令有:
ZADD key score member,添加一个或多个元素到SortedSet,如果已经存在则更新其score值。ZREM key member,删除SortedSet中的一个指定元素。ZSCORE key member,获取SortedSet中的指定元素的score值。ZRANK key member,获取SortedSet中的指定元素的排名。ZCARD key,获取SortedSet中的元素个数。ZCOUNT key min max,统计score值在给定范围内的所有元素的个数ZINCRBY key increment member,让SortedSet中的指定元素自增,步长为指定的increment值。ZRANGE key min max,按照score排序后,获取指定排名范围内的元素。ZRANGEBYSCORE key min max,按照score排序后,获取指定score范围内的元素。ZDIFF、ZINTER、ZUNION,求差集、交集、并集。
注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可,例如:
- 升序获取SortedSet中的指定元素的排名:
ZRANK key member - 降序获取SortedSet中的指定元素的排名:
ZREVRANK key memeber
再举一个例子,将班级的下列学生得分存入Redis的SortedSet中:
1 | Jack 85, Lucy 89, Rose 82, Tom 95, Jerry 78, Amy 92, Miles 76 |
示例代码:
1 | ZADD stus 85 Jack 89 Lucy 82 Rose 95 Tom 78 Jerry 92 Amy 76 Miles |
Java客户端
四种客户端
- Jedis
以Redis命令作为方法名称,学习成本低,简单实用。但是Jedis实例是线程不安全的,多线程环境下需要基于连接池来使用。 - Lettuce
Lettuce是基于Netty实现的,支持同步、异步和响应式编程方式,并且是线程安全的。支持Redis的哨兵模式、集群模式和管道模式。 - Redisson
Redisson是一个基于Redis实现的分布式、可伸缩的Java数据结构集合。包含了诸如Map、Queue、Lock、 Semaphore、AtomicLong等强大功能。 - SpringDataRedis
提供了对不同Redis客户端的整合(Lettuce和Jedis)
在本文,我们讨论Jedis和SpringDataRedis。
Jedis
Github地址:https://github.com/redis/jedis
通过这个Github地址,我们可以看出来,jedis是redis官方的。
快速入门
示例代码:
1 | package com.kakawanyifan; |
运行结果:
1 | result = OK |
连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,推荐使用Jedis连接池代替Jedis的直连方式。
Jedis连接池,JedisConnectionFactory:
1 | package com.heima.jedis.util; |
建立连接,示例代码:
1 | // 建立连接 |
释放连接,示例代码:
1 | jedis.close(); |
特别的,我们可以点进jedis.close()方法,会发现该方法内部根据是否有连接池判断是应该归还连接,还是释放连接。
示例代码:
1 | public void close() { |
SpringDataRedis
一个现象
关于SpringDataRedis,我们在《基于Java的后端开发入门:21.SpringBoot [1/3]》的"整合Redis客户端"部分,已经讨论过了。
在这里做一些补充,先看一个现象。
示例代码:
1 | package com.kakawanyifan.demo; |
运行结果:
1 | Kaka |
这个没有任何问题,然后我们通过redis-cli命令get name,示例代码:
1 | get name |
运行结果:
1 | (nil) |
没有?
通过keys看一下,示例代码:
1 | keys *name* |
运行结果:
1 | 1) "\xac\xed\x00\x05t\x00\x04name" |
key怎么这么奇怪?
自定义序列化
RedisTemplate默认是采用JDK序列化,这种方式存在两个问题:
- 可读性差
- 内存占用较大
上文的"\xac\xed\x00\x05t\x00\x04name"就是例子。
我们可以自定义RedisTemplate的序列化方式:
1 | package com.kakawanyifan.demo; |
- 对于value,采用了JSON序列化来代替默认的JDK序列化方式。
对象序列化
现在,假设我们有一个对象User,我们要把这个对象保存到Redis中。
User类,示例代码:
1 | package com.kakawanyifan.demo; |
存取对象,示例代码:
1 | package com.kakawanyifan.demo; |
运行结果:
1 | User(id=1, user=kaka) |
我们通过get命令看一下,示例代码:
1 | get user:1 |
运行结果:
1 | "{\"@class\":\"com.kakawanyifan.demo.User\",\"id\":1,\"user\":\"kaka\"}" |
在Redis的内容中,有这么一项@class,专门标明了User对象的类com.kakawanyifan.demo.User(序列化时对应的class名称)。
StringRedisTemplate
在上文,专门标明了序列化时对应的class名称,这样会带来额外的内存开销,为了节省内存空间,可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。
特别的,SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。
省去了我们自定义RedisTemplate的序列化方式的步骤。
示例代码:
1 | package com.kakawanyifan.demo; |
运行结果:
1 | User(id=2, user=kaka-2) |
通过get命令看一下,示例代码:
1 | get user:2 |
运行结果:
1 | "{\"id\":2,\"user\":\"kaka-2\"}" |
Python客户端
redis-py
安装命令:
1 | pip install redis |
PiPy地址:https://pypi.org/project/redis/
GitHub地址:https://github.com/redis/redis-py
官方文档:https://redis.readthedocs.io/en/stable/index.html
decode_responses
示例代码:
1 | import redis |
运行结果:
1 | b'Kaka' |
默认是字节类型,可以设置decode_responses=True,示例代码:
1 | import redis |
运行结果:
1 | Kaka |
连接池
redis-py使用ConnectionPool来管理对一个RedisServer的所有连接,避免每次建立、释放连接的开销。
示例代码:
1 | import redis |
运行结果:
1 | Kaka |
常见操作
关于Redis中的各种操作,可以参考官方文档。
https://redis.readthedocs.io/en/stable/commands.html
这里列举一些常用的。
String
-
set1
set(name, value, ex=None, px=None, nx=False, xx=False)
参数:
ex,过期时间(秒)。px,过期时间(毫秒)。nx,如果设置为True,则只有name不存在时,当前set操作才执行。xx,如果设置为True,则只有name存在时,当前set操作才执行。
-
get,获取值。 -
mget,批量获取值。 -
getset(name, value),设置新值并获取原来的值。 -
strlen(name),返回name对应值的字节长度(默认字符集是UTF-8,一个汉字三个字节) -
incr(name, amount=1),自增name对应的值,当name不存在时,则创建name=amount,否则,则自增。
incrbyfloat方法自增浮点数类型。
decr进行自减操作。 -
append(key, value),在name对应的值后面追加内容。
hash
hset(name, key, value),hmset(name, mapping),在name对应的hash中批量设置键值对:1
r.hmset("hash1", {"k1": "v1", "k2": "v2", "k3": "v3"})
hkeys(name),获取所有的keyhvals(name),得到所有的value。hgetall(name),取出所有的键值对。hdel(name),删除。hincrby(name, key, amount=1),自增自减整数。hincrbyfloat(name, key, amount=1.0)表示自增自减浮点数。
list
lpush(name,values),增加。rpush表示从右边增加。r.lset(name, index, value),对name对应的list中的某一个索引位置重新赋值。r.lrem(name, value, num),删除。1
2
3
4
5
6# 将列表中左边第一次出现的"11"删除
r.lrem("list2", "11", 1)
# 将列表中右边第一次出现的"99"删除
r.lrem("list2", "99", -1)
# 将列表中所有的"22"删除
r.lrem("list2", "22", 0)lindex(name, index),取值。
set
sadd(name, values),新增。srem(name, values),在name对应的集合中删除某些值。
其他操作
delete(name),删除。expire(name ,time),为某个redis的某个name设置超时时间。rename(src, dst),重命名。
管道
Redis默认在执行每次请求都会创建连接(连接池申请连接)和断开连接(归还连接池),如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline是原子性操作。
示例代码:
1 | import redis |
