1.《redis数据结构概览》
1.数据结构概览
数据模型:一共5种,String(字符串)、List(列表)、Hash(哈希)、Set(集合)和 Sorted Set(有序集合)

数据结构:一共有 6 种,分别是简单动态字符串、双向链表、压缩列表、哈希表、跳表和整数数组。

对应关系图:

2.全局哈希表
Redis 使用了一个全局哈希表来保存所有键值对

哈希桶中的元素保存的并不是值本身,而是指向具体值的指针。这也就是说,不管值是 String,还是集合类型,哈希桶中的元素都是指向它们的指针。

3.列表(list)
对应两种实现方法,一种是压缩列表(ziplist),另一种是双向循环链表。

当列表中存储的数据量比较小的时候,列表就可以采用压缩列表的方式实现。具体需要同时满足下面两个条件:

列表中保存的单个数据(有可能是字符串类型的)小于 64 字节;(cpu缓存行一行最多64字节)
列表中数据个数少于 512 个。
压缩列表:

特点:

可以存储不同字节大小的数据
基于数组实现,但不支持随机访问
获取首尾元素时间复杂度O(1),其他情况O(n)
压缩列表在表头有三个字段 zlbytes、zltail 和 zllen,分别表示列表长度(所占空间大小)、列表尾的偏移量(列尾到起始位置的偏移量)和列表中的 entry 个数;

其中每个entry,分为四个部分previous_entry_length,entry_length,encoding,content

分别表示 上一个元素的长度,本元素长度 ,编码方式,文本内容

4.字典(hash)
有两种实现方式,一种是压缩列表,另一种是散列表

有当存储的数据量比较小的情况下,Redis 才使用压缩列表来实现字典类型。具体需要满足两个条件:

字典中保存的键和值的大小都要小于 64 字节;
字典中键值对的个数要小于 512 个。
5.集合(set)
有两种实现方法,一种是基于有序数组,另一种是基于散列表。

当要存储的数据,同时满足下面这样两个条件的时候,Redis 就采用有序数组,来实现集合这种数据类型。

存储的数据都是整数;
存储的数据元素个数不超过 512 个。
6.有序集合(sortedset)
当数据量比较小的时候,Redis 会用压缩列表来实现有序集合。具体点说就是,使用压缩列表来实现有序集合的前提,有这样两个:

所有数据的大小都要小于 64 字节;
元素个数要小于 128 个。
7.为什么 sortedset,hash,list都会用压缩列表呢 ?
当数据量小的时候通过数组下标访问最快、占用内存最小,而压缩列表只是数组的升级版;
因为数组需要占用连续的内存空间,所以当数据量大的时候,就需要使用链表了,同时为了保证速度又需要和数组结合,也就有了散列表。

1、内存利用率,数组和压缩列表都是非常紧凑的数据结构,它比链表占用的内存要更少。Redis是内存数据库,大量数据存到内存中,此时需要做尽可能的优化,提高内存的利用率。

2、数组对CPU高速缓存支持更友好,所以Redis在设计时,集合数据元素较少情况下,默认采用内存紧凑排列的方式存储,同时利用CPU高速缓存不会降低访问速度。当数据元素超过设定阈值后,避免查询时间复杂度太高,转为哈希和跳表数据结构存储,保证查询效率。

2.《redis 动态字符串详解》
1.案例:实现一个图片存储系统,并快速定位文件位置
案例描述:开发一个图片存储系统,要求这个系统能快速地记录图片 ID 和图片在存储系统中保存时的 ID(可以直接叫作图片存储对象 ID)。同时,还要能够根据图片 ID 快速查找到图片存储对象 ID。因为图片数量巨大,所以我们就用 10 位数来表示图片 ID 和图片存储对象 ID,

例如,图片 ID 为 1101000051,它在存储系统中对应的 ID 号是 3301000051。

photo_id: 1101000051
photo_obj_id: 3301000051
1
2
图片 ID 和图片存储对象 ID 正好一一对应,是典型的“键 - 单值”模式,这和 String 类型提供的“一个键对应一个值的数据”的保存形式刚好契合。

当我们储存一亿张图片时,用6.4G内存,但是,随着图片数据量的不断增加,我们的 Redis 内存使用量也在增加,结果就遇到了大内存 Redis 实例因为生成 RDB 而响应变慢的问题。

