redis 五种罕用的数据结构为 string (字符串)、list (列表)、set (汇合)、hash (哈希) 和 zset (有序汇合)。小白易读,倡议珍藏。
万丈高楼平地起
reids 是 键值对 构造的 NoSql 数据库,key
都是字符串,常说的数据类型不同,说的都是 value
。
redis 所有的数据都会有一个dicEntry
,泛滥dicEntry
组成一个链表。上方那个 sds
就是 key
,能够看出是一个字符串。下方那个绿色的redisObject
就是 value
。能够看出图中给的例子就是string
类型。redisObject
会指向实在的数据 (比方图中的字符串“world”)。前面咱们说的数据类型特指value
局部。
string (字符串)
Redis 的字符串是动静字符串,是能够批改的字符串。当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间。一个字符串最大能够接受 512M。
罕用指令
设置获取值
127.0.0.1:6379> set name pjjlt
OK
127.0.0.1:6379> get name
"pjjlt"
127.0.0.1:6379> exists name
(integer) 1
设置应用 set
,获取应用get
,查看某key 是否存在用exists
。
设置过期工夫
127.0.0.1:6379> setex company 10 gongsi
OK
127.0.0.1:6379> get company
"gongsi"
127.0.0.1:6379> get company
(nil)
能够在设置值的时候间接指定,keycompany
能够存活 10 秒。此外,也能够将 设置值 和设置过期工夫 离开,应用expire
。
127.0.0.1:6379> set company gongsi
OK
127.0.0.1:6379> expire company 10
(integer) 1
127.0.0.1:6379> get company
"gongsi"
127.0.0.1:6379> get company
(nil)
保障不笼罩 value
redis 还提供了命令,在设置值的时候,如果发现 key
已存在,此次设置失败,保障原始 value
不被笼罩。应用 setnx
命令。
127.0.0.1:6379> setnx company gongsi
(integer) 1
# 能够看到第二次设置失败,返回值为 0.
127.0.0.1:6379> setnx company haha
(integer) 0
127.0.0.1:6379> get company
"gongsi"
批量设置获取值
127.0.0.1:6379> mset name pjjlt age 26 company gongsi
OK
127.0.0.1:6379> mget name age company
1) "pjjlt"
2) "26"
3) "gongsi"
批量设置应用mset
,批量获取应用mget
。批量设置获取,缩小 IO,进步性能,你值得领有。
计数
redis 还能够通过 自增 的形式计数。
127.0.0.1:6379> set key 10
OK
127.0.0.1:6379> incr key
(integer) 11
127.0.0.1:6379> incr key
(integer) 12
# 字符串报错
127.0.0.1:6379> set key2 haha
OK
127.0.0.1:6379> incr key2
(error) ERR value is not an integer or out of range
# 超出 long 的范畴
127.0.0.1:6379> set key3 9223372036854775807
OK
127.0.0.1:6379> incr key3
(error) ERR increment or decrement would overflow
# key4 不存在
127.0.0.1:6379> incr key4
(integer) 1
127.0.0.1:6379> incr key4
(integer) 2
能够通过 incr
关键字自增,能够看出 key 自增了两次。不能给字符串自增,那样会报错,例如 key2。不能超过 long
的范畴,那样也会报错,例如 key3。如果初始key 不存在,则增从 0 开始,例如 key4。
追加值
127.0.0.1:6379> set name pj
OK
127.0.0.1:6379> append name jlt
(integer) 5
127.0.0.1:6379> get name
"pjjlt"
字符串长度
127.0.0.1:6379> get name
"pjjlt"
127.0.0.1:6379> strlen name
(integer) 5
设置并返回原先值
127.0.0.1:6379> get name
"pjjlt"
127.0.0.1:6379> getset name mj
"pjjlt"
127.0.0.1:6379> get name
"mj"
设置指定地位的字符
127.0.0.1:6379> get name
"mj"
127.0.0.1:6379> setrange name 0 p
(integer) 2
127.0.0.1:6379> get name
"pj"
获取局部字符串
127.0.0.1:6379> set name pjjlt
OK
127.0.0.1:6379> getrange name 0 2
"pjj"
总结
命令 | 解释 |
---|---|
set | 设置值 |
get | 获取值 |
setex | 设置值并增加过期工夫 |
setnx | 保障不笼罩 value |
mset | 批量设置值 |
mget | 批量获取值 |
incr | 计数 |
append | 追加值 |
strlen | 字符串长度 |
getset | 设置并返回原先值 |
setrange | 设置指定地位的字符 |
getrange | 获取局部字符串 |
外部编码
尽管某种数据类型的 value
名称是统一的,比方都是 string
,然而依据数据量的大小,会采纳不同的 外部编码 ,这样能够更高效的利用空间嘛。外部编码类型也贮存在redisObject
中。利用 object encoding key
可查看外部编码类型。
int
:长整型,超过长整型或者是字符串会降级。
embstr
:小于等于 44 个字节的字符串。笔者用的是 redis5.0.9,有人说这个字节范畴是 39,亲测是 44。查了一下,源码的确改了,当初是 44.
raw
:大于 44 个字节的字符串。
127.0.0.1:6379> set name 1234567890
OK
127.0.0.1:6379> object encoding name
"int"
# 这里设置 44 个字符
127.0.0.1:6379> set name qwertyuiopqwertyuiopqwertyuiopqwertyuiopqwer
OK
127.0.0.1:6379> object encoding name
"embstr"
# 这里设置 45 个字符
127.0.0.1:6379> set name qwertyuiopqwertyuiopqwertyuiopqwertyuiopqwert
OK
127.0.0.1:6379> object encoding name
"raw"
应用场景
能够用于 计数
,比方网站访问量。
能够 共享 Session
,比方分布式系统,多个实例验证用户是否登录。
能够 限速
,比方管制一个 ip 或者一个用户 肯定工夫内 拜访次数。
list (列表)
Redis 的列表相当于 Java 语言外面的 LinkedList,留神它是链表而不是数组。这意味着 list 的插入和删除操作十分快,工夫复杂度为 O(1),然而索引定位很慢,工夫复杂度为 O(n)。list 的两端都能够弹入弹出数据,所以能够做 栈
和 队列
。
栈与队列
栈
栈如同一个死胡同,只有一个进口,后进来的先出,先进来的后出。
127.0.0.1:6379> rpush books python java golong
(integer) 3
127.0.0.1:6379> rpop books
"golong"
127.0.0.1:6379> rpop books
"java"
127.0.0.1:6379> rpop books
"python"
127.0.0.1:6379> rpop books
(nil)
数据从左边进(rpush),左边出(rpop),先进去的最初进去。
队列
队列如同排队打饭的同学们,先进先出。
127.0.0.1:6379> rpush books python java golong
(integer) 3
127.0.0.1:6379> lpop books
"python"
127.0.0.1:6379> lpop books
"java"
127.0.0.1:6379> lpop books
"golong"
127.0.0.1:6379> lpop books
(nil)
数据从左边进(rpush),右边出(lpop),先进先出。
常用命令
向队列任意地位退出元素
方才演示的 rpush
、lpush
都是从中间退出元素,这两个命令不再演示。还能够应用 linsert
在某指定元素前或后插入新的元素。
127.0.0.1:6379> rpush books python java golong
(integer) 3
# 在 java 后面插入 ruby
127.0.0.1:6379> linsert books before java ruby
(integer) 4
# 在 java 前面插入 c#
127.0.0.1:6379> linsert books after java c#
(integer) 5
# 查看所有元素
127.0.0.1:6379> lrange books 0 -1
1) "python"
2) "ruby"
3) "java"
4) "c#"
5) "golong"
依据下面在 java
前后插入了 ruby
和c#
查找元素
127.0.0.1:6379> lrange books 0 -1
1) "python"
2) "ruby"
3) "java"
4) "c#"
5) "golong"
127.0.0.1:6379> lindex books 2
"java"
127.0.0.1:6379> llen books
(integer) 5
指令简略,索性写一块吧。lrange
能够遍历列表,参数为 start
,end
。这里0 -1,是指从第一个到最初一个,即遍历列表。lindex
查找指定地位的元素,参数是下标值。这个命令是 慢查问 ,须要遍历链表。llen
能够查看列表元素个数。
删除数据
方才演示的 rpop
、lpop
能够弹出一个元素,不再演示。
能够应用 lrem 删除多个同一元素
count > 0:从左到右,删除最多 count 个元素。
count < 0:从右到左,删除最多 count 绝对值 个元素。
count = 0,删除所有。
# 从左删除 a 元素,删除了 3 个
127.0.0.1:6379> rpush char a a b b a a c
(integer) 7
127.0.0.1:6379> lrem chae 3 a
(integer) 0
127.0.0.1:6379> lrem char 3 a
(integer) 3
127.0.0.1:6379> lrange char 0 -1
1) "b"
2) "b"
3) "a"
4) "c"
# 从右删除 3 个 a 元素
127.0.0.1:6379> rpush char1 a a b b a a c
(integer) 7
127.0.0.1:6379> lrem char1 -3 a
(integer) 3
127.0.0.1:6379> lrange char1 0 -1
1) "a"
2) "b"
3) "b"
4) "c"
# 删除所有 a 元素
127.0.0.1:6379> rpush char2 a a b b a a c
(integer) 7
127.0.0.1:6379> lrem char2 0 a
(integer) 4
127.0.0.1:6379> lrange char2 0 -1
1) "b"
2) "b"
3) "c"
还能够应用 ltrim
截取一部分数据,删除其余数据
127.0.0.1:6379> rpush char3 a b c d e f g
(integer) 7
127.0.0.1:6379> ltrim char3 1 3
OK
127.0.0.1:6379> lrange char3 0 -1
1) "b"
2) "c"
3) "d"
批改
127.0.0.1:6379> lrange books 0 -1
1) "python"
2) "ruby"
3) "java"
4) "c#"
5) "golong"
127.0.0.1:6379> lset books 2 javaScript
OK
127.0.0.1:6379> lrange books 0 -1
1) "python"
2) "ruby"
3) "javaScript"
4) "c#"
5) "golong"
能够用 lset
更改某个地位上的元素,这也是个 慢查问,工夫复杂度为 O(n)。
阻塞操作
blpop
和 brpop
在lpop
和 rpop
根底上减少了阻塞工夫,如果间接获取,发现列表中没有数据,那么会阻塞期待一段时间,如果该段时间内还是无奈失去数据,就返回期待时长。若设置的工夫是 0
的话,即为有限期待。这里须要两个终端做配合。
# 终端 1
127.0.0.1:6379> lpop books
(nil)
127.0.0.1:6379> blpop books 5
(nil)
(5.06s)
# 这里须要在终端 1 执行 blpop 后插入一条数据
127.0.0.1:6379> blpop books 20
1) "books"
2) "java"
(4.61s)
# 这里须要在终端 1 执行 blpop 后插入一条数据
127.0.0.1:6379> blpop books 0
1) "books"
2) "python"
(9.66s)
# 除此之外,还能够同时阻塞多个队列,先有数据的那个弹出
127.0.0.1:6379> blpop books schools 0
1) "schools"
2) "hzsy"
(26.75s)
总结
命令 | 解释 |
---|---|
rpush lpush | 弹入数据 |
rpop lpop | 弹出数据 |
brpop blpop | 阻塞弹出数据 |
linsert | 向队列任意地位退出元素 |
lrange | 遍历列表 |
lindex | 查找指定地位上元素 |
llen | 列表长度 |
lrem | 删除多个同一元素 |
ltrim | 截取指定列表 |
lset | 批改列表指定地位元素 |
外部编码
ziplist
:当列表的元素个数小于 list-max-ziplist-entries
配置(默认 512 个),同时列表中每个元素的值都小于 list-max-ziplist-value
配置时(默认 64 字节),Redis 会选用 ziplist
来作为 列表 的外部实现来缩小内存的应用。linkedlist
:当列表类型无奈满足 ziplist
的条件时,Redis 会应用 linkedlist
作为列表的外部实现。
应用场景
能够做 栈
或者 队列
。
还能够利用阻塞性能做 音讯队列
。
hash (哈希)
Redis 的字典相当于 Java 语言外面的 HashMap,它是无序字典。外部实现构造上同 Java 的 HashMap 也是统一的,同样的数组 + 链表二维构造。扩容 rehash
的时候,采纳渐进式。在 rehash
时,保留两个新旧 hash 构造,查问的时候都查,再依据定时工作,一点点将旧 hash 上的数据迁徙到新的 hash 上,迁徙结束,旧 hash 被删除,并发出内存。咱们默认 key 为 hashKey
,filed 为 小 key
。
常用命令
设置值
127.0.0.1:6379> hset user name pjjlt
(integer) 1
127.0.0.1:6379> hset user age 26
(integer) 1
127.0.0.1:6379> hset user company gongsi
(integer) 1
获取值
127.0.0.1:6379> hget user name
"pjjlt"
删除 field
127.0.0.1:6379> hdel user company
(integer) 1
计算 field 个数
127.0.0.1:6379> hlen user
(integer) 2
批量设置获取值
127.0.0.1:6379> hmset user name pjjlt age 26 city shijiazhuang
OK
127.0.0.1:6379> hmget user name age
1) "pjjlt"
2) "26"
判断 filed 是否存在
127.0.0.1:6379> hexists user name
(integer) 1
获取所有 filed 或者 value
127.0.0.1:6379> hkeys user
1) "name"
2) "age"
3) "city"
127.0.0.1:6379> hvals user
1) "pjjlt"
2) "26"
获取所有 filed 和 value
127.0.0.1:6379> hgetall user
1) "name"
2) "pjjlt"
3) "age"
4) "26"
5) "city"
6) "shijiazhuang"
自增
127.0.0.1:6379> hincrby user age -8
(integer) 18
127.0.0.1:6379> hset user scroe 99.6
(integer) 1
127.0.0.1:6379> hincrbyfloat user scroe 0.4
"100"
hincrby
和 hincrbyfloat
别离减少或者缩小 整型 与浮点型。
计算值的长度
127.0.0.1:6379> hget user name
"pjjlt"
127.0.0.1:6379> hstrlen user name
(integer) 5
总结
命令 | 解释 |
---|---|
hset | 设置值 |
hget | 获取值 |
hdel | 删除值 |
hlen | 计算 field 个数 |
hmset | 批量设置值 |
hmget | 批量获取值 |
hexists | 判断 field 是否存在 |
hkeys | 获取所有 field |
hvals | 获取所有 value |
hgetall | 获取所有 filed 和 value |
hincrby | 减少整型数值 |
hincrbyfloat | 减少浮点型数值 |
hstrlen | 计算值的长度 |
外部编码
ziplist
:当列表的元素个数小于 list-max-ziplist-entries
配置(默认 512 个),同时列表中每个元素的值都小于 list-max-ziplist-value
配置时(默认 64 字节),Redis 会选用 ziplist
来作为 列表 的外部实现来缩小内存的应用。
hashtable: 当哈希类型无奈满足 ziplist
的条件时,Redis 会应用 hashtable
作为哈希的外部实现,因为此时 ziplist
的读写效率会降落,而 hashtable
的读写工夫复杂度为 O(1)。
应用场景
hash
很适宜缓存对象,比方商城零碎能够寄存商品,hash
key 为商品 id,field
为各种属性,value
为数据。当然 string
也能够寄存商品,只不过它的value
,时 json 串,还须要解析,从代码角度和网络代价来讲都不如hash
。
set (汇合)
set
相当于 Java 语言外面的 HashSet,它外部的键值对是无序的惟一的。它的外部实现相当于一个非凡的字典,字典中所有的 value
都是一个值 NULL。
常用命令
减少元素
127.0.0.1:6379> sadd books java python python ruby java
(integer) 3
sadd
能够增加一个或者多个元素,并且去重。
删除元素
127.0.0.1:6379> srem books python ruby
(integer) 2
srem
能够删除一个或者多个元素。
计算元素个数
127.0.0.1:6379> sadd books python ruby c#
(integer) 3
127.0.0.1:6379> scard books
(integer) 4
判断元素是否在汇合中
127.0.0.1:6379> sismember books java
(integer) 1
127.0.0.1:6379> sismember books c
(integer) 0
随机返回肯定数量的元素
127.0.0.1:6379> srandmember books 2
1) "java"
2) "ruby"
127.0.0.1:6379> srandmember books 2
1) "c#"
2) "ruby"
随机弹出一个元素
127.0.0.1:6379> spop books
"ruby"
127.0.0.1:6379> scard books
(integer) 3
获取所有元素
127.0.0.1:6379> smembers books
1) "c#"
2) "java"
3) "python"
计算并查集
127.0.0.1:6379> sadd set1 a b c d e
(integer) 5
127.0.0.1:6379> sadd set2 d e f g
(integer) 4
# 计算两个汇合交加
127.0.0.1:6379> sinter set1 set2
1) "e"
2) "d"
# 计算两个汇合并集
127.0.0.1:6379> sunion set1 set2
1) "g"
2) "a"
3) "d"
4) "e"
5) "c"
6) "f"
7) "b"
# 计算两个汇合差集
127.0.0.1:6379> sdiff set1 set2
1) "c"
2) "b"
3) "a"
总结
命令 | 解释 |
---|---|
sadd | 减少元素 |
srem | 删除元素 |
scard | 计算元素个数 |
sismember | 判断元素是否在汇合中 |
srandmember | 随机返回肯定数量的元素 |
spop | 随机弹出一个元素 |
smembers | 获取所有元素 |
sinter | 计算两个汇合交加 |
sunion | 计算两个汇合并集 |
sdiff | 计算两个汇合差集 |
外部编码
intset
:当汇合中的元素都是整数且元素个数小于 set-max-intset-entries
配置 (默认 512 个) 时,Redis 会选用 intset
来作为汇合的外部实现,从而缩小内存的应用。
hashtable
:当汇合类型无奈满足 intset
的条件时,Redis 会应用 hashtable
作为汇合的外部实现。
应用场景
利用 并查集
能够用于查找用户共同爱好。
利用 不可重复性
,能够用于抽奖,保障每个用户只能中一次奖。
zset(有序汇合)
zset 可能是 Redis 提供的最为特色的数据结构。它相似于 Java 的 SortedSet 和 HashMap 的结合体,一方面它是一个 set,保障了外部 value 的唯一性,另一方面它能够给每个 value 赋予一个 score,代表这个 value 的排序权重。
常用命令
# 设置值
127.0.0.1:6379> zadd books 9 java
(integer) 1
127.0.0.1:6379> zadd books 8 python
(integer) 1
127.0.0.1:6379> zadd books 7 golang
(integer) 1
# 查看肯定范畴内的值
127.0.0.1:6379> zrange books 0 -1
1) "golang"
2) "python"
3) "java"
# 删除某个值
127.0.0.1:6379> zrem books golang
(integer) 1
# 依据 score 正序排
127.0.0.1:6379> zrange books 0 -1
1) "python"
2) "java"
# 依据 score 顺叙排
127.0.0.1:6379> zrevrange books 0 -1
1) "java"
2) "python"
# 查看元素个数
127.0.0.1:6379> zcard books
(integer) 2
# 查看某元素分值
127.0.0.1:6379> zscore books java
"9"
# 正序排名,从 0 开始
127.0.0.1:6379> zrank books python
(integer) 0
127.0.0.1:6379> zrank books java
(integer) 1
# 肯定范畴内 scor 内的元素
127.0.0.1:6379> zrangebyscore books 0 8.8
1) "python"
总结
命令 | 解释 |
---|---|
zadd | 设置值 |
zrange | 查看肯定范畴内的值 |
zrem | 删除某个值 |
zrange | 依据 score 正序排 |
zrevrange | 依据 score 顺叙排 |
zcard | 查看元素个数 |
zscore | 查看某元素分值 |
zrank | 正序排名,从 0 开始 |
zrangebyscore | 肯定范畴内 scor 内的元素 |
外部编码
zset 外部的排序功能是通过「跳跃列表」数据结构来实现的,它的构造十分非凡,也比较复杂。举个例子吧,就如同一个公司,有 9 个员工,分为 3 各小组,每个小组算一个小组长 (留神小组长还具备员工角色,只不过多了小组长角色) 小组长再选出一个技术总监(技术总监同时具备员工、小组长、技术总监角色)
应用场景
适宜排名性质的场景,比方微博热搜,某技术网站热门博客等等。
总结不易,小伙伴给个赞再走吧。