当前位置: 首页 > news >正文

Redis中间件(三):Redis存储原理与数据模型

文章目录

  • 一、redis实现kv数据组织方式
    • 0. redis的kv存储结构
      • 存储结构
      • 存储转换
    • 1. 单线程指什么, redis是不是单线程?
    • 2. 命令处理为什么是单线程,不采用多线程?
    • 3. 单线程为什么这么快?
      • 内存数据库(读/写速度极快)
      • Reactor网络模型(使用IO多路复用高效)
      • 数据结构高效(按需切换保持效率与空间平衡)
      • 数据组织方式高效 (按需扩容+Rehash避免一次性耗时)
    • *4 做了那些优化?
      • 分治 (rehash分摊 + 定时器空闲处理)
      • 耗时阻塞操作,在其他线程处理
      • 对象类型采用不同的数据结构实现
    • 5. redis IO多线程工作原理
      • 跳表skiplist
      • 柔性数组 list[ ];
      • reactor_io多线程网络模型
      • Q:为什么redis中字符串选择64字节为分界线?


一、redis实现kv数据组织方式

redis 中KV组织是通过字典,通过 hashtable 来实现;
hashtable 就是通 hash运算的方式来决定字符串放到数组的哪一个槽位,数组大小根据数据量来进行调整,所以就会涉及到扩容缩容

  • 字符串经过 hash 函数运算得到 64 位整数;
  • 64位整数 % 表长size 得到余数,即是字符串在数组中的槽位
    将数组存入对应的槽位中。

0. redis的kv存储结构

存储结构

  1. key-value键值对 通过hash的方式存储到数组中。
  2. value主要的数据结构有string,list,hash,set,zset; value的数据不是单一类型,可以嵌入kv键值对。
    请添加图片描述

存储转换

redis 在进行编码存储的时候,同一类型不同的数据量会导致编码的方式不同。
数据量的时候,以存储效率高 为主
数据量的时候,以运行查询快 为主
在这里插入图片描述
源码调试步骤

1】makefile编译make clear
bear make  (编译前先安装bear)
//推荐插件clongd: ctrl + 鼠标左键实现接口跳转2】编译调试
查看src/Makefile 确认是有 -g -ggdb, 表示带调试信息【3】配置两个文件
tasks.json   帮助编译代码指令
launch.json  怎么启动redis-server【4】启动调试
vscode 菜单 运行->启动调试

1. 单线程指什么, redis是不是单线程?

单线程:指命令处理在一个线程.
redis不是单线程:本质上是一个单线程执行命令、多线程做杂活的高性能数据库.

2. 命令处理为什么是单线程,不采用多线程?

前提:单线程不能有 (例如cpu运算、阻塞io)耗时操作, 而影响redis相应性能.

1. 避免加锁复杂度
+多线程会引入并发冲突,需要加锁控制:
+加锁粒度难把控,锁得多了性能下降,锁得少了数据错乱,容易出现死锁、竞态等问题。代码复杂度和维护成本上升;

2. 避免线程切换开销
+多线程会发生频繁的 CPU 上下文切换;
+线程切换需要保存/恢复寄存器等上下文信息,如果 Redis 每条命令都切换线程,反而会拉低整体性能;

3. 数据结构无需加锁
+单线程访问所有核心数据结构(如字典、跳表等),天然线程安全
操作原子性强,不会出现“半更新”的问题;
+命令顺序可控,执行逻辑简单可靠

不采用多线程原因
1.加锁复杂,redis对象类型由多个数据结构实现
2.频繁的的cpu上下文切换,抵消多线程的优势


3. 单线程为什么这么快?

内存数据库(读/写速度极快)

所有数据存储在内存中,读取/写入速度极快(O(1)、O(logN))

Reactor网络模型(使用IO多路复用高效)

详情在⇒ 5. redis IO多线程工作原理


数据结构高效(按需切换保持效率与空间平衡)

按需切换合适的数据结构,在执行效率与空间占用间保持平衡。

  • 数据结构切换规则
    请添加图片描述

数据组织方式高效 (按需扩容+Rehash避免一次性耗时)

*插件Bookmarks查看书签项目 : xie fan / redis · GitLab

0.负载因子 = used/size

  • used是数组存储元素个数, size是数组长度
  • redis负载因子是1,如果大于1扩容(翻倍),小于0.1缩容(正好缩到比used大的最小2的n次方的大小)

1.扩容 (解决冲突问题)
2.缩容(if 负载因子 < 0.1)
3.渐进式rehash (解决扩缩容引发问题,一步一步数据迁移)

  • 把扩容过程分摊到增删改查中携带一次rehash操作/在定时器中,在redis空闲时,每次rehash 1ms。
  • 处理渐进式rehash不会发生扩容和缩容,为了应对散列表中存储了大量元素,一次性hash耗时;以数组槽位为单位rehash

4.scan 将redis中散列表所有key枚举出来 (解决指令 "key"的耗时)

  • 采用高位进位加法遍历,rehash后的槽位在遍历顺序上是相邻的
    请添加图片描述

扩容的时候,会发生rehash,扩容后每个元素可能会有两个位置出现
请添加图片描述
缩容也会发生rehash,位置的调整与扩容相反请添加图片描述

  • 渐进式rehash:当 hashtable 中的元素过多的时候,不能一次性 rehash 到 ht[1] ,这样会长期占用 redis,其他命令得不到响应,所以需要使用渐进式 rehash;

    rehash步骤:将ht[0]中的元素重新经过 hash 函数生成64位整数,再对ht[1]长度进行取余,从而映射到ht[1]
    渐进式 rehash 规则:1. 分治的思想,将rehash分到之后的每步增删改查的操作当中;2. 在定时器中,闲时最大执行一毫秒rehash ,每次步长100个数组槽位
    处于渐进式 rehash 阶段时,是否会发生扩容缩容?不会!
    