我们保存了 1 亿张图片的信息,用了约 6.4GB 的内存,一个图片 ID 和图片存储对象 ID 的记录平均用了 64 字节。但问题是,一组图片 ID 及其存储对象 ID 的记录,实际只需要 16 字节就可以了。

question:那么redis 的动态字符串类型为什么会使用这么多内存呢?另外48Byte 的内存用到哪里了呢?

2.动态字符串结构体 (Simple Dynamic String,SDS)
当我们用String类型键值对存储数据时,

当你保存 64 位有符号整数时,String 类型会把它保存为一个 8 字节的 Long 类型整数,这种保存方式通常也叫作 int 编码方式。

当你保存的数据中包含字符时,String 类型就会用简单动态字符串(Simple Dynamic String,SDS)结构体来保存,如下图所示:

len: 4字节 ,表示buf 使用长度

alloc:4字节,表示 内存分配器分配空间 大小

buf:字节数组,保存实际数据,Redis 会自动在数组最后加一个“\0”,这就会额外占用 1 个字节的开销。

3.RedisObject
对于 String 类型来说,除了 SDS 的额外开销,还有一个来自于 RedisObject 结构体的开销。

因为 Redis 的数据类型有很多,而且,不同数据类型都有些相同的元数据要记录(比如最后一次访问的时间、被引用的次数等),所以,Redis 会用一个 RedisObject 结构体来统一记录这些元数据,同时指向实际数据。

Long类型时: ptr引用指针直接填充赋值为整数数据这样就不用额外的指针再指向整数了,节省了指针的空间开销。这种布局方式也被称为 。
小于等于44字节的字符串:RedisObject 中的元数据、指针和 SDS 是一块连续的内存区域,这样就可以避免内存碎片。这种布局方式也被称为
大于44字节的字符串时,SDS 的数据量就开始变多了,Redis 就不再把 SDS 和 RedisObject 布局在一起了,而是会给 SDS 分配独立的空间,并用指针指向 SDS 结构。这种布局方式被称为
下面是 三种编码方式的结构图:

所以,例子中,保存“1101000051:3301000051”只使用 了 32个字节,另外32字节消耗在哪里了?

这就要讲一下 redis 中的 dicEntry结构了

4.全局哈希表的 dicEntry结构
Redis 会使用一个全局哈希表保存所有键值对,哈希表的每一项是一个 dictEntry 的结构体,用来指向一个键值对。

dictEntry 结构中有三个 8 字节的指针,分别指向 key、value 以及下一个 dictEntry,三个指针共 24 字节,如下图所示:

但是,加上 这三个指针也才 24个字节 + 32字节 ,不够64字节啊,为什么?

应为redis 内存分配器 jemalloc,分配内存 按页分配,且分配 2的幂次方大小的内存页

所以,申请 了 56个字节,但是 内存分配器分配了 64字节

至此,保存“1101000051:3301000051”为什么分配了64字节就 真相大白了

那么,用什么来存储海量 的String类型,单向键值对呢?

3.压缩列表
1. ziplist结构
压缩列表(ziplist):表头有 zlbyts(4字节,列表所占字节大小),zltail(4字节,列尾到 列表其实位置的偏移量offset),zllen(2字节,列表entry元素个数); 最后在列表尾部有一个 zlend(1字节)表示列表结束

prev_len: 上一个元素 长度,prev_len 有两种取值情况:1 字节或 5 字节。

取值 1 字节时,表示上一个 entry 的长度小于 254 字节。虽然 1 字节的值能表示的数值范围是 0 到 255,但是压缩列表中 zlend 的取值默认是 255,因此,就默认用 255 表示整个压缩列表的结束,其他表示长度的地方就不能再用 255 这个值了。所以,当上一个 entry 长度小于 254 字节时,prev_len 取值为 1 字节;

否则,就取值为 5 字节。

len:当前元素长度,4字节

encoding: 编码方式,1字节

content:保存实际数据

如上图,展示了一个总长为80字节,包含3个节点的压缩列表。如果我们有一个指向压缩列表起始地址的指针p,那么表尾节点的地址就是P+60。

