猿学长带你掌握redis五大常用数据类型

Redis可以说是除MySQL之外用的最多的一种数据库了!众所周知,它是一个非关系型数据库(NoSQL),当然它的出现也绝不是为了取代MySQL。非关系型数据库有很多种类型:面向列的NoSQL、基于图的NoSQL、文档型NoSQL ...... Redis是一种Key-Value型的NoSQL。Redis所有的key(键)都是字符串。我们在谈基础数据结构时,讨论的是value(值)的数据类型,主要包括常见的5种数据类型,分别是:String、List、Set、Zset、Hash。

一、String

数据结构

String是Redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个Key对应一个Value。String类型是二进制安全的。意味着Redis的String可以包含任何数据。比如jpg图片或者序列化的对象。String类型是Redis最基本的数据类型,Redis中一个String类型的Value最多可以是512M。

String的数据结构为简单动态字符串(SimpleDynamicString,缩写SDS)。是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。

常用命令

操作作用
set添加键值对
get获取 key 对应的值
apend将给定的 value 追加到原值的末尾。
strlen获取 key 对应的值的长度
setnx当 key 不存在时,设置 key 的值
incr将 key 中存储的值加 1,只能对数字值操作,如果为空,新增值为 1
decr将 key 中存储的值减 1,只能对数字值操作,如果为空,新增值为 -1
incrby将 key 中存储的数字值递增指定的步长,若 key 不存在,则相当于在原值为 0 的值上递增指定的步长。
decrby将 key 中存储的数字值递减指定的步长,若 key 不存在,则相当于在原值为 0 的值上递减指定的步长。
mset同时设置多个 key-value
mget同时获取多个 key 的值
msetnx当多个 key 均不存在时,则设置成功
getrange类似 java 中的 substring
setrange覆盖指定位置的值
setex设置键值,并设置过期时间
getset以旧换新,设置新值同时返回旧值

使用场景

缓存:经典使用场景,把常用信息,字符串,图片或者视频等信息放到Redis中,Redis作为缓存层,MySQL做持久化层,降低MySQL的读写压力

计数器:Redis是单线程模型,一个命令执行完才会执行下一个,同时数据可以一步落地到其他的数据源。

Session:常见方案Spring Session + Redis实现Session共享。

字符串的Encoding有三种方式:

  • 如果一个字符串String保存的是整数值,如:set age 13,那么这个整数值可以用long类型标识。那么该字符串的redisObject会把13这个整数值保存在ptr属性中,并将Encoding设置为int。

  • 如果字符串String保存的是一个字符串值,并且字符串大于39个字节,那么这个字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串,并将redisObject的Encoding设置为raw。

  • 如果字符串String保存的是一个字符串值,并且字符串小于39个字节那么字符串将使用embstr编码的方式来储存这个字符串。

embstr对别raw的优点:

  • embstr创建字符串对象(redisObject)的次数只需要一次,而raw是两次(redisObjcet和SDS分开分配)。

  • embstr调用释放内存的函数一次,rew编码的字符串对象要少一次。

  • 由于embstr编码是内存的连续性的,而raw不是连续的,因此embstr存,取速度比较快

二、List

数据结构

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。它的底层实际是个双向链表,这意味着List的插入和删除操作效率会比较快,时间复杂度是O(1)。使用List结构,我们可以轻松地实现最新消息排队功能,List的另一个应用就是消息队列,可以利用List的 PUSH 操作,将任务存放在List中,然后工作线程再用 POP 操作将任务取出进行执行。

常用命令

操作作用
lpush从左边插入一个或多个值
rpush从右边插入一个或多个值
lrange从列表左边获取指定范围的值
lpop从左边弹出多个元素
rpop从右边弹出多个元素
rpoplpush从一个列表右边弹出一个元素放到另外一个列表中
lindex获取指定位置的索引
llen获取列表的长度
linsert在某一个值的前/后插入一个值
lrem删除指定数量的某个元素
lset替换指定位置的值

使用场景

  • 消息队列:List类型的lpop和rpush(或者反过来,lpush和rpop)能实现队列的功能,故而可以用Redis的List类型实现简单的点对点的消息队列。

  • 排行榜:List类型的range命令可以分页查看队列中的数据,但是只有顶式计算的排行榜才适合使用List类型存储。

  • 最新列表:List类型的lpush命令和range命令能实现最新列表的功能.每次通过lpush的命令往列表里插入新的元素,然后通过lrange命令读取最新元素列表,如朋友圈的点赞列表、评论列表。

redisObject如何表示List

列表对象List的编码方式encoding有两种,分别是:Ziplist(压缩列表)、Linkedlist。

Ziplist:

优点:节省内存

缺点:比其他结构要消耗跟多的时间,所以Redis在数据量少的时候使用压缩列表储存。列表元素数量少于512,并且所有元素的长度都少于64字节时,使用Ziplist(压缩列表)储存,相反会使用Linkedlist。

Ziplist节省内存的原理:

普通数据能够支持随机访问的原因是储存的内存是连续的,但是有一个问题,就是数组中每一个元素的大小都是必须相同的,如果大小不一样的话,那么该元素的内存就必须按照数组中最大的元素(假设是五个字节)的内存存放,那么储存少于5个字节的元素就会存在内存浪费问题。

Linkedlist:

列表元素数量少于512,并且所有元素的长度都少于64字节时,使用Ziplist(压缩列表)储存,相反会使用LindedList。

三、Set

数据结构

