Redis 是一个开源,高级的键值存储和一个适用的解决方案,用于构建高性能,可扩展的 Web 应用程序。Redis 也被作者戏称为数据结构服务器 ,这意味着使用者可以通过一些命令,基于带有 TCP 套接字的简单 服务器-客户端 协议来访问一组可变数据结构 。(在 Redis 中都采用键值对的方式,只不过对应的数据结构不一样罢了)。
Redis 有 5 种基础数据结构,它们分别是:string(字符串)、list(列表)、hash(字典)、set(集合) 和 zset(有序集合)。这 5 种是 Redis 相关知识中最基础、最重要的部分,下面我们说一下它们基本的操作命令和应用场景。
进入redis客户端:
redis-cli -a -c -h -p // -a访问服务端密码,-c表示集群模式,-h指定主机ip,指定端口号)
redis-cli // 本机6379 无密码 单体模式
redis-cli -a 123456 -p 6374 -c // 本机6374 密码123456 集群模式
1.String
字符串 string 是Redis 最简单的数据结构。Redis 所有的数据结构都是以唯一的 key 字符串作为名称,然后通过这个唯一 key 值来获取相应的 value 数据。
1.1 常用命令
1.增(set)
set key value // 存入字符串键值对
mset key value [key value...] // 批量存储字符串键值对
setnx key value // 存入一个不存在的键值对(只有当前key不存在时才能增加成功)
2.删除与过期
del key [key...] // 删除一个键
expire key seconds // 设置过期时间(秒)
3.原子操作
incr key // 将key中存储的数字加1
incrby key increment // 将key锁存储的值加上increment
decr key // 将key中存储的数字减1
decr key decrement // 将key所存储的值减去decrement
4.查(get)
get key // 获取一个字符串的键值
mget key [key...] // 批量获取字符串键值
1.2 应用示例
1.对象缓存
- 方式一:利用set命令,一次将对象的json字符串存入
set user:1 value(对象的json字符串)
- 方式二:利用mset将对象的各个字段分开存储。这种方案更加灵活,可以对指定字段进行操作
mset user:1:name zhangsan user:1:age 18
mget user:1:name ~~ user:1:age ~~
2.简单分布式锁
通过setnx只能新增不存在的可以进行简单的分布式锁
PS:分布式锁问题就是放大版的单机线程同步问题,这不过这里不再是一个个线程,而是一台*立的服务器(进程)。
setnx product true // 进程一设置成功 == 获取到锁
setnx product true // 进程二再来时设置失败 == 阻塞
...... // 操作临界资源
del product // 进程一释放锁
注意:一般最好设置过期时间来避免死锁
set product true ex 10 nx // 过期时间=10s
3.计数器
incr article:readcount:{文章id}
get article:readcount:{文章id}
4.Web集群Session共享
spring session + redis 实现session共享
2.Hash(哈希表)
Redis 的字典相当于Java 语言里面的 HashMap,它是无序字典。内部实现结构上同 Java 的 HashMap 也是一致的,同样的数组 + 链表二维结构。第一维 hash 的数组位置碰撞时,就会将碰撞的元素使用链表串接起来。
优点:
- 同类数据归类整合存储,方便数据管理
- 相比string操作消耗内存与cpu更小
- 相比string更节省空间
缺点:
- 过期功能不能用在field上,只能用在key上
- Redis集群架构下不适合大规模使用
2.1 常用命令
1.增(hset)
hset key filed value // 存储一个哈希表key的剑指
hmset key filed value [filed value...] // 在一个哈希表key中存储多个键值对
hsetnx key filed value // 存储一个不在的哈希表key的键值
2.删
hdel key filed [key filed...] // 删除哈希表key中的field键值
3.原子操作
hincrby key filed increment // 为哈希表key中field键的值加上增量increment
4.查(hget)
hget key filed // 获取哈希表key对应的field键值
hmget key filed [key filed...] // 批量获取哈希表key中多个field键值
hlen key // 返回哈希表key中field的数量
hgetall key // 返回哈希表中所有的键值
2.2 应用示例
1.对象缓存
用hash结构存储对象相较于string:cpu占用小,而且更节省空间
hmset user:1 name zhangsan age 18
hmget user:1 name age
2.模拟购物车(字段修改)
以用户id为key,商品id为field,商品数量为value
hset cart:1001 10088 1 // 添加商品
hincrby cart:1001 10088 1 // 增加数量
hlen cart:1001 // 商品总数
hdel cart:1001 // 删除商品
hgetall cart:1001 // 获取购物车所有商品
3.List(队列&栈)
Redis 的列表相当于 Java 语言里面的 LinkedList,注意它是链表而不是数组。这意味着 list 的插入和删除操作非常快,时间复杂度为 O(1),但是索引定位很慢,时间复杂度为 O(n),这点让人非常意外。 当列表弹出了最后一个元素之后,该数据结构自动被删除,内存被回收。
3.1 常用命令
1.存(push)
lpush key value [key value...] // 将一个或多个值value插入到key列表的表头(最左边)
rpush key value [key value...] // 将一个或多个值value插入到key列表的表尾(最右边)
2.取(pop)
lpop key // 移除并返回key列表的头元素
rpop key // 溢出并返回key列表的尾元素
blpop key timeout // 从key列表表头弹出一个元素,若列表中没有元素,阻塞等待timeout秒,如果timeout=0,一直阻塞等待
brpop key timeout // 从key列表表尾弹出一个元素,若列表中没有元素,阻塞等待timeout秒,如果timeout=0,一直阻塞等待
lrange key start stop // 范围取(range),返回列表key中指定区间[start,stop]内的元素,
3.2 应用示例
1.实现栈,队列,阻塞队列
一般都使用lpush,pop看具体场景要求
stack(栈) = lpush + lpop // FILO
queue(队列) = lpush + rpop // FIFO
blockingqueue(阻塞队列) = lpush + brpop
2.订阅(消息队列)
list可以用于微博和微信的订阅消息,比如我关注了某个公众号,它一发文章我就能收到
- 服务器通过redis为每个用户都维护一个消息队列
- 当某个用户发送消息后,通过查数据库查出订阅者的uId,然后向指定消息队发送
- 待订阅者打开相关页面时,再从相应消息队列里取出
//...消息发送方查出订阅者uId
// 向订阅者的通道发送消息
lpush msg:uId msgId1
lpush msg:uId msgId2
// 从队列中取出消息给订阅者
lrange msg:uId 0 5 // 取5条msg
4.Set(随机队列)
Redis 的集合相当于 Java 语言里面的 HashSet,它内部的键值对是无序的唯一的。它的内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值NULL。 当集合中最后一个元素移除之后,数据结构自动删除,内存被回收。
4.1 常用命令
1.存(sadd)
sadd key member [member...] // 向集合中存入元素,元素存在则忽略,key不存在则新建
2 取(srandmember&spop)
srandmember key [count] // 随机取count个元素,不删除
spop key [count] // 随机取count个元素,删除
3.查 & 删
smembers key // 获取集合key中所有元素
scard key // 获取集合key的元素个数
smember key member // 判断元素member是否在集合key中
srem key member [member...] // 从集合key中删除元素
4.集合操作
sinter key [key...] // 多个key的Set 取交集
sunion key [key...] // 并集
sdiff key [key...] // 差集
4.2 应用示例
1.微信抽奖小程序(随机出队)
将参与抽奖的user放入set,可以查看抽奖人数,然后随机抽取count个(srandmember),并决定是否删除中奖用户。
sadd key {userId} // 将参与抽奖用户放入集合。
smembers act:1 // 查看所有参与抽奖用户
srandmemeber key [count] // 抽取count名中奖者(不删除中奖人)
spop key [count] // 抽取count名中奖者(删除中奖人)
2.点赞(不可重复性)
将点赞的人放入set,可以判断该用户是否点过赞
sadd like:{消息ID} {用户ID} // 点赞
srem like:{消息ID} {用户ID} // 取消点赞
sismember like:{消息ID} {用户ID} // 检查用户是否点过赞
smembers like:{消息ID} // 获取点赞用户列表
scard like:{消息ID} // 获取点赞用户数
3.微信微博关注模型(集合运算)
通过set的集合运算可以得到,用户间互相关注的都有谁
sinter love:uId1 love:uId2 // 得到user1与user2的共同关注(交集)
4.实现电商商品筛选
sadd brand:huawei P30
sadd brand:xiaomi mi-6X
sadd brand:iphone iphone8
sadd os:android P30 mi-6X
sadd cpu:brand:intel P30 mi-6X
sadd ram:8G P30 mi-6X iphone8
sinter os:android cpu:brand:intel ram:8G ==> {P30, mi-6X}
5.ZSet(有序列表)
zset 似于 Java 的 SortedSet 和 HashMap 的结合体,一方面它是一个 set,保证了内部 value 的唯一性,另一方面它可以给每个 value 赋予一个 score,代表这个 value 的排序权重。
5.1 常用命令
ZADD key score member [[score member]…] // 往有序集合key中加入带分值元素
ZCARD key // 返回有序集合key中元素个数
ZREM key member [member …] // 从有序集合key中删除元素
ZINCRBY key increment member // 为有序集合key中元素member的分值加上increment
ZSCORE key member // 返回有序集合key中元素member的分值
ZRANGE key start stop [WITHSCORE] // 正序获取有序集合key从start下标到stop下标的元素(withscores 参数可以附带获取元素的 score)
ZREVRANGE key start stop [WITHSCORE] // 倒序获取有序集合key从start下标到stop下标的元素(withscores 参数可以附带获取元素的 score)
ZUNIONSTORE destkey numkeys key [key ...] // 并集计算,deskey(合并后的结果集、无重复),numkeys(要合并的集合的个数),key(要合并的集合)
ZINTERSTORE destkey numkeys key [key …] // 交集计算
5.2 应用示例
1.实现排行榜
ZINCRBY hotNews:20201104 这就是开放不止步的中国 // 点击新闻,当前新闻score+1
ZREVRANGE hotNews:20201104 0 10 // 展示当日排行前十
ZUNIONSTORE hotNews:week 7 hotNews:20201104 ... hotNews:20201110 // 合并七日的结果到 hotNews 这个Set中
ZREVRANGE hotNews:week 0 7 // 在 hotNews 中取七日搜索榜单前七个
2.其余功能
ZSet 还可以用来存粉丝列表,value 值是粉丝的用户 ID,score是关注时间。我们可以对粉丝列表按关注时间进行排序。
ZSet 也可以用来存储学生的成绩,value 值是学生的 ID,score是他的考试成绩。我们可以对成绩按分数进行排序就可以得到他的名次。