*4 做了那些优化?

分治 (rehash分摊 + 定时器空闲处理)

  • 把rehash分摊到每一个操作步骤中:Redis的哈希表扩容不再一次性迁移数据,每次增删改查都“顺带”迁移一小部分数据,避免了一次性 rehash 导致服务卡顿。
  • 定时器 + 空闲时间处理:比如 redis空闲时,定时器设置以100为步长最多rehash 1ms,利用 Redis 空闲时间来释放大key。

耗时阻塞操作,在其他线程处理

操作线程方式
Lazy Free创建后台线程异步删除大 Key
AOF Rewrite在子进程中异步执行
RDB Save在子进程中快照,不阻塞主线程
Redis 6.0 I/O 多线程网络I/O在多线程中完成,提高并发性能

对象类型采用不同的数据结构实现

redis会根据数据的类型 + 内容大小 + 特性 来动态的选择最优的编码方式,即省内存又加快速度。
请添加图片描述


5. redis IO多线程工作原理

跳表skiplist

通过增加层级,每次从最高层级跳跃式快速找到节点;
理想情况O(log2(n)), 但现实中crud操作后都需重新构造理想的跳表;
现实优化:理想跳表 --> 概率型跳表 --> 实践型跳表
请添加图片描述

1.每个节点出现在哪些层是 随机的(通常用概率 p=0.252.每一层都是一个有序链表
3.高层节点稀疏,底层节点密集
4.增删改需要重新构建理想跳表结构
5.查找,插入,删除时间复杂度都是O(logn)
6.Redis中使用跳表实现了有序集合(Sorted Set,zset)的排序部分zset中同时使用 哈希表 + 跳表哈希表负责快速查找成员是否存在跳表负责按 score 排序并实现范围查询

跳表特性
1.多层级有序链表, 最底层包含所有元素
2.可以二分查找的数据结构, 增删改查都是O(log2n)
3.范围查询非常方便,通过O(log2n)快速找到边界,然后在最底层遭到范围内所有元素


柔性数组 list[ ];

1.柔性数组的大小是在运行时动态决定的; 它不占结构体静态大小,而是结构体分配内存时追加在后面的一段连续空间。
2. sizeof返回结构体大小不包含柔性数组的大小;一次malloc一次free

一种结构体末尾的数组字段,用于实现可变长度数组

struct MyStruct {int id;char name[20];//柔性数组成员,必须是结构体的最后一个字段int data[];  
};

reactor_io多线程网络模型

redis主线程管理调度 + 执行命令,IO 线程专职网络通信 ==> 让系统性能和单线程一致性两者兼得.

  • reactor网络模型

    多条客户端连接,开启io多线程
    1.没开启io多线程:串行操作
    2.开启io多线程: 先读数据和解析协议并行computer串行加密数据和发送数据 并行 
    

    请添加图片描述

  • 线程模型
    请添加图片描述

  • 处理流程在这里插入图片描述


Q:为什么redis中字符串选择64字节为分界线?

*内存分配优化*: 小于64字节的字符串能映射到更小的内存块,避免浪费
*CPU缓存优化*: 64字节正好是cache line大小,提高访问效率
*SDS优化策略*: Redis会针对短字符串启用更激进的性能优化策略
*内存碎片控制*: 避免频繁内存分配、释放引起的碎片化

存储原理角度思考
请添加图片描述


优秀笔记:
1. 存储原理与数据模型
2. Redis 存储原理与数据模型(三)
参考学习:https://github.com/0voice

http://www.dtcms.com/a/319623.html

相关文章:

  • Dokcer创建中间件环境
  • LeetCode:347.前K个高频元素
  • 手写数字识别实战 - 从传统机器学习到深度学习
  • 13-netty基础-手写rpc-消费方生成代理-05
  • Qt——入门
  • 数据赋能(386)——数据挖掘——迭代过程
  • Spring、Spring MVC、MyBatis 和 Spring Boot的关系
  • Ethereum:如何优雅部署 NPM 包中的第三方智能合约?
  • LoadBalancingSpi
  • Beelzebub靶机
  • MyCAT实战环节
  • 动手学深度学习13.10. 转置卷积 -笔记练习(PyTorch)
  • 在新建word中使用以前文件中的列表样式
  • Python调用Shell指令的方法与实践
  • 深海中的类型晨曦
  • Jmeter使用第一节-认识面板(Mac版)
  • 初识C++类的6个默认成员函数
  • 以复合赋值运算符(op=)优化单独运算符(op)的实现
  • BKP 与 RTC 时钟
  • 从Text2SQL到Text2Metrics:衡石指标管理技术跃迁
  • 【Bluedroid】蓝牙音频接收端活动设备切换机制深度解析(sink_set_active_device)
  • 密码学侧信道攻击(Side-channel Attack):从物理泄露中窃取密钥
  • 水库大坝安全监测系统主要概述
  • 护网行动之后:容器安全如何升级?微隔离打造内网“微堡垒”
  • SkyWalking-1--SkyWalking是什么?
  • 基于MATLAB实现支持向量机(SVM)分类
  • `/dev/vdb` 是一个新挂载的 4TB 硬盘,但目前尚未对其进行分区和格式化。
  • WebSocket 在多线程环境下处理 Session并发
  • 多数据中心运维:别让 “分布式” 变成 “混乱式”
  • 机器学习 [白板推导](七)[概率图模型]