Redis Set对外提供的功能与List类似是一个列表的功能,特殊之处在于Set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,Set是一个很好的选择,并且Set提供了判断某个成员是否在一个Set集合内的重要接口,这个也是List所不能提供的。Redis的Set是String类型的无序集合。它底层其实是一个Value为Null的Hash表,所以添加,删除,查找的复杂度都是O(1)。一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变。

常用命令

操作作用
sadd添加一个或多个元素
smembers取出所有元素
sismember判断集合中是否有某个值
scard返回集合中元素的个数
srem删除一个或多个元素
spop随机弹出指定数量的元素
srandmember随机获取指定数量的元素,不会从集合中删除
smove将某个元素从一个集合移动到另一个集合
sinter取多个集合的交集
sinterstore取多个集合的交集,放到一个新的集合
sunion取多个集合的并集,自动去重
sunionstore取多个集合的并集,放到一个新的集合
sdiff取多个集合的差集
sdiffstore取多个集合的差集,放到一个新的集合

使用场景

推荐:通过sinter命令计算交集,比如美团给你推荐附近外卖时就可以根据你的外卖记录与附近商家计算交集推送安全提示:微信群成员保存在一个set中,用户好友也保存在Set中。当用户加入群聊时可以提醒非好友用户注意安全。

编码格式

Set集合值set 的encoding编码方式有:intset、hashtable。

intset:当 集合的长度少于 512 时,并且所有元素都是整数,使用 intset存储。否则使用 hashtable。

Hashtable:Hashtable编码的底层实现是字典,字典的每个键是字符串对象,只不过值都是空(NULL)。

四、Hash

数据结构

Redis hash 是一个键值对集合。Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。类似Java里面的Map<String,Object>。

以用户ID为查找的key为例,存储的value用户对象包含姓名,年龄,生日等信息,若用普通的key/value结构来存储。

每次修改用户的某个属性需要,先反序列化改好后再序列化回去。开销较大。

用户ID数据冗余。

通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。

常用命令

操作作用
hset设置多个 field 的值
hget返回指定的 field 的值
hgetall返回所有的域和值
hmset和 hset 类似(已弃用)
hexists判断给定的 field 是否存在
hkeys返回所有的 field
hvals返回所有的 value
hlen返回 field 的数量
hincrby在 field 的值加上指定的数量
hsetnx当 field 不存在时,设置 field 的值

使用场景

商品对象、用户对象。这个场景需要验证性对待,如果商品对象、用户对象信息每次都需要全量的话不妨存string,但是仅仅部分使用就可以考虑使用hash结构SKU等信息,这个场景hash就比较合适了。一个hash结构中存储某个商品所有sku。

编码格式

Hash 的Encoding 编码方式共有两种:Ziplist、Hashtable

Ziplist:当哈希对象保存的键值对数量少于512,且所有键值对的长度都少于64字节时,使用压缩列表保存。在压缩列表中,每当有新的键值对要加入到哈希对象时, 程序会先将保存了  键的压缩列表节点  推入到压缩列表表尾, 然后再将  保存了值的压缩列表节点  推入到压缩列表表尾, 因此:保存了同一键值对的两个节点总是紧挨在一起。

Hashtable:若哈希对象保存的键值对个数大于512,并且其中有键值对大于64个字节,就使用Hashtable 保存。

五、sorted set

数据结构

Redis有序集合Zset与普通集合Set非常相似,是一个没有重复元素的字符串集合。不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分是可以重复的 。因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。

Zset是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。

Zset底层使用了两个数据结构:

  • Hash:hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。

  • 跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。

举个例子:在有序链表和跳跃表中,查询出51。

有序链表:

要查找值为51的元素,需要从第一个元素开始依次查找、比较才能找到。共需要6次比较。

跳跃表:

从第2层开始,1节点比51节点小,向后比较。21节点比51节点小,继续向后比较,后面就是NULL了,所以从21节点向下到第1层在第1层,41节点比51节点小,继续向后,61节点比51节点大,所以从41向下在第0层,51节点为要查找的节点,节点被找到,共查找4次。从此可以看出跳跃表比有序链表效率要高。

常用命令

操作作用
zadd添加一个或多个元素
zrange返回指定索引范围的元素(score 升序)
zrevrange返回指定索引范围的元素(score 降序)
zrangebyscore返回指定 score 范围的元素(score 升序)
zrevrangebyscore返回指定 score 范围的元素(score 降序)
zincrby为指定元素的 score 加上指定的数量
zrem删除集合中一个或多个元素
zremrangebyrank删除指定索引范围元素
zrenrangebyscore删除指定 score 范围元素
zcount返回指定 score 范围内元素的个数
zrank返回某个元素在集合中的排名(score 升序)
zrevrank返回某个元素在集合中的排名(score 降序)
zscore返回集合中指定元素的 score

使用场景

排行榜:美团要做一个销量排行榜,就可以使用店家的订单做score,这个查询出来的结果就是有序的权重队列:score作为优先级,这样取出来的数据权重都是最大优先执行的延时任务score作为任务启动执行时间,取值时判断该值执行即可。

编码格式

Zset的Encoding 编码有两种,分别是:Ziplist、Skiplist。

Ziplist:与Hash有异曲同工之妙,都是使用相邻两个节点存储Score和Memberskiplist。

Skiplist:redis 的 Skiplist 是由字典Dict 和 跳表构成的。

总结

在开发中,字符串类型是用的最多的数据类型,导致我们忽视了redis的其他四种数据类型,在具体场景下选择具体的数据类型对提升redis性能有非常大的帮助。redis虽然支持消息队列的实现,但是并不支持ack。所以redis实现的消息队列不能保证消息的可靠性,除非自己实现消息确认机制,不过这非常麻烦,所以如果是重要的消息还是推荐使用专门的消息队列去做。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值