回到 存储海量图片并要求快速定位图片的例子:

每个 entry 保存一个图片存储对象 ID(8 字节),此时,每个 entry 的 prev_len 只需要 1 个字节就行,因为每个 entry 的前一个 entry 长度都只有 8 字节,小于 254 字节。这样一来,一个图片的存储对象 ID 所占用的内存大小是 14 字节(1+4+1+8=14),实际分配 16 字节。

2.使用ziplist好处以及 ziplist应用
Redis 基于压缩列表实现了 List、Hash 和 Sorted Set 这样的集合类型,这样做的最大好处就是节省了 dictEntry 的开销。当你用 String 类型时,一个键值对就有一个 dictEntry,要用 32 字节空间。但采用集合类型时,一个 key 就对应一个集合的数据,能保存的数据多了很多,但也只用了一个 dictEntry,这样就节省了内存。

3.如何用集合类型保存单值的键值对?
在保存单值的键值对时,可以采用基于 Hash 类型的二级编码方法。这里说的二级编码,就是把一个单值的数据拆分成两部分,前一部分作为 Hash 集合的 key,后一部分作为 Hash 集合的 value,这样一来,我们就可以把单值数据保存到 Hash 集合中了。

以图片 ID 1101000060 和图片存储对象 ID 3302000080 为例,我们可以把图片 ID 的前 7 位(1101000)作为 Hash 类型的键,把图片 ID 的最后 3 位(060)和图片存储对象 ID 分别作为 Hash 类型值中的 key 和 value。

增加一条记录后,内存占用只增加了 16 字节:

127.0.0.1:6379> hset 1101000 060 3302000080
(integer) 1
1
2
4.什么时候使用ziplist?
Redis Hash 类型的两种底层实现结构,分别是压缩列表和哈希表。

Hash 类型底层结构什么时候使用压缩列表,什么时候使用哈希表呢?其实,Hash 类型设置了用压缩列表保存数据时的两个阈值,一旦超过了阈值,Hash 类型就会用哈希表来保存数据了。

hash-max-ziplist-entries:表示用压缩列表保存时哈希集合中的最大元素个数。
hash-max-ziplist-value:表示用压缩列表保存时哈希集合中单个元素的最大长度。
为了能充分使用压缩列表的精简内存布局,我们一般要控制保存在 Hash 集合中的元素个数。所以,在刚才的二级编码中,我们只用图片 ID 最后 3 位作为 Hash 集合的 key,也就保证了 Hash 集合的元素个数不超过 1000,同时,我们把 hash-max-ziplist-entries 设置为 1000,这样一来,Hash 集合就可以一直使用压缩列表来节省内存空间了。

4.集合类型:一亿个key要统计怎么办?
总结

几种类型使用:

HyperLogLog:基数统计,集合元素量达到亿级别而且不需要精确统计

Bitmap:记录的数据只有 0 和 1 两个值的状态

Set 和 Sorted Set:都支持多种聚合统计(交并差集),不过,对于差集计算来说,只有 Set 支持

List和Sorted Set:

排序统计时,List 中的元素虽然有序,但是一旦有新元素插入,原来的元素在 List 中的位置就会移动,那么,按位置读取的排序结果可能就不准确了。而 Sorted Set 本身是按照集合元素的权重排序,可以准确地按序获取结果,所以建议你优先使用它。

