Redis介绍与基本使用
约 1956 个字 393 行代码 预计阅读时间 11 分钟
Redis基本介绍
Redis是一个开源的高性能键值数据库,提供字符串、哈希、列表、集合、有序集合等多种数据结构,具备内存读写带来的低延迟与高吞吐特点,同时支持持久化、主从复制、哨兵与集群机制以实现高可用和水平扩展。它常用于缓存、会话存储、排行榜、消息队列、分布式锁等场景,其优势在于速度快、结构丰富、部署灵活
Ubuntu上安装和卸载Redis
安装可以使用如下的命令:
| Bash |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | # 更新包索引
sudo apt update
# 安装 Redis
sudo apt install redis-server -y
# 检查版本
redis-server --version
# 启动服务并设置开机自启
sudo systemctl start redis-server
sudo systemctl enable redis-server
# 验证运行状态
sudo systemctl status redis-server
|
修改基本配置:
| Bash |
|---|
| sudo nano /etc/redis/redis.conf
|
常见修改的配置如下:
| Text Only |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13 | # 设置密码(取消注释并修改)
requirepass your_strong_password
# 允许远程访问(默认只允许本地,有风险,建议配合防火墙)
# bind 127.0.0.1 ::1
bind 0.0.0.0
# 关闭保护模式(如果允许远程访问)
protected-mode no
# 内存限制(示例:限制 256MB)
maxmemory 256mb
maxmemory-policy allkeys-lru
|
修改完配置之后需要重启Redis服务:
| Bash |
|---|
| sudo systemctl restart redis-server
|
完整卸载Redis可以参考如下步骤:
| Bash |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | # 停止服务
sudo systemctl stop redis-server
sudo systemctl disable redis-server
# 卸载并清除配置文件
sudo apt purge redis-server -y
sudo apt autoremove --purge -y
# 手动删除数据目录(谨慎操作!)
sudo rm -rf /etc/redis/
sudo rm -rf /var/lib/redis/
sudo rm -rf /var/log/redis/
# 删除源码安装的文件(如果是源码安装)
sudo rm -f /usr/local/bin/redis-*
|
Redis常见通用命令
在Redis命令行客户端输入下面的命令可以查看当前版本支持的通用命令:
下面介绍几种常见的通用命令:
keys:查看符合指定规则的所有key del:删除指定的key。支持批量删除,返回实际删除成功的key的数量 exists:判断指定的key是否存在 expire:为指定的key设置有效期,单位为秒。 ttl:查看指定的key剩余的有效期。在未给key设置有效期时,查出该key的结果为-1表示永不过期,对一个已经过期的key进行查询,则结果为-2
常见命令使用示例:
String类型命令
String类型是Redis中最基础的数据类型,键对应的值根据字符串的格式可以是字符串、整数或浮点数,最大长度为512MB。常见的操作命令可以分为两类:
- 设置和获取键值对
- 值为整数或浮点数的自增
对于设置和获取,有下面的几种命令:
set:添加或者修改一个键值对 get:获取指定key的值 mset:批量添加多个键值对 mget:批量获取多个键值对
例如下面的例子:
| Bash |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | 127.0.0.1:6379> set name Rose # 原来不存在
OK
127.0.0.1:6379> get name
"Rose"
127.0.0.1:6379> set name Jack # 原来存在,就是修改
OK
127.0.0.1:6379> get name
"Jack"
127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> MGET name age k1 k2 k3
1) "Jack" # 之前存在的name
2) "10" # 之前存在的age
3) "v1"
4) "v2"
5) "v3"
|
其中set命令有两种变体:
setex:添加一个不存在的键值对,如果存在则不进行任何操作。等同于set key value nx setnx:添加一个键值对并且设置有效期。等同于set key value ex seconds
例如下面的例子:
| Bash |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | 127.0.0.1:6379> set name Jack //设置名称
OK
127.0.0.1:6379> setnx name lisi //如果key不存在,则添加成功
(integer) 0
127.0.0.1:6379> get name //由于name已经存在,所以lisi的操作失败
"Jack"
127.0.0.1:6379> setnx name2 lisi //name2 不存在,所以操作成功
(integer) 1
127.0.0.1:6379> get name2
"lisi"
127.0.0.1:6379> setex name 10 jack
OK
127.0.0.1:6379> ttl name
(integer) 8
127.0.0.1:6379> ttl name
(integer) 7
127.0.0.1:6379> ttl name
(integer) 5
|
对于自增来说,有下面的两种命令:
incr:针对值为整数,自增1 incrby:针对值为整数,自增自定义步长 incrbyfloat:针对值为浮点数,自增自定义步长
例如下面的例子:
| Bash |
|---|
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 | 127.0.0.1:6379> get age
"10"
127.0.0.1:6379> incr age # 增加1
(integer) 11
127.0.0.1:6379> get age # 获得age
"11"
127.0.0.1:6379> incrby age 2 # 一次增加2
(integer) 13 # 返回目前的age的值
127.0.0.1:6379> incrby age 2
(integer) 15
127.0.0.1:6379> incrby age -1 # 也可以增加负数,相当于减
(integer) 14
127.0.0.1:6379> incrby age -2 # 一次减少2个
(integer) 12
127.0.0.1:6379> DECR age # 相当于incr负数,减少的另一种方法
(integer) 11
127.0.0.1:6379> get age
"11"
|
Hash类型
在前面的String类型中,想要修改一个键的值必须是替换而不能进行单独修改,如果值是一个Json字符串,那么要修改Json字符串中的某个字段就需要替换掉整个Json字符串,相对比较麻烦。对于Hash类型,值可以相当于一个Java中的HashMap,有自己的key和value,所以在Hash类型中,创建一个Redis的Key(相当于创建一个HashMap),接着插入键值对field和value(相当于HashMap中的key和value)
在Hash类型中,常见的操作也分为两类:
- 设置和获取键值对
- 值为整数或浮点数的自增
对于设置和获取,有下面的几种命令:
hset:添加或者修改一个field的值 hget:获取指定field的值 hmset:批量添加多个field的值 hmget:批量获取多个field的值 hgetall:获取指定key下的所有field和value hkeys:获取指定key下所有的field hvals:获取指定key下所有的value
例如下面的示例:
| Bash |
|---|
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 | 127.0.0.1:6379> hset test f1 v1
(integer) 1
127.0.0.1:6379> hget test f1
"v1"
127.0.0.1:6379> hmset test f2 v2 f3 v3 f4 v4
OK
127.0.0.1:6379> hmget test f1 f2 f3 f4
1) "v1"
2) "v2"
3) "v3"
4) "v4"
127.0.0.1:6379> hgetall test
1) "f1"
2) "v1"
3) "f2"
4) "v2"
5) "f3"
6) "v3"
7) "f4"
8) "v4"
127.0.0.1:6379> hkeys test
1) "f1"
2) "f2"
3) "f3"
4) "f4"
127.0.0.1:6379> hvals test
1) "v1"
2) "v2"
3) "v3"
4) "v4"
|
对于hset来说,有一个变体:hsetnx,与setnx类似,但是hsetnx不是检测key是否存在,而是检测field是否存在
对于自增来说,有hincrby,作用与incrby类似,但是要指定field,例如下面的示例:
| Bash |
|---|
| 127.0.0.1:6379> hset test1 f1 1 # 设置
(integer) 1
127.0.0.1:6379> hincrby test1 f1 2 # 自增2
(integer) 3
127.0.0.1:6379> hincrby test1 f1 2 # 自增2
(integer) 5
|
List类型
List类型可以理解为是一个双向链表,常见的操作如下:
lpush:左侧(链表头部)插入元素 lpop:左侧删除并返回元素,如果没有元素返回nil rpush:右侧(链表尾部)插入元素 rpop:右侧删除元素 lrange:获取指定区间(左闭右闭)的元素,下标从0开始 blpop:左侧阻塞式删除并返回元素,没有元素时会阻塞等待指定的超时时间,超时没有元素则返回nil brpop:右侧阻塞式删除并返回元素,没有元素时会阻塞等待指定的超时时间,超时没有元素则返回nil
例如下面的示例:
| Bash |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | 127.0.0.1:6379> lpush l1 1 2 3 4
(integer) 4
127.0.0.1:6379> lpop l1 3
1) "4"
2) "3"
3) "2"
127.0.0.1:6379> rpush l2 1 2 3 4
(integer) 4
127.0.0.1:6379> rpop l2 3
1) "4"
2) "3"
3) "2"
127.0.0.1:6379> lpush l3 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lrange l3 2 3
1) "3"
2) "2"
|
Set类型
Set类型类似于HashSet,元素值不能重复,常见的操作命令可以分为两类:
- 设置、获取和删除等元素操作
- 多个Set类型的
key进行交集、查集和并集运算
对于元素操作,有下面常见的命令:
sadd:新增一个或多个元素 srem:删除指定元素 scard:返回指定key的元素个数 sismember:判断指定元素是否存在key中 smembers:获取指定key的所有元素
例如下面的示例:
| Bash |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | 127.0.0.1:6379> sadd s1 1 1 2 3 4 4
(integer) 4
127.0.0.1:6379> smembers s1
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> srem s1 1
(integer) 1
127.0.0.1:6379> scard s1
(integer) 3
127.0.0.1:6379> sismember s1 1
(integer) 0
127.0.0.1:6379> sismember s1 2
(integer) 1
|
对于交集、查集和并集运算,有下面的命令:
sinter:多个key交集运算(取共同) sdiff:多个key差集运算(取指定key有,但是其他key没有的) sunion:多个key并集(将所有的key放在指定的新key中)
例如下面的示例:
| Bash |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | 127.0.0.1:6379> sadd s1 1 2 3
(integer) 3
127.0.0.1:6379> sadd s2 3 4 5
(integer) 3
127.0.0.1:6379> sinter s1 s2 # 取出s1和s2中都有的
1) "3"
127.0.0.1:6379> sdiff s1 s2 # 取出s1有,但是s2没有的
1) "1"
2) "2"
127.0.0.1:6379> sdiff s2 s1 # 取出s2有,但是s1没有的
1) "4"
2) "5"
127.0.0.1:6379> sunion s1 s2 # 取出s1和s2的总和
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
|
需要注意,上面的交集、并集和差集命令不会影响到原来的key,即s1和s2不会做出任何改变:
| Bash |
|---|
| 127.0.0.1:6379> scard s1
(integer) 3
127.0.0.1:6379> scard s2
(integer) 3
|
如果需要将交集、并集和差集结果放到一个新的key中,可以使用下面的命令:
sinterstore:取交集,结果保存到一个指定的新key中 sdiffstore:取差集,结果保存到一个指定的新key中 sunionstore:取并集,结果保存到一个指定的新key中
例如下面的示例:
| Bash |
|---|
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 | 127.0.0.1:6379> sinterstore s3 s1 s2 # 交集保存到s3中
(integer) 1
127.0.0.1:6379> smembers s3
1) "3"
127.0.0.1:6379> sdiffstore s4 s1 s2 # 差集保存到s4中
(integer) 2
127.0.0.1:6379> smembers s4
1) "1"
2) "2"
127.0.0.1:6379> sunionstore s5 s1 s2 # 并集保存到s5中
(integer) 5
127.0.0.1:6379> smembers s5
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> sunionstore s1 s1 s2 # 并集保存到s1中
(integer) 5
127.0.0.1:6379> smembers s1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
|
ZSet类型
ZSet类型(sorted set)在Set的基础上为每个成员增加一个score,并按照score进行排序。常见的操作命令如下:
zadd:添加一个或多个元素到sorted set,如果已经存在则更新其score值 zrem:删除sorted set中的一个指定元素 zscore:获取sorted set中的指定元素的score值 zrank:获取sorted set中的指定元素的排名 zcard:获取sorted set中的元素个数 zcount:统计score值在给定范围内的所有元素的个数 zincrby:让sorted set中的指定元素自增,步长为指定的increment值 zrange:按照score排序后,获取指定排名范围内的元素,下标从0开始 zrangebyscore:按照score排序后,获取指定score范围内(左闭右闭)的元素 zdiff、zinter、zunion:求差集、交集、并集
注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可,例如:
- 升序获取sorted set中的指定元素的排名:
zrank key member - 降序获取sorted set中的指定元素的排名:
zrevrank key member
例如下面的示例:
| Bash |
|---|
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 | 127.0.0.1:6379> zadd z1 100 Tom 90 Jack 95 Rose
(integer) 3
127.0.0.1:6379> zrange z1 0 -1 withscores
1) "Jack"
2) "90"
3) "Rose"
4) "95"
5) "Tom"
6) "100"
127.0.0.1:6379> zscore z1 Tom
"100"
127.0.0.1:6379> zrank z1 Rose
(integer) 1
127.0.0.1:6379> zrevrank z1 Rose
(integer) 1
127.0.0.1:6379> zcount z1 90 100
(integer) 3
127.0.0.1:6379> zincrby z1 5 Jack
"95"
127.0.0.1:6379> zrangebyscore z1 95 100 withscores
1) "Jack"
2) "95"
3) "Rose"
4) "95"
5) "Tom"
6) "100"
127.0.0.1:6379> zrem z1 Jack
(integer) 1
127.0.0.1:6379> zcard z1
(integer) 2
|
推荐的层级结构
在Redis中没有表的概念,如果有两种类型具有同样的数据,例如商品ID值为1和用户ID值为1,此时key直接存1就不合适,对此可以使用:进行层级划分,例如:
这样划分出的结构就是:
在一些Redis可视化客户端中就会看到对应的文件夹形式
Redis的Java客户端(Spring Data Redis)
引入依赖
| XML |
|---|
| <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
|
Spring Data Redis配置文件内容
| YAML |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | spring:
data:
redis:
host: 127.0.0.1
port: 6379
password: your_password # 没有密码可删
database: 0
timeout: 5000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 1000ms
|
基本使用
| Java |
|---|
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 | @Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// String
stringRedisTemplate.opsForValue().set("name", "Jack");
String name = stringRedisTemplate.opsForValue().get("name");
// Object
redisTemplate.opsForValue().set("user:1", new User(1, "Tom"));
Object user = redisTemplate.opsForValue().get("user:1");
// String
stringRedisTemplate.opsForValue().set("age", "18");
String age = stringRedisTemplate.opsForValue().get("age");
// Hash
redisTemplate.opsForHash().put("user:1", "name", "Tom");
Object name = redisTemplate.opsForHash().get("user:1", "name");
// List
redisTemplate.opsForList().leftPush("list:1", "a");
redisTemplate.opsForList().rightPush("list:1", "b");
// Set
redisTemplate.opsForSet().add("set:1", "x", "y", "z");
Boolean isMember = redisTemplate.opsForSet().isMember("set:1", "x");
// ZSet
redisTemplate.opsForZSet().add("zset:1", "Tom", 100);
redisTemplate.opsForZSet().add("zset:1", "Jack", 90);
Set<Object> zset = redisTemplate.opsForZSet().range("zset:1", 0, -1);
|
自定义序列化配置
| Java |
|---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | @Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// key 采用 String 序列化
StringRedisSerializer stringSerializer = new StringRedisSerializer();
template.setKeySerializer(stringSerializer);
template.setHashKeySerializer(stringSerializer);
// value 采用 JSON 序列化
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}
|