分布式缓存技术-redis基础篇 (redis里只有五种数据类型?redis的数据类型源码与底层实现是?为啥这样设计?不同数据类型应用场景是?被面试官问倒了就赶紧来瞅瞅把)

18 篇文章 0 订阅
13 篇文章 0 订阅

一、前言

这一节主要 就是 一些发展 比较 介绍 大致看看就好

1.1redis诞生历程

 它是为了记录用户访问记录而诞生的,最开始的数据结构就是下面这种,就是简单的队列

1.2 关系型数据库与非关系型数据库的区别

1.2.1关系型数据库

我们都知道 一般数据都存在关系型数据库 mysql oracle 这些里面 那么 为什么 还要用到 非关系型数据库 redis呢? 看看下面俩图

特点

 不足

1.2.2 非关系型数据库

非关系数据库具有的特点

 非关系型数据库的用途及代表

1.3数据库的发展

从SQL型 到 NoSQL 型 再到近几年的 NewSQL NewSQL 中典型的是TiDB 上家公司都有用到

1.4为什么要选redis

既然都知道 redis 是把数据存在内存中, 那为啥不用 hashmap ,Memachache 呢?

当然是它具备更多的优势 如下

二、命令

作为开发,我们一般 都是在代码里通过工具类来操作redis 那么 redis的原始的命令有哪些常用的呢?还有印象吗,感兴趣的可以看看

 最后这条命令可以看到 nam 键对应的值 xiaobao 是 string类型 那么 redis究竟有哪些类型呢

三、redis中的数据类型

官网介绍redis类型的入口 An introduction to Redis data types and abstractions – Redis

可能一般的教程里就说了前面五种,但是实际上它是有8种的 下面是个概览

 现在详细介绍 它是有下面这八种数据类型的

 而每一种类型 我们都会以下面这四种角度去分析

3.1 String

这种数据类型是使用场景最多的

3.1.1 String的存储类型

不要想着 跟 java里的String类型一样 它实际上在底层 会区分 为以下三种类型 下面的这个String 才与java中的一样

3.1.2 String中int,float,string的一些操作指令

针对 这几种类型的一些操作指令

3.1.3 存储原理(底层编码)

先看这第一张图,每个redis的对象都是下面这样一个数据结构 键值存储在 一个dictEntry的对象里面然后 还有个指向下一个dictEntry的指针

redis的键都是字符串类型的,但是这里的字符串并不是C里面字符串,而是redis经过了自定义后的叫SDS这样一种 后面会细说

然后值的话都是封装在 一个redisObject对象内 看到里面会有个 type 是值的类型 还有个 ptr指针 指向真正数据存储的对象,至于为啥这样设计呢

以上设计都是 因为redis 数据是存在内存中的 而内存在现阶段还是很可贵的 为了在不同的情况下 尽量节省内存空间,提升查询速度

 redisObject:redis存储值的对象

如图 type 是 OBJ_STRING 然后 encoding是 OBJ_ENCODING_RAW

 

encoding:说到encoding这里要介绍一下 长整型 就用 int 编码 其他字符串 44字节以下用embstr 以上用 raw

 encoding 示例 还有个 huihui 对应的值 你猜?

SDS

为什么redis底层要封装一个自己的字符串类型SDS 呢?

里面 的len alloc flags buf都是啥呢?继续看

 SDS的源码 底层数据结构就是这样 设计几种类型的对象是为了 节省内存 当 字符数组发生变化时可做扩容缩容

下面是原本C字符数组的一些缺点 与优化后的SDS的特点

SDS在此基础上做了几点优化

内容还是存放在字符数组 多了几个属性

len记录长度比原本遍历获取快了,可扩容就不会增加长度后造成溢出,可扩容所以 不需要预先的内存的分配 空间的预分配 和惰性的内存释放

因为明确了长度 所以就不存在二进制安全的问题 读到固定长度就停止

 embstr 与 raw的区别 前者与 redisobject对象内存空间连续,后者不连续 优缺点:前者分配内存更快 但是长度修改时 值的分配内存地址可能发生改变

什么时候会出现编码的转换

以上设计都是 因为redis 数据是存在内存中的 而内存在现阶段还是很可贵的 为了在不同的情况下 尽量节省内存空间,提升查询速度

3.1.4 应用场景

3.2 Hash

3.2.1 Hash的介绍与其优缺点

假设我要存一张表里面的 如下的 这种 数据 咋存呢?

redis里面 key 倒是可以分为多层 如图一这样设置 图二中可看到像数据库中的这种数据 但是每次 set 在键中都要冗余存储id 显得太麻烦并且浪费内存了

于是 Hash(哈希)来了

基本数据结构如下 40亿个 很恐怖

HashString的区别

优点:

因为 不像上面说的 要存表数据必须 多级存储键 会冗余存储 而Hash就一个键 所以节省了内存

一个key对应的多个field可以有name 另一个也可以 所以减少key冲突

不需要像mget样 与redis交换很多次才能拿到 数据 所以 减少了性能消耗

缺点:

Hash的field不能像String一样单独设置过期时间 比如上面的 设置过期时间只能 一次性对 qingshan这个key对应的 所有field 一起设置过期时间

然后数据不能分片存储 一个key对应的 所有field必须在一起

3.2.2操作命令

基本的一些操作命令

他还是与string一样 也是使用redisObject存储 但是指针指向的对象的数据类型 有两种编码 它的两种编码如下

3.2.3 存储原理(底层编码)

3.2.3.1 ziplist

