目录
Redis基础数据结构有哪些?
一、String(字符串)
1、应用场景:
2、字符串(String)常用的命令:
二、list(列表)
1、应用场景:
2、list操作的常用命令:
三、hash (字典)
1、应用场景:
2、hash常用的操作命令:
四、set(集合)
1、应用场景:
2、set的常用命令:
五、zset(有序集合)
1、应用场景:
2、zset有序集合的常用操作命令:
在任何一种编程语言里,字符串 String 都是最基础的数据结构, 那你有想过 Redis 中存储一个字符串都进行了哪些操作嘛?
在 Redis 中 String 是可以修改的,称为 动态字符串 ( Simple Dynamic String 简称 SDS )( 快拿小本本记名词,要考的 ),说是字符串但它的内部结构更像是一个 ArrayList ,内部维护着一个字节数组,并且在其内部预分配了一定的空间,以减少内存的频繁分配。
Redis 的内存分配机制是这样:
当字符串的长度小于 1MB时,每次扩容都是加倍现有的空间。
如果字符串长度超过 1MB时,每次扩容时只会扩展 1MB 的空间。
这样既保证了内存空间够用,还不至于造成内存的浪费, 字符串最大长度为 512MB . 。
image.png
上图就是字符串的基本结构,其中 content 里面保存的是字符串内容, 0x\0 作为结束字符不会被计算 len 中。
分析一下字符串的数据结构
struct SDS{ T capacity; //数组容量 T len; //实际长度 byte flages; //标志位,低三位表示类型 byte[] content; //数组内容 }capacity 和 len 两个属性都是泛型,为什么不直接用 int类型 ?因为 Redis 内部有很多优化方案,为更合理的使用内存,不同长度的字符串采用不同的数据类型表示,且在创建字符串的时候 len 会和 capacity 一样大,不产生冗余的空间,所以 String 值可以是字符串、数字(整数、浮点数) 或者 二进制。
存储key-value键值对,这个比较简单不细说了
Redis 中的 list 和 Java 中的 LinkedList 很像,底层都是一种链表结构, list 的插入和删除操作非常快,时间复杂度为 0(1),不像数组结构插入、删除操作需要移动数据。
像归像,但是 redis 中的 list 底层可不是一个双向链表那么简单。
当数据量较少的时候它的底层存储结构为一块连续内存,称之为 ziplist(压缩列表) ,它将所有的元素紧挨着一起存储,分配的是一块连续的内存;当数据量较多的时候将会变成 quicklist(快速链表) 结构。
可单纯的链表也是有缺陷的,链表的前后指针 prev 和 next 会占用较多的内存,会比较浪费空间,而且会加重内存的碎片化。在redis 3.2之后就都改用 ziplist+链表 的混合结构,称之为 quicklist(快速链表) 。
下面具体介绍下两种链表
ziplist(压缩列表)
先看一下 ziplist 的数据结构,
struct ziplist<T>{ int32 zlbytes; //压缩列表占用字节数 int32 zltail_offset; //最后一个元素距离起始位置的偏移量,用于快速定位到最后一个节点 int16 zllength; //元素个数 T[] entries; //元素内容 int8 zlend; //结束位 0xFF }int32 zlbytes : 压缩列表占用字节数 int32 zltail_offset : 最后一个元素距离起始位置的偏移量,用于快速定位到最后一个节点 int16 zllength :元素个数 T[] entries :元素内容 int8 zlend :结束位 0xFF
压缩列表为了支持双向遍历,所以才会有 ztail_offset 这个字段,用来快速定位到最后一 个元素,然后倒着遍历
image.png
entry 的数据结构:
struct entry{ int<var> prevlen; //前一个 entry 的长度 int<var> encoding; //元素类型编码 optional byte[] content; //元素内容 }entry 它的 prevlen 字段表示前一个 entry 的字节长度,当压缩列表倒着遍历时,需要通过这 个字段来快速定位到下一个元素的位置。
由于list它是一个按照插入顺序排序的列表,所以应用场景相对还较多的,例如:
消息队列: lpop 和 rpush (或者反过来, lpush 和 rpop )能实现队列的功能
朋友圈的点赞列表、评论列表、排行榜: lpush 命令和 lrange 命令能实现最新列表的功能,每次通过 lpush 命令往列表里插入新的元素,然后通过 lrange 命令读取最新的元素列表。
Redis 中的 Hash 和 Java的 HashMap 更加相似,都是 数组+链表 的结构,当发生 hash 碰撞时将会把元素追加到链表上,值得注意的是在 Redis 的 Hash 中 value 只能是字符串.
hset books java "Effective java" (integer) 1 hset books golang "concurrency in go" (integer) 1 hget books java "Effective java" hset user age 17 (integer) 1 hincrby user age 1 #单个 key 可以进行计数 和 incr 命令基本一致 (integer) 18Hash 和 String 都可以用来存储用户信息 ,但不同的是 Hash 可以对用户信息的每个字段单独存储; String 存的是用户全部信息经过序列化后的字符串,如果想要修改某个用户字段必须将用户信息字符串全部查询出来,解析成相应的用户信息对象,修改完后在序列化成字符串存入。而 hash可以只对某个字段修改,从而节约网络流量,不过hash内存占用要大于 String ,这是 hash 的缺点。
Redis 中的 set 和 Java 中的 HashSet 有些类似,它内部的键值对是无序的、唯一 的。它的内部实现相当于一个特殊的字典,字典中所有的value都是一个值 NULL。当集合中最后一个元素被移除之后,数据结构被自动删除,内存被回收。
zset 也叫 SortedSet 一方面它是个 set ,保证了内部 value 的唯一性,另方面它可以给每个 value 赋予一个 score ,代表这个value的排序权重。它的内部实现用的是一种叫作“ 跳跃列表 ”的数据结构。
zset 可以用做排行榜,但是和 list 不同的是 zset 它能够实现动态的排序,例如: 可以用来存储粉丝列表,value 值是粉丝的用户 ID,score 是关注时间,我们可以对粉丝列表按关注时间进行排序。
zset 还可以用来存储学生的成绩, value 值是学生的 ID, score 是他的考试成绩。 我们对成绩按分数进行排序就可以得到他的名次。
总结
本文很多概念都一带而过了,只是给大家粗略的讲述一下Redis五种基础数据结构和应用场景,旨在给小伙伴们一个面试备题的方向
作者:程序员内点事
原文链接: https://juejin.im/post/5e7ae9635188255dd461f066