Redis有五大数据结构:string、list、hash、set和zset,在开发中使用频率很高,掌握了这五个数据结构基本上就掌握了Redis内容的一半了。本文主要对几种数据类型的基本操作以及使用场景进行归纳。
string表示的是一个可变的字节数组,Redis的字符串是动态字符数组,可以进行修改,内部结构实现上类似于Java的,采用预分配冗余空间的方式来减少内存的频繁分配。字符串实际分配的空间一般高于字符串长度,当字符串小于1M时,扩容时采用加倍现有的空间,超过1M,扩容时一次最多扩1M的空间。
set 变量名+变量内容
> set reader beijing.zhangyue.keji.gufen.youxian.gongsi
OK
get +变量名
> get reader
"beijing.zhangyue.keji.gufen.youxian.gongsi"
strlen +变量名
> strlen reader
(integer) 42
getrange +变量名+开始位置+结束位置
> getrange reader 28 34
"youxian"
setrange +变量名称+开始位置+目标字串
> setrange reader 28 wooxian
(integer) 42 # 返回长度
> get ireader
"beijing.zhangyue.keji.gufen.wooxian.gongsi"
在字符串末尾添加
> append reader .hao
(integer) 46 # 返回长度
> get reader
"beijing.zhangyue.keji.gufen.wooxian.gongsi.hao"
注:字符串没有提供字符插入方法和子串删除方法
字符串可以使用del指令进行主动删除,使用expire指令设置过期时间,时间一过自动删除(被动删除),可以使用ttl指令获取字符串的寿命
> expire reader 60
(integer) 1 # 1表示设置成功,0表示变量reader不存在
> ttl reader
(integer) 50 # 还有50秒的寿命,返回-2表示变量不存在,-1表示没有设置过期时间
> del reader
(integer) 1 # 删除成功返回1
> get reader
(nil) # 变量reader没有了
如果字符串的内容是整数,也可以当成计数来使用
> set reader 42
OK
> get reader
"42"
> incrby reader 100
(integer) 142
> get reader
"142"
> decrby reader 100
(integer) 42
> get ireader
"42"
> incr reader # 等价于incrby reader 1
(integer) 43
> decr reader # 等价于decrby reader 1
(integer) 42
由于Redis具有支撑高并发的特性,通常能起到加速读写和减低后端压力的作用,web端的大多数请求都是从Redis中获取的数据,如果Redis中没有需要的数据,则会从MySQL中去获取,并将获取到的数据写入Redis
Redis中有一个字符串相关的incr key,incr命令对值做自增操作,返回结果分为以下三种情况:
*值不是整数,返回错误
*值是整数,返回自增后的结果
*key不存在,默认键为0,返回1
文章的阅读量,视频的播放量等都会使用redis来计数,每播放一次,对应的播放量就会加1,同时将这些数据异步存储到数据库中达到持久化的目的。
在分布式系统中,用户的每一次请求都会访问到不同的服务器,这就会导致session不同步的问题,假如一个用来获取用户信息的请求落在A服务器上,获取到用户信息后存入session。下一个请求落在B服务器上,想要从session中获取用户信息就不能正常获取了,因为用户信息的session在服务器A上。使用Redis集中管理这些session,将session存入Redis,使用的时候直接从Redis中获取。
为了安全考虑,有些网站会对IP进行限制,限制同一IP在一定时间内访问次数不能超过n次。
Redis列表的存储结构用的是双向链表,对列表进行首尾插入删除性能比较好,链表元素的位置使用自然数0,1,2,3,…,n-1表示,还可以使用负数-1,-2,…,-n来表示,-1表示倒数第一,-2表示倒数第二,-n表示第一个元素,对应的下标为0.
# 右进左出
> rpush reader go
(integer) 1
> rpush reader java python
(integer) 3
> lpop reader
"go"
> lpop reader
"java"
> lpop reader
"python"
# 左进右出
> lpush reader go java python
(integer) 3
> rpop reader
"go"
...
# 右进右出
> rpush reader go java python
(integer) 3
> rpop reader
"python"
...
# 左进左出
> lpush reader go java python
(integer) 3
> lpop reader
"python"
> rpush reader go java python
(integer) 3
> llen reader
(integer) 3
lindex+变量名+位置来访问指定位置的元素;lrange+变量名+开始位置+结束位置来获取链表子元素列表;使用lrange获取全部元素时,需要提供end_index,如果没有负下标,需要通过llen获取长度,得到end_index,有负下标同样可以用-1替代。
> rpush reader go java python
(integer) 3
> lindex reader 1
"java"
> lrange reader 0 2
1) "go"
2) "java"
3) "python"
> lrange reader 0 -1 # -1表示倒数第一
1) "go"
2) "java"
3) "python"
lset+变量名+指定位置+修改值
> rpush reader go java python
(integer) 3
> lset reader 1 javascript
OK
> lrange reader 0 -1
1) "go"
2) "javascript"
3) "python"
linsert +变量名+before/after+指定位置(元素)+要插入的值
> rpush reader go java python
(integer) 3
> linsert reader before java ruby
(integer) 4
> lrange reader 0 -1
1) "go"
2) "ruby"
3) "java"
4) "python"
lrem+变量名+指定删除的最大个数+要删除的元素的值(可以是多个)
> rpush reader go java python
(integer) 3
> lrem reader 1 java
(integer) 1
> lrange reader 0 -1
1) "go"
2) "python"
ltrim+变量名+start+end,表示需要保留列表的下标范围,范围之外的所有元素都将会被移除,如果指定参数的end对应真实的下标小于start,其效果等价于del,因为这样的参数表示需要保留列表元素的下标范围为空。
> rpush reader go java python javascript ruby erlang rust cpp
(integer) 8
> ltrim reader -3 -1
OK
> lrange reader 0 -1
1) "erlang"
2) "rust"
3) "cpp"
ziplist 经过特殊编码的双向列表结构,在列表元素较少的情况下会使用的一块连续的内存存储,数据量比较多的时候会改成quicklist。
列表用来存储多个有序的字符串,满足消息队列的特点,使用lpush+rpop或者rpush+lpop实现消息队列。redis还支持阻塞操作,在弹出元素的时候使用阻塞命令来实现阻塞命令。
列表满足栈,那么也满足栈先进后出的特点。
列表支持索引范围获取元素,可以分页获取文章列表。
等价于Java的HashMap,实现结构上使用二维结构,第一维是数组,第二维是链表,数组里存放的是链表的头指针,hash的内容key和value存放在链表中。通过key查找元素时,先计算key的hashcode,然后用hashcode对数组的长度进行取模定位到链表的表头,再对链表进行遍历获取到相应的value值。
hget定位具体key对应的value,hmget获取多个key对应的value,hgetall获取所有的键值对,hkey和hvals获取所有的列表和value值
> hmset reader go fast java fast python slow
OK
> hget reader go
"fast"
> hmget reader go python
1) "fast"
2) "slow"
> hgetall reader
1) "go"
2) "fast"
3) "java"
4) "fast"
5) "python"
6) "slow"
> hkeys reader
1) "go"
2) "java"
3) "python"
> hvals reader
1) "fast"
2) "fast"
3) "slow"
使用hdel删除指定的key,可以同时删除多个key
> hmset reader go fast java fast python slow
OK
> hdel reader go
(integer) 1
> hdel reader java python
(integer) 2
hexists指令
> hmset reader go fast java fast python slow
OK
> hexists reader go
(integer) 1
采用渐进式rehash,同时保留两个新旧hash结构,申请新的两倍大下的数组,然后将所有的键值对重新分配到新的数组下标对应的链表中。
与扩容原理一致,只不过新的数组大小要比旧数组小一倍。
hash对于每一个内部的key都可以用作一个单独的计数器,如果value不是整数,调用会出错。
> hincrby reader go 1
(integer) 1
> hincrby reader python 4
(integer) 4
> hincrby reader java 4
(integer) 4
> hgetall reader
1) "go"
2) "1"
3) "python"
4) "4"
5) "java"
6) "4"
> hset reader rust good
(integer) 1
> hincrby reader rust 1
(error) ERR hash value is not an integer
存储、读取、修改用户属性
内部使用hash结构,所有的value指向同一个内部值。
一次可以增加多个元素
> sadd reader go java python
(integer) 3
使用smembers列出所有元素,使用scard获取集合长度,使用srandmembers获取随机count个元素,如果没有count参数,默认1.
> sadd reader go java python
(integer) 3
> smembers reader
1) "java"
2) "python"
3) "go"
> scard reader
(integer) 3
> srandmember reader
"java"
srem删除一到多个元素,spop删除随机一个元素
> sadd reader go java python rust erlang
(integer) 5
> srem reader go java
(integer) 2
> spop reader
"erlang"
sismember,接收单个元素
> sadd reader go java python rust erlang
(integer) 5
> sismember reader rust
(integer) 1
> sismember reader javascript
(integer) 0
抽奖功能:支持获取随机数;用户标签:将同一个特征相同的抽象为标签,例如一个用户对篮球、足球感兴趣,另一个用户对橄榄球、乒乓球感兴趣,这些兴趣点就是一个标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同感兴趣的标签。给用户打标签的时候需要①给用户打标签,②给标签加用户,需要给这两个操作增加事务。
等价于Map
zadd,增加一到多个score/value对
> zadd reader 4.0 python
(integer) 1
> zadd reader 4.0 java 1.0 go
(integer) 2
zcard得到元素个数
> zcard reader
(integer) 3
zrem删除元素,可以一次删除多个
> zrem reader go python
(integer) 2
通过zscore获取自动元素的权重,zrank获取指定元素的正向排名,zrevrank获取指定元素的反向排名
> zscore reader python
"5"
> zrank reader go # 分数低的排名考前,rank值小
(integer) 0
> zrank reader java
(integer) 1
> zrank reader python
(integer) 2
> zrevrank reader python
(integer) 0
zrange指定排名范围参数获取对应的列表,加withscores 可以获取元素的权重,zrevrange按负向排名获取元素列表
> zrange reader 0 -1 # 获取所有元素
1) "go"
2) "java"
3) "python"
> zrange reader 0 -1 withscores
1) "go"
2) "1"
3) "java"
4) "4"
5) "python"
6) "5"
> zrevrange reader 0 -1 withscores
1) "python"
2) "5"
3) "java"
4) "4"
5) "go"
6) "1"
zrangebbyscore指定score范围获取对应的元素列表,zrevrangeyscore获取倒排元素列表,-inf表示负无穷,+inf表示正无穷
> zrangebyscore reader 0 5
1) "go"
2) "java"
3) "python"
> zrangebyscore reader -inf +inf withscores
1) "go"
2) "1"
3) "java"
4) "4"
5) "python"
6) "5"
> zrevrangebyscore reader +inf -inf withscores # 注意正负反过来了
1) "python"
2) "5"
3) "java"
4) "4"
5) "go"
6) "1"
通过排名范围,也可以通过score范围来一次性移除多个元素
> zremrangebyrank reader 0 1
(integer) 2 # 删掉了2个元素
> zadd reader 4.0 java 1.0 go
(integer) 2
> zremrangebyscore reader -inf 4
(integer) 2
> zrange reader 0 -1
1) "python"
排行榜:例如用户发表文章,用score来表示点赞量,根据点赞量进行排行;延迟消息队列,例如下单系统,下单后需要在15分钟内支付,如果15分钟内未支付,则订单取消,下单后的15分钟时间作为score,订单作为value存入redis,如果消费的时间大于这笔记录的score,则将这笔记录移除订单。
以上就是Redis五大数据结构以及相关用法和使用场景