ziplist 与一般的双向链表不同的是 说是链表但是内存连续 与数组类似 与数组不同的是它每个节点的内存大小不相等,然后 指针不是存的前后节点的 物理地址(因为内存连续所以没必要存了)而存的节点的长度

 那么 ziplist的结构是怎样的呢?如下

 

ziplist 中的内容存在 entry节点中 而 entry的结构又是怎样的呢?

那 什么时候 会用到ziplist呢?如下

使用 ziplist存储后 ziplist的编码方式又如何确定呢? ziplist的编码方式与它的长度有关

3.2.3.2 HashTable

而如果以上两种条件 有一个不满足 就会使用 HashTable 的结构 如下 跟我们想的也一样 外面有个key value 是dict 类型 value是个 hashtable

然后hashtable内是最终存放数据的 dictEntry

如下 下面这俩图看看就好

 

hashtable -dict 存储结构

细化后的HashTable 结构如下 咋一看可能有点蒙蔽,不慌,我来解释下

最外层是dict对象 dict对象内又有俩dicthashtable 其中只有一个dictht 先被用到 另一个是作为扩容用的

dictht 又包含了个 dicthashtable dicthashtable里面是个数组 然后数组内存的是链表 链表内存的就是我们的数据 与下一个存放数据的dictEntry的指针

下图中跟只有发生hash碰撞 才会形成链表

扩容:上面说 只有一个 dicthashtable 先被用到,另一个用做扩容 扩容 的时机是 链表中dictEntry的数量除以数组总长度 > 5

扩容是怎样做呢 ? 比如原本 的 dicthashtable 是500M 那么用作扩容的那个dicthashtable 就会 乘以二 然后 取最靠近的2^n 也就是 1024M

当然他也有缩容,这里就不说了

3.2.4 应用场景

3.3 list列表(有序)

3.3.1 介绍

与队列类似 但是这玩意就挺牛逼的 有头尾节点 有指针 有 下标 所以针对它的操作挺丰富的

3.3.2 操作指令

以下七个指令我解释下 分别是 向 队列queue 左侧 入队 a ,向 队列queue 左侧 入队 b ,向 队列queue 右侧 入队 d,e , 弹出 左侧首个元素 ,弹出右边首个元素 , 找到 队列下表为0的元素 , 找到 队列 从 0下标到末尾所有元素

3.3.3 存储原理(底层编码)

可以看到它的编码是 quicklist

 

3.3.4 应用场景

除了存储一些 有序的信息如下面的 消息 文章 评论 公告 之类的

还可以作为分布式环境下的 一个队列 或者栈使用 因为它是阻塞式的 所以也不用担心线程安全

3.4 Set

3.4.1 介绍

无序集合 跟java里面的 set 基本一样 重点是后面它那跳表

3.4.2 操作指令

如下指令的意思分别是 往myset 中添加 元素 ,列举 myset 中所有元素 ,统计set内有多少个元素 ,随机取到myset中一个元素但不删除 ,

随机取到myset中一个元素然后删除 ,删除指定的元素 ,类似于contains 元素是否被包含 返回 0 不包含 1 包含

3.4.3 存储原理(底层编码)

他也有两种编码 存 只int类型就用的第一种 超过 512个元素 就改成 hashtable

intset的源码

3.4.4 应用场景

抽奖就很适合 还有些别的作用 点赞啊,关注啊 还有 比如下图中的 无序的东西就往里面放就完事了

然后是他还有个特别重要的功能 取交集 并集 等 因此 那种共同好友 实现就方便了

 

 

 

3.5 zset

3.5.1 介绍

有序集合 怎样实现有序的呢 它有个 分值

3.5.2 操作命令

下面指令分别 是 添加元素 ,列举某个范围的元素 与分数 ,倒序列举 某个范围的元素 与分数 ,列举分数是 20 或者 30 的元素 ,删除 php 与 cpp元素 ,

统计总数 ,python分数加5 ,统计 分数 在 20到60之间的数量 ,python分数排名第几 ,python分数多少

3.5.3 存储原理(底层编码)

与前面类似 元素 数量小于 128 并且 所有元素长度 小于64字节

  

skiplist : 跳跃表 每个元素 所处节点 还有个字段 存储了一个层级的数组 比如 我要插入一个元素 如果没有层级的说法 需要一个个元素遍历 有了这个层级

我从第二层遍历 会比第一层快 从第三层遍历会比第二层快 从 高层遍历到比插入元素大的元素后 然后返回 再遍历底层 直到找到 第一层 确定 元素需要存放的位置

level值 每次元素插入是 随机生成的

随机获取层级的 底层方法

跳跃表长这样

跳跃表 的数据结构 前进指针 后退指针 分值 还有层级 层级节点跨度 熟悉不

3.5.4 应用场景

3.6 其他数据结构

除了上面五种常用数据结构外 还有下面几种

因为 redis底层存的二进制数据 所以可以做位图

比如 这个 我设置 的a 把第七八位从 0 1 改成了 1 0 就从a 变成了 b

还有些 与或 比如 把一个三天都打卡的人找出来 就与一下

然后是整地理位置的 数据类型 下面是经纬度

再然后是计算某网站点击数啥的 不需要太精确的功能 可以用下面这个 hyperlog 啥的 它耗内存贼低 具体就不说了

留个小作业

QQ或者某论坛 推荐 可能认识的人 咋实现

答案就在这...

用 set 整个交集

如果能看到这里 你是真滴秀 欢迎关注 B站 请叫我觉哥 我会定期在B站直播陪伴学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我才是真的封不觉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值