redis核心与实战(一)数据结构篇相关推荐

  1. redis有序集合键(数据结构篇)

    文章目录 有序集合(soted set / zset) 有序集合示例 基本操作 添加元素 删除元素 返回元素的分值 增加或减少元素的分值 返回有序集合的基数 返回元素的排名(rank) 返回元素的逆序 ...

  2. 【送书福利-第四期】从程序员到架构师:大数据量、缓存、高并发、微服务、多团队协同等核心场景实战书籍

    大家好,我是洲洲,欢迎关注,一个爱听周杰伦的程序员.关注公众号[程序员洲洲]即可获得10G学习资料.面试笔记.大厂独家学习体系路线等-还可以加入技术交流群欢迎大家在CSDN后台私信我! 送书福利-第四 ...

  3. Redis核心技术与实战-学习笔记(十四):时间序列数据

    一.时间序列数据 时间序列数据:没有严格的关系模型,记录的信息可以表示成键和值的关系. (例如,一个设备ID对应一条记录): 时间序列数据的读写特点 持续高并发写入,需要连续记录数万个设备的实时状态值 ...

  4. Redis 核心篇:唯快不破的秘密

    " 天下武功,无坚不摧,唯快不破! " 学习一个技术,通常只接触了零散的技术点,没有在脑海里建立一个完整的知识框架和架构体系,没有系统观.这样会很吃力,而且会出现一看好像自己会,过 ...

  5. Redis入门到实战(实战篇)缓存更新、穿透、雪崩、击穿!

    Redis基础篇 Java面试宝典-redis 实战篇Redis 开篇导读 亲爱的小伙伴们大家好,马上咱们就开始实战篇的内容了,相信通过本章的学习,小伙伴们就能理解各种redis的使用啦,接下来咱们来 ...

  6. Redis总结_实战篇

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 实战篇Redis 开篇导读 1.短信登录 1.1.导入黑马点评项目 1.1.1 .导入SQL 1.1.2.有关当前模型 1. ...

  7. redis存10万条数据_redis如何存储数据

    飞天技术汇 | 阿里云Redis产品升级大全 阿里云Redis重磅产品升级:全球多活版.混合存储版.多线程性能增强版. 这期飞天技术汇你将看到 ● 企业如何实现业务快速全球化布局 ● 冷热数据如何分离 ...

  8. 网易有道 | REDIS 云原生实战

    REDIS 云原生实战 摘要 本次以Redis为范例,阐述了有道基础架构团队在基础设施容器化道路上的实践,主要将从声明式管理,Operator工作原理,容器编排,主从模式,集群模式,高可用策略,集群扩 ...

  9. Redis核心数据结构List应用场景-商品列表、缓存击穿、PV阅读量、抢红包、推送帖子、普通分布式锁、Redis可重入锁与红锁

    List应用场景 Redis之List 一. Redis list命令实战 二.商品列表 高并发的淘宝聚划算实现技术方案 SpringBoot+Redis实现商品列表功能 二.缓存击穿 什么是缓存击穿 ...

最新文章

  1. IOS开发控制器之间传值的几种小方法
  2. [译] Android 上一次编写,随处测试
  3. 持续集成工具Jenkins看这篇就够啦!
  4. CSS3 Media Queries在iPhone4和iPad上的运用
  5. Java Lambdas简介
  6. 关系数据库——视图/存储过程/触发器
  7. C++Primer学习之一引用和指针
  8. 【燕郊】【2015-12-31】【知乎】
  9. 用双边模式,让生意立刻火爆
  10. JAVA 多线程学习总结
  11. python 怎么得到图像深度图 软件_Python为8bit深度图像应用color map
  12. Selenium爬虫 -- Pyhton进阶:使用cookie登陆某网站
  13. 数据结构 KMP 算法实现
  14. ADT公司G729 方案指标
  15. Python图像(字母数字)识别
  16. Vue3 的新特性(二) —— Composition-Api
  17. 检查型异常有哪些java_JAVA系列之检查型异常与非检查型异常的详解
  18. NTFS文件系统下文件恢复
  19. win10升级助手_不用QQ也能电脑远程,win10这隐藏功能太良心了!真后悔发现太晚...
  20. 为什么我的微信小程序开发工具调试窗口一片空白?

热门文章

  1. android 各版本市占率,Android各版本市占率:果冻豆遥遥领先
  2. arcmap创建空间索引_GIS中创建空间索引的一种方法
  3. npoi 所有列调整为一页_别再浪费纸了,一张纸就能打印Word、Excel、PPT所有内容,真厉害...
  4. qt中使用QStringLiteral宏来实现带参数的输出
  5. GPUImage – 亮度平均 GPUImageLuminosity
  6. C语言 ##__VA_ARGS__ - C语言零基础入门教程
  7. php正则表达式 匹配数字,正则表达式之匹配数字范围
  8. pdm 导入mysql 注释_PowerDesigner逆向导入MYSQL数据库并显示中文注释(转载)
  9. 盛金公式解一元三次方程_【国际数学竞赛】高次方程求根
  10. java c标签 if有值_c标签 if else c标签 总结