数据类型
1. 概述
Redis(Remote Dictionary Server)是速度非常快的非关系型(NOSQL)内存键值数据库。Redis支持物质基本数据类型String、Hash、List、Set、ZSet。
2. 五种数据类型
键的类型只能是字符串;值支持五种数据类型:String、Hash、List、Set、ZSet。
2.1. String --字符串
String 数据结构是简单的 key-value 类型,value 不仅可以是 String,也可以是数字。
应用场景: 常规key-value缓存应用。单值缓存,对象缓存,分布式锁。
# 1. 单值缓存
SET KEY value
GET KEY
# 2. 对象缓存
SET user:1 value(json格式数据)
# 用户的name一般不会变化,余额会经常发生变化,这时会用到批量处理的命令
MSET user:1:name hyk user:1:balance 1888
MGET user:1:name user:1:balance
# 3. 分布式锁
SETNX product:10001 true // 返回1,代表获取锁成功
SETNX product:10001 false // 返回0,代表获取锁失败
...执行业务逻辑
DEL product:10001 // 执行完业务,释放锁
SET product:10001 true ex 10 nx // 防止程序意外终止,导致死锁
案例: 计数器,分布式系统全局序列号
# 使用场景一、 计数器
# 每次打开,都执行一次命令 INCR article:readcount:{文章ID}
redis 127.0.0.1:6379> INCR article:readcount:1001
redis 127.0.0.1:6379> GET article:readcount:1001
# 使用场景二、 分布式系统全局序列号
# 分库、分表的集群环境下设置id自增会有问题,可以借助redis实现分布式系统全局序列号,由redis统一管理id自增
# 这种方式会导致热点key的压力非常大,比如高并发的订单表会让redis压力很大。
redis 127.0.0.1:6379> INCRBY orderId
# 上面这种方式,会使得系统和redis交互过多,可以每次去redis获取1000
redis 127.0.0.1:6379> INCRBY orderId 1000
2.2. Hash – 字典
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash相比stringl更适合用于存储对象。
常用命令:hget,hset,hgetallhmset,hmget等。
应用场景: 对象信息存储
HMSET user {userid}:name hyk {userid}:balance 1888
HMSET user 1:name hyk 1:balance 1888
HMGET user 1:name 1:balance
Redis中有一个经典的Big Key限制,如果user表中有几百万条数据,通过user 这个key一下取出几百万条数据是很慢的,会对数据进行分组存储,一个user一般不会超过1000条数据。
案例: 购物车
# 用户id为key,商品id为field,商品数量为value
# 添加商品
HSET cart:0001 00001 1
# 增加商品数量
HINCRBY cart:0001 00001 1
# 商品总数
HLEN cart:0001
# 删除商品
HDEL cart:0001 00001
# 获取购物车中所有商品
HGETALL cart:0001
2.3. List – 列表
列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
常用命令:LPUSH,RPUSH,LPOP,RPOP 等。
应用场景: 常用数据结构,关注列表,粉丝列表
# 常用数据结构(栈,队列,阻塞队列)
Stack = LPUSH + LPOP
Queue = LPUSH + RPOP
Blocking MQ = LPUSH + BRPOP # BRPOP从key列表表尾弹出一个元素,如果列表中没有会一直阻塞等待,也可以设置超时时间
案例: 微博和微信公众号消息流(最后发的动态显示在最开始)
# 我的id 000, 我关注的001发了推文00001,我关注的002发了推文00002
LPUSH 000 00001
LPUSH 000 00002
LRANGE 000 0 4 # 左闭右闭,List已经进行了排序,省去了排序的时间
2.4. Set – 集合
底层使用了intset和hashtable两种数据结构存储的,intset我们可以理解为数组,hashtable就是普通的哈希表。
常用命令:SADD,SMEMBERS,SREM
应用场景:
在微博应用中,可以将一个用户所有的关注人存储在一个集合中,将其所有粉丝存在一个集合。因为 Redis 非常人性化的为集合提供了求交集、并集、差集等操作,那么就可以非常方便的实现如共同关注、共同喜好、二度好友等功能。
1.共同好友、二度好友
2. 利用唯一性,可以统计访问网站的所有独立 IP
3. 好友推荐的时候,根据 tag 求交集,大于某个 threshold 就可以推荐
案例: 微信抽奖小程序,微信微博点赞收藏标签
# 五个人参与抽奖
SADD act:01 001
SADD act:01 002
SADD act:01 003
SADD act:01 004
SADD act:01 005
# 查看参与act01的成员
SMEMBERS act:01
# 从act01中随机选出2个,不会从集合中删除
SRANDMEMBER act:01 2
# 从act01中随机选出2个,会从集合中删除
SPOP act:01 2
# 点赞
SADD like:0001 001
# 取消点赞
SREM like:0001 001
# 检查用户是否点过赞
SISMEMBER like:001 001
# 获取点赞的用户列表
SMEMBERS like:0001
# 获取点赞用户数
SCARD like:0001
2.5. ZSet – 有序集合
Sorted Set是将Set中的元素增加了一个权重参数score。比如一个存储成绩的Sorted Set,value可以是学号,score就可以是考试得分,这样数据插入集合时就进行了一个天然的排序。
常用命令:ZADD,ZREM,ZCARD
还可以用 Sorted Sets 来做带权重的队列,比如普通消息的 score 为1,重要消息的 score 为2,然后工作线程可以选择按 score 的倒序来获取工作任务。让重要的任务优先执行。
应用场景:
- 带有权重的元素,比如一个游戏的用户得分排行榜
- 班级成绩,工资表排序
- 排行榜实现
案例: 热度排行榜(热搜)
# 点击新闻
ZINCRBY hotNews:20190819 1 守护香港
# 展示当日排行前十
ZREVARANGE hotNews:20190819 0 9 WITHSCORES
# 七日搜索排行榜单计算
ZUNIONSTORE hotNews:20190813-20190819 7 hotNews:20190813 20190814 20190815 ... 20190819
# 展示七日排行前十
ZREVRANGE hotNews:20190813-20190819 0 9 WITHSCORES
3. 跳表
跳跃表是一种有序的数据结构,它通过在每个节点中维持多个指向其他的节点指针,从而达到快速访问队尾目的。跳跃表的效率可以和平衡树想媲美了,最关键是它的实现相对于平衡树来说,代码的实现上简单。
应用场景:
- 是有序集合的底层实现之一。
3.1. 跳表结构
跳跃表受到多层链表结构的启发。上面每一层链表的节点个数,是下面一层的节点个数的一半,这样查找过程就非常类似于一个二分查找,使得查找的时间复杂度可以降低到 O(logn)。
这种方法在插入数据的时候有很大的问题。新插入一个节点之后,就会打乱上下相邻两层链表上节点个数严格的 2:1 的对应关系。如果要维持这种对应关系,就必须把新插入的节点后面的所有节点 (也包括新插入的节点) 重新进行调整,这会让时间复杂度重新蜕化成O(n)。删除数据也有同样的问题。
为了避免这一问题,它不要求上下相邻两层链表之间的节点个数有严格的对应关系,而是 为每个节点随机出一个层数(level)。比如,一个节点随机出的层数是 3,那么就把它链入到第 1 层到第 3 层这三层链表中。
https://blog.csdn.net/weixin_41622183/article/details/91126155
3.2. 插入数据
https://blog.csdn.net/weixin_41622183/article/details/91126155
3.3. 查找数据
https://blog.csdn.net/weixin_41622183/article/details/91126155
3.4. 与红黑树等平衡二叉树对比
- 插入速度非常快速,因为不需要进行旋转等操作来维护平衡性;
- 更容易实现;
- 支持无锁操作。
4. 参考文献
[1] 黄健宏.Redis设计与实现[M].北京:机械工业出版社