Redis数据结构
# 常用的数据类型
常用的数据类型有5个:使用字符串存储的String类型,代表对象的哈希类型,集合的 set 类型,有序序列的 sorted set 类型,列表(它的底层实现是双向队列)。
常用:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)
# string
命令格式 | 功能 | 案例 |
---|---|---|
set key value | 将key-value缓存redis中 | set name dafei |
get key | 从redis中获取key对应value值 | get name |
incr key | 将key对应value值 + 1 | incr age |
decr key | 将key对应value值-1 | decr age |
setex key seconds value | 将key-value缓存到redis中,seconds 秒后失效 | setex sex 10 man |
ttl key | 查看key存活时间 | ttl sex |
del key | 从redis中删除key | del name |
setnx key value | 如果key已经存,不做任何操作,如果key不存,直接添加 | setnx name xiaofei |
- SET stores a string value.
- SETNX stores a string value only if the key doesn't already exist. Useful for implementing locks.
- GET retrieves a string value.
- MGET retrieves multiple string values in a single operation.
使用场景
- 缓存查询结果,降低数据库的访问压力。例如搜索某个东西的时候,我们可以把查询条件加上固定 id 作为 key ,查询的结果,作为 value。
- 缓存视频播放数量,使用 redis 作为视频播放数的计数器
- 共享 session
- 当我们的服务器做了负载均衡,我们的请求就会发送到不同的服务器上进行处理
- 当我们的 a 服务器承受的访问压力较大时,此时负载均衡可能将新进来的访问给到 b 服务器。这个时候,用户刷新一次再进行访问,就可能需要重新登录
- 为了避免这种不正常的情况,我们可以用 redis 将用户的 session 集中管理。在这种模式下,我们只需要保证 redis 的高可用和拓展性就可以保证每次自己的登录信息都是一致的。每次获取用户更新或者查询的信息时,统一从 redis 中获取即可。
# Hash类型
命令格式 | 功能 | 案例 |
---|---|---|
hset key field value | 将field value对缓存到redis中hash中,键值为key | hset user name dafei |
hget key field | 从key对应hash列表中获取field字段 | hget user name |
hexists key field | 判断key对应的hash列表是否存在 field字段 | hexists user age |
hdel key field | 删除key对应hash列表中field字段 | hdel user age |
hincrby key field increment | 给key对应hash列表中field字段 + increment | hincrby user age 10 |
hlen key | 查看key对应的hash列表field的数量 | hlen user |
hkeys key | 获取key对应的hash列表所有的field值 | hkeys user |
hvals key | 获取key对应的hash列表所有的field对应的value值 | kvals user |
hgetall key | 获取key对应的hash列表中所有的field及其对应的value值 | hgetall user |
使用场景
跟上面的字符串存储的类型是类似的,但是这种方式更利于内部信息的更改操作。
对于登录信息的缓存有两种方案,
- 第一种方案是将对象的信息转为 json 格式的字符串存在 redis,这就是 string 的字符串的做法。
- 第二种方式是将 user 对象转换为哈希对象存储。在 redis 里面,这种方式更侧重于修改、更改信息,但是查询操作相对来说更加麻烦
# List类型
我们从操作 list 命令中就可以看到,它里面有 push 和 pop 命令,猜也能猜到,它就是一个队列。
但实际使用跟猜测的还是有一点出入的。当整个列表的元素数量较小,或者是元素的体积较小时,它会采用一种紧凑的结构,叫做压缩列表。当整个列表的元素较大以及数量较多时,它会采用另外一种数据结构,叫做双向链表。
- 这样做的好处有很多,当整个列表的元素较小,数量较少时,我们采用压缩列表就可以很大的程度上节省空间。
- 压缩列表的底层实现是:数字加元素内容(非常紧凑的实现方式)
常用的命令
命令格式 | 功能 | 案例 |
---|---|---|
rpush key value | 从右边往key集合中添加value值 | rpush hobby java |
lrange key start stop | 从左边开始列表key集合,从start位置开始,stop位置结束 | lrange hobby 0 -1 |
lpush key value | 从左边往key集合中添加value值 | lpush hobby c++ |
lpop key | 弹出key集合中最左边的数据 | lpop hobby |
rpop key | 弹出key集合中最右边的数据 | rpop hobby |
llen key | 获取列表长度 | llen hooby |
- 可以用来存储用户收藏文章的列表,以及关注人列表等等。一些比较多增删改的地方
# Set类型
Set集合是String类型的无序集合,set是通过HashTable实现的,对集合我们可以取交集、并集、差集。
常用的命令
命令格式 | 功能 | 案例 |
---|---|---|
sadd key members [....] | 往key 集合中添加member元素 | sadd myset a b c |
smembers key | 遍历key集合中所有的元素 | smembers myset |
srem key members [....] | 删除key集合中members元素 | srem myset a |
spop key count | 从key集合中随机弹出count个元素 | spop myset 1 |
- 去重(set 集合自带的特性)
- 抽奖(pop随机抽取)
# Sorted set 类型
Sorted set 也称Zset类型,是一种具有排序效果的set集合。它跟set集合一样也是 string 类型元素的集合,且不允许重复的成员。并且要求每个元素都会关联一个double 类型的分数。后续可以通过分数来为集合中的成员进行从小到大的排序。
Sorted set集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
命令格式 | 功能 | 案例 |
---|---|---|
zadd key score member | 往key集合中添加member元素,分数为score | zadd players 100 a |
zincrby key increment member | 将key集合中的member元素 分数 + increment | zadd players 100 a |
zrange key start stop [withscores] | 将key集合中的元素按分数升序排列 [显式分数] | zrange players 0 -1 withscores |
zrevrange key start stop [withscores] | 将key集合中的元素按分数降序排列 [显式分数] | zrevrange players 0 -1 withscores |
zrank key member | 返回member元素在key结合中的正序排名 | zrank players a |
zrevrank key member | 返回member元素在key结合中的倒序排名 | zrevrank players a |
zcard key | 返回key集合元素个数 | zcard players |
- 排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等。
- 按照新增时间进行排序、按照操作的新增时间对关注人列表进行排序
127.0.0.1:6379> help set
SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
summary: Set the string value of a key
since: 1.0.0
group: string
2
3
4
5
6
# 全局命令
全局命令针对的是所有的key,大部分用来做运维,做管理的
常用的全局key
命令格式 | 功能 | 案例 |
---|---|---|
keys pattern | 按照pattern 匹配规则,列表redis中所有的key | keys xxx:* |
exists key | 判断key是否存在 | exists name |
expire key seconds | 给key设置过期时间,超时:seconds | expire name 10 |
persist key | 取消key过期时间 | persist name |
select index | 切换数据库,默认是第0个,共有【0,15】个 | select 0 |
move key db | 从当前数据库将key移动到指定db库 | move name 1 |
randomkey | 随机返回一个key | randomkey |
rename key newkey | 将key改名为newkey | rename name newname |
echo message | 打印message信息 | echo message |
dbsize | 查看key个数 | dbsize |
info | 查看redis数据库信息 | info |
config get * | 查看所有redis配置信息 | config get * |
flushdb | 清空当前数据库 | flushdb |
flushall | 清空所有数据库 | flushall |
# 理论知识
# 事务
Redis事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED
redis 127.0.0.1:6379> GET book-name
QUEUED
redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED
redis 127.0.0.1:6379> SMEMBERS tag
QUEUED
redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
2) "C++"
3) "Programming"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- redis 的事务类似于 bash 的指令集,仅代表批量操作
- 批量操作一旦开始,其他现成的操作不能在中间插入
- 批量操作一旦开始,中间有错误,不会回滚,会继续向下执行
# 内存淘汰机制
- LRU (Least Recently Used):最近最少使用算法。根据数据最近被访问的时间来决定哪些数据将被淘汰。当内存空间不足时,将优先淘汰最近最少被访问的数据。
- LFU (Least Frequently Used):最不经常使用算法。根据数据被访问的频率来决定哪些数据将被淘汰。当内存空间不足时,将优先淘汰访问频率最低的数据。
- Random:随机删除算法。随机选择一些数据项进行删除。这种方法简单粗暴,但无法保证删除的数据是最不重要的。
- TTL (Time To Live):生存时间算法。通过设置数据的过期时间来自动淘汰过期的数据。当数据的过期时间到达时,Redis 会自动将其删除。
- Maxmemory Policy:根据策略选择淘汰数据。除了以上几种具体的算法外,Redis 还提供了一种灵活的淘汰策略,允许用户根据具体的需求选择淘汰算法。常见的策略包括:
volatile-lru
:只对设置了过期时间的键使用 LRU 算法进行淘汰。volatile-ttl
:只对设置了过期时间的键进行淘汰,删除剩余时间较短的键。volatile-random
:只对设置了过期时间的键进行随机淘汰。allkeys-lru
:对所有键使用 LRU 算法进行淘汰。allkeys-random
:对所有键进行随机淘汰。noeviction
:不淘汰任何数据,当内存不足时,新增操作会报错,Redis
默认内存淘汰策略;
volatile 意味着变化
在 Redis 中,"volatile" 键表示具有过期时间的键,其 TTL(Time To Live)属性会随着时间的推移而变化。
allkeys-xxx
表示从所有的键值中淘汰数据,而volatile-xxx
表示从设置了过期键的键值中淘汰数据。
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "0"
127.0.0.1:6379> config get maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"
127.0.0.1:6379> config set maxmemory-policy volatile-ttl
OK
2
3
4
5
6
7
8
# 删除策略
Redis采用了惰性删除和定期删除的策略来处理过期键。在这种策略下,Redis定期扫描只会检查设置了过期时间的键,而不会扫描所有键。过期键的信息会被存储在一个单独的过期字典(expires)中,以避免全盘扫描的情况发生。
Redis每隔一段时间(默认为100毫秒)会随机抽取过期字典中的键,检查它们是否已经过期,并将过期的键删除。通过这种随机扫描的方式,避免了对所有键进行扫描,从而降低了对服务的影响,避免了可能的不可用情况。
对于已经过期但未被扫描的键,当客户端尝试访问这些键时,Redis会检查其过期状态。如果键已经过期,Redis会在获取键的时候将其删除,并返回null值给客户端。
过期字典中的键是指向实际数据库键空间中键对象的指针,而值则是键的过期时间,以毫秒精度的Unix时间戳表示。通过这种方式,过期字典中的键和实际数据库中的键相互对应,避免了重复对象的出现,同时也最大程度地节省了空间。
typedef struct redisDb {
dict *dict; //键空间,存放所有的key-value键值对
dict *expires; //设置了过期时间的key到它的过期时间的键值对
} redisDb;
2
3
4
# 怎么用?
操作技巧:spring-data-redis api方法名是redis命令的全称
# 思考的点
- 该功能点是否需要用缓存?
- 直接使用一个 map 能否解决问题?
- 设计 redis 的key value
- 需要排序吗?
- 不需要排序,推荐使用 string,利用 json object 完成数据存储
# 整体总结
redis概念,定位,优点,缺点,运用
- redis 定位是一个高性能的缓存,可以帮助减轻数据库的访问压力
- 优点是速度快!能够满足高性能高并发下的性能要求,显著地降低数据库的存储与访问压力
- 缺点是不支持 acid 的特性,缺少一些关系性的维护
常用的数据类型,各种命令
我们平常比较常用的数据类型5个,第一个是 string,string 是最简单并且最通用的数据类型,当然,这也是我们平常工作中最常用的数据类型。它可以以文本方式存储字符串、整数以及浮点型的数据,甚至 object 对象也可以存储。第2个是哈希,哈希是用来存储某个特定对象的,类似于字典或者 map,可以将多个键子对存储在一个键里面,适合用来存储对象的属性。第3个是 set,set 就是无序集,它可以用来帮助我们完成去重或者随机抽取的操作。第4个是 sorted set,也叫做 zset,这个集合是根据 score 属性去进行排序的,当我们需要借助 redis 去帮我们完成自动排序,那么我们就需要用到 sorted set。第5个是 list list,它的实现方式有两种,第一个是双向链表,第2个是压缩列表。我们使用的时候只需要理解它是一个队列,并且支持从两端进行元素的插入和删除(仅提供 pop 以及 push api,类似于队列或栈,只能对两端的元素进行操作)
- 这里需要注意,在List类型中。redis 它这样的设计是为了保证整体的操作性能,并且我们常用的操作也是仅在数据的头和尾进行增删操作,而对中间元素的增删操作比较少见。所以 redis 更倾向于优化对数据的头尾元素的操作,是为了提高整体的性能效率
- 很多命令我们不知道的可以直接从 redis 里面拿到,其实跟我们 api 里面的操作是非常类似的
redis 全局命令
redis-cli -h 127.0.0.1
redis-cli -h 127.0.0.1 -a test123
2
3
dbsize
keys *
keys aa?
del key
expire key 5
ttl key
说明: >=0:表示剩余的过期时间 -1:不存在过期时间(持久存在) -2:找不到这个键,过期了或者不存在
2
3
4
5
6
7
8
9
10