Redis目前支持九种数据类型,包括五种基本数据类型和四种特殊的数据类型,这篇文章主要详细介绍五种基本的数据类型String,Hash,List,Set,ZSet的使用场景,操作命令只对String做简单的示例,更多具体命令内容可以直接参考Redis命令文档
下图对Redis的数据结构做了一个简单的归纳
注意Redis是key-Value键值对数据结构,下图存储格式统一省略了key,例如String,添加Key则为Map<String,String>
一、String
String应该是目前使用最频繁的数据结构,它可以存储几乎不超过512M大小的所有类型的字符串,例如说Json对象、二进制或者Base64编码之后的图片,String的key和value的大小上限均为512,我们存储Key时,在能辨别业务对象的同时,需要尽可能的小,避免占用太大的内存空间
存储格式:Map<String,String>
使用场景:
- 缓存、热点数据
- 分布式session
- 分布式锁
- INCR 计数器
- 文章的阅读量,微博点赞数,允许一定的延迟,先写入 Redis 再定时同步到数据库
- 全局ID
- INCR 限流
接下来我们一起使用Redis命令来操作一下吧,在此之前需要先安装Linux环境的Redis
127.0.0.1:6379> set user:1 zhangsan
OK
127.0.0.1:6379> get user:1
"zhangsan"
127.0.0.1:6379> select 1 切换数据库
OK
127.0.0.1:6379[1]>
127.0.0.1:6379[1]> set name lisi EX 100 EX设置过期时间
OK
127.0.0.1:6379[1]> INCR views:page:1 INCR 新增自加类型的字符串
(integer) 1
127.0.0.1:6379[1]> INCR views:page:1
(integer) 2
127.0.0.1:6379[1]> INCRBY views:page:1 10 INCRBY 新增自加类型的字符串并指定单次增加的数量
(integer) 12
RedisDesktopManager界面
127.0.0.1:6379[1]> setnx name lisi //存入一个不存在的键值对,如果key不存在,同set;若存在,则不做任何操作。
二、List
Redis中列表(list)类型是用来存储多个有序、可重复的字符串,列表中的每个字符串成为元素(element),一个列表最多可以存储2^32 - 1个元素。在Redis中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是一种比较灵活的数据结构,可以充当栈和队列的角色,在实际开发中有很多应用场景
存储格式:Map<String,List<String>>
使用场景:
-
消息队列
Redis的lpush + brpop命令组合即可实现阻塞队列,生产者客户端使用lpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的争抢列表尾部的元素
-
最新列表
list类型的lpush命令和lrange命令能实现最新列表的功能,每次通过lpush命令往列表里插入新的元素,然后通过lrange命令读取最新的元素列表,如朋友圈的点赞列表、评论列表。但是,并不是所有的最新列表都能用list类型实现,因为对于频繁更新的列表,list类型的分页可能导致列表元素重复或漏掉,举个例子,当前列表里由表头到表尾依次有(E,D,C,B,A)五个元素,每页获取3个元素,用户第一次获取到(E,D,C)三个元素,然后表头新增了一个元素F,列表变成了(F,E,D,C,B,A),此时用户取第二页拿到(C,B,A),元素C重复了。只有不需要分页(比如每次都只取列表的前5个元素)或者更新频率低(比如每天凌晨更新一次)的列表才适合用list类型实现。对于需要分页并且会频繁更新的列表,需用使用有序集合sorted set类型实现。另外,需要通过时间范围查找的最新列表,list类型也实现不了,也需要通过有序集合sorted set类型实现,如以成交时间范围作为条件来查询的订单列表。之后在介绍有序集合sorted set类型的应用场景时会详细介绍sorted set类型如何实现最新列表
-
排行榜
list类型的lrange命令可以分页查看队列中的数据。可将每隔一段时间计算一次的排行榜存储在list类型中,如京东每日的手机销量排行、学校每次月考学生的成绩排名、斗鱼年终盛典主播排名等,
但是,并不是所有的排行榜都能用list类型实现,只有定时计算的排行榜才适合使用list类型存储,与定时计算的排行榜相对应的是实时计算的排行榜,list类型不能支持实时计算的排行榜,之后在介绍有序集合sorted set的应用场景时会详细介绍实时计算的排行榜的实现。
三、Hash
底层数据结构是由哈希表实现,一个存储空间保存多个健值对数据,对一系列存储的数据进行编组,方便管理,典型应用存储对象信息。string类型也可以实现存对象,对这个对象进行json序列化,然后取的时候再进行反序列化,但是,每次修改的时候只能整体修改,不能单个修改某个属性,且必须有实体类型的支撑。序列化和反序列化也需要消耗服务器性能
存储格式:Map<String,Map<String,String>>
使用场景:
缓存频繁修改的对象
hash类型的(key, field, value)的结构与对象的(对象id, 属性, 值)的结构相似,也可以用来存储对象。
在介绍string类型的应用场景时有所介绍,string + json也是存储对象的一种方式,那么存储对象时,到底用string + json还是用hash呢?
两种存储方式的对比如下表所示:
对象的某个属性需要频繁修改时,不适合用string+json,因为它不够灵活,每次修改都需要重新将整个对象序列化并赋值,如果使用hash类型,则可以针对某个属性单独修改,没有序列化,也不需要修改整个对象。比如,商品的价格、销量、关注数、评价数等可能经常发生变化的属性,就适合存储在hash类型里。
不常变化的属性存储在hash类型里也没有问题,比如商品名称、商品描述、上市日期等。但是,当对象的某个属性不是基本类型或字符串时,使用hash类型就必须手动进行复杂序列化,比如,商品的标签是一个标签对象的列表,商品可领取的优惠券是一个优惠券对象的列表等,即使以coupons(优惠券)作为field,value想存储优惠券对象列表也还是要使用json来序列化,这样的话序列化工作就太繁琐了,不如直接用string + json的方式存储商品信息来的简单
综上,一般对象用string + json存储,对象中某些频繁变化的属性抽出来用hash存储。
四、Set
Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
存储格式:Map<String,Set<String>>
使用场景:
- 标签:比如我们博客网站常常使用到的兴趣标签,把一个个有着相同爱好,关注类似内容的用户利用一个标签把他们进行归并。
- 统计网站的独立IP。利用set集合当中元素不唯一性,可以快速实时统计访问网站的独立IP。
五、ZSet
redis的有序集合与集合一样也是String类型元素的集合,不允许有重复的元素,每一个元素都会关联一个double类型的分数(Score),redis正是通过分数来为集合中的成员进行排序,有序集合中的成员是唯一的,但是分数可以重复,集合是通过哈希表实现的,集合中的最大元素是2的32次方减1。它类似于Java中的sortset和hashmap的结合体,但是在redis中是通过两种底层数据结构实现的。一种是ziplist压缩列表,另一种就是redis中最经典的数据结构skipList跳跃表。
存储格式:Map<String,SortSet<String>>
使用场景:排行榜、热搜榜