关于redis:那些年我们用过的Redis还记得大明湖畔那些Redis数据吗

33次阅读

共计 9730 个字符,预计需要花费 25 分钟才能阅读完成。

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),先进先出。

常用命令

向队列任意地位退出元素

方才演示的 rpushlpush 都是从中间退出元素,这两个命令不再演示。还能够应用 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 前后插入了 rubyc#

查找元素

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能够遍历列表,参数为 startend。这里0 -1,是指从第一个到最初一个,即遍历列表。
lindex 查找指定地位的元素,参数是下标值。这个命令是 慢查问 ,须要遍历链表。
llen 能够查看列表元素个数。

删除数据

方才演示的 rpoplpop 能够弹出一个元素,不再演示。

能够应用 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)。

阻塞操作

blpopbrpoplpoprpop 根底上减少了阻塞工夫,如果间接获取,发现列表中没有数据,那么会阻塞期待一段时间,如果该段时间内还是无奈失去数据,就返回期待时长。若设置的工夫是 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"

hincrbyhincrbyfloat 别离减少或者缩小 整型 浮点型

计算值的长度

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很适宜缓存对象,比方商城零碎能够寄存商品,hashkey 为商品 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 各小组,每个小组算一个小组长 (留神小组长还具备员工角色,只不过多了小组长角色) 小组长再选出一个技术总监(技术总监同时具备员工、小组长、技术总监角色)

应用场景

适宜排名性质的场景,比方微博热搜,某技术网站热门博客等等。

总结不易,小伙伴给个赞再走吧。

正文完
 0