Redis 存储原理和数据模型

news2024/11/16 23:31:49

redis 是不是单线程

在这里插入图片描述

  • redis 单线程指的是命令处理在一个单线程中。
  • 主线程
    • redis-server:命令处理、网络事件的监听。
  • 辅助线程
    • bio_close_file:异步关闭大文件。
    • bio_aof_fsync:异步 aof 刷盘。
    • bio_lazy_free:异步清理大块内存。
    • io_thd_*:io 多线程。
    • jemalloc_bg_thd:后台线程,进行内存分配,内存释放。
  • 辅助线程负责处理阻塞的操作,这样可以不阻塞主线程,让主线程最大限度地处理命令,优化性能。

命令处理为什么是单线程

  • 单线程的局限:不能有耗时的操作,比如 CPU 运算、阻塞的 IO;对于 redis 而言会影响响应性能。
  • redis 处理 IO 密集型:
    • 磁盘 IO:
      • fork 进程,在子进程做持久化。
      • 使用 bio_aof_fsync,另起线程做持久化(异步 aof 刷盘)。
    • 网络 IO:
      • 服务多个客户端,造成 IO 密集;数据请求或返回数据量比较大。
      • 开启 IO 多线程。
  • redis 处理 CPU 密集型
    • 数据结构切换:redis 会根据当前数据量的大小,选择一个数据结构去存储。
    • 渐进式数据迁移:当数据量小的时候,会分配一个小的内存,当数据量大的时候,会分配一个大的内存(翻倍扩容),那么就需要将原来内存中的数据迁移到新的内存中,redis 不会将原数据一次性都挪过去,而是采用一定的策略逐渐挪过去。
  • 为什么不采用多线程
    • 加锁复杂、锁粒度不好控制。
    • 频繁的 CPU 上下文切换,抵消多线程的优势。

redis 单线程为什么快

  • 高效的 reactor 网络模型
  • 数据结构高效
    • 在执行效率与空间占用间保持平衡,可以进行数据结构切换。
  • redis 是内存数据库,大部分情况下:操作完内存后会立刻返回给客户端,不需要关注写磁盘的问题。特殊情况:使用 aof 持久化方式 + always 策略:每一次操作完内存后,都必须持久化到磁盘中,然后再返回给客户端。
  • 数据组织方式
    typedef struct redisDb {
    	dict *dict; // 存储所有的 key 和 value
    	dict *expires; // 存储所有过期的 key
    	dict *blocking_keys; // 存储阻塞连接的 key
    	dict *ready_keys; 
    	dict *watched_keys; // 被检测的 key ( MULTI/EXEC )
    }
    
    struct dict {
    	dictType *type;
    	
    	dictEntry **ht_table[2];
    	unsigned long ht_used[2];
    	
    	long rehashidx; // 默认为 -1,记录迁移的位置
    
    	int16_t pauserehash;
    	signed char ht_size_exp[2]; 
    }
    
    • redis 支持 16 个 db,默认使用 db0,可以通过 use 选择某一个 db。
    • redis 内部会分配一个指针数组(每一个数组元素都对应一个链表)。key 通过 hash 函数会生成一个 64 位的整数,这个整数对该数组的长度取余,得到一个该数组的索引值,然后将 key 和 value 存储在该索引位置的链表中。
    • ht_size_exp 记录指针数组长度 2 n 2^n 2n n n n 的值,指针数组长度为什么是 2 n 2^n 2n ?
      • 因为要把取余运算优化为位运算,优化的前提是 s i z e = 2 n size = 2^n size=2n ;当 s i z e = 2 n size = 2^n size=2n 时,hash(key) % size = hash(key) & (size - 1)
      • 取余运算中会有除法运算,计算机做除法运算会比较慢,做位运算会很快。 2 n 2^n 2n 又可以优化为 1 < < n 1<<n 1<<n
    • 负载因子 = used / / /size,used 是指针数组存储元素的个数,size 是指针数组的长度。负载因子越小哈希冲突越小,负载因子越大哈希冲突越大。
    • 扩容操作
      • 第一次分配指针数组空间长度为 4( 1 < < 2 1<<2 1<<2)。
        static int _dictExpandIfNeeded(dict *d)
        {
        	if (dictIsRehashing(d)) return DICT_OK;
        			
        	if(DICTHT_SIZE(d->ht_size_exp[0]) == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE)
        }
        
      • 当负载因子 ≥ 1 \geq 1 1 时,即 used / size >= 1,为了减小哈希冲突,会进行翻倍扩容。第二次扩容会准备一个空间长度为 8 的指针数组,然后将原来数组中的元素迁移到扩容后的数组中。如果正在 fork(在 rdb、aof 复写以及 rdb-aof 混用的情况下),会阻止扩容,但是此时若负载因子 > 5 >5 >5,索引效率大大降低,则会马上扩容。
    • 缩容操作
      • 当负载因子 < 0.1 < 0.1 <0.1 时,即 (size > 4) && ((used / size) < 0.1),会进行缩容,缩容后的数组长度恰好大于元素个数并且为 2 n ≥ 4 2^n \geq 4 2n4
    • 扩容操作和缩容操作都需要 rehash,因为 key-value 对的存储位置发生了变化。
    • 一个 dict 结构包含两个散列表(散列表 = 哈希函数 + 指针数组),为什么要准备两个 ht_table ?
      • 为了防止迁移元素较多时,迁移任务变为 CPU 密集型。
      • 使用两个 ht_table 可以将原来数组中的元素逐渐迁移到扩容后的数组中,而不是一次性将元素全部挪过去;当原来数组中的元素全部挪过去后,会 free 原数组;如果 free 的空间比较大,会使用 bio_lazy_free 另起线程去 free 这块空间。
    • 渐进式 rehash
      • 处于渐进式 rehash 阶段时,不会发生扩容缩容
      • 当指针数组中的元素过多的时候,不能一次性 rehash 到 ht_table[1],这样会长期占用 redis,其它命令得不到响应,所以需要使用渐进式 rehash。
      • 步骤:将 ht_table[0] 中的元素重新经过 hash 函数生成 64位整数,再对 ht_table[1] 的长度进行取余,从而映射到 ht_table[1]。
      • 策略:
        • 在每次增删改查的时候,迁移一个索引单位。
        • 在服务器空闲的时候,会迁移 1ms ,以 100 个索引单位为步长。
          int dictRehashMilliseconds(dict *d, int ms) {
          	if (d->pauserehash > 0) return 0;				
          	long long start = timeInMilliseconds();
          	int rehashes = 0;
          			
          	while(dictRehash(d, 100)) {
          		rehashes += 100;
          		if (timeInMilliseconds() - start > ms) break;
          	}
          	return rehashes;
          }
          

redis io 多线程工作原理

  • redis 采用 reactor 网络模型。
    在这里插入图片描述
  • redis 配置
    # redis.conf
    io-threads 4
    io-threads-do-reads yes
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1482806.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

fly-barrage 前端弹幕库(3):滚动弹幕的设计与实现

项目官网地址&#xff1a;https://fly-barrage.netlify.app/&#xff1b; &#x1f451;&#x1f40b;&#x1f389;如果感觉项目还不错的话&#xff0c;还请点下 star &#x1f31f;&#x1f31f;&#x1f31f;。 Gitee&#xff1a;https://gitee.com/fei_fei27/fly-barrage&a…

电子电器架构新趋势 —— 最佳着力点:域控制器

电子电器架构新趋势 —— 最佳着力点&#xff1a;域控制器 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师&#xff08;Wechat&#xff1a;gongkenan2013&#xff09;。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师…

Ubuntu进入python时报错:找不到命令 “python”,“python3” 命令来自 Debian 软件包 python3

一、错误描述 二、解决办法 进入”/usr/bin”目录下&#xff0c;查看/usr/bin目录中所有与python相关的文件和链接&#xff1a; cd /usr/bin ls -l | grep python 可以看到Python3指向的是Python3.10&#xff0c;而并无指向python3的软连接 只需要在python与python3之间手动…

探索数字未来:DApp钱包Defi引领新纪元

​小编介绍&#xff1a;10年专注商业模式设计及软件开发&#xff0c;擅长企业生态商业模式&#xff0c;商业零售会员增长裂变模式策划、商业闭环模式设计及方案落地&#xff1b;扶持10余个电商平台做到营收过千万&#xff0c;数百个平台达到百万会员&#xff0c;欢迎咨询。 随…

LINUX基础培训二十八之Shell正则表达式

一、何为正则表达式 在编写处理字符串的程序或网页时&#xff0c;经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说&#xff0c;正则表达式就是记录文本规则的代码。 在Windows/Dos下用于文件查找的通配符&#xff08; wildcard…

借助ChatGPT使用Python搭建一个工具网站

文章目录 前言网站搭建过程总结 前言 不知不觉ChatGPT已经风靡一年多了&#xff0c;现在基本每天工作时都会用到&#xff0c;相比于传统的搜索引擎它究竟强在哪呢&#xff1f;我觉得以往的搜索引擎是一个机器&#xff0c;你给它关键信息它能返回匹配关键词的内容数据&#xff…

【C语言】动态内存管理常用函数

前言 我们在之前学习的数组开辟的空间是固定不变的&#xff0c;有时候我们需要的空间⼤⼩在程序运⾏的时候才能知道~ c语言中的动态内存开辟&#xff0c;让程序员⾃⼰可以根据实际需求申请和释放相应空间&#xff0c;这使得空间的开辟变得灵活了许多。 欢迎关注个人主页&#x…

管家婆云上管家新帐套建立步骤

第一步&#xff0c;客户服务管理&#xff1b; 第二步&#xff0c;添加帐套&#xff1b; 第三步&#xff0c;点击“管理”进入&#xff1b;第四步&#xff0c;添加帐套信息&#xff1b; 第五步&#xff0c;找到客户&#xff0c;查看帐套信息&#xff1b; 第六步&#xff0c;确认…

微服务简介及其相关技术栈

目录 1、简介 2、技术栈 3、单体架构 4、分布式架构 5、微服务 6、总结 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅长web应用开发、数据结构和算法&#xff0c;初步涉猎Pyth…

【Java实战项目】SpringBoot + Vue3打造你的在线电子书平台!

今天给大家分享一个基础的Java实战项目&#xff0c;用SpringBoot和Vue3开发一个电子书平台&#xff0c;大家可以尝试做一下这个项目&#xff0c;以此来检验这段时间的学习成果&#xff01;废话不多说&#xff0c;下面正式进入项目&#xff1a; 一、项目介绍 1. 项目简介 在线…

基于springboot+vue的公寓报修管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

机器人与AGI会撞出什么火花?

真正的科技变革是不是就要来临了&#xff1f;各方大佬都开始布局机器人&#xff0c;对于普通人的就业会造成什么影响&#xff1f; ​ 优牛企讯-企业动态信息监控专家 在优牛企讯-企业动态监控专家搜索可知&#xff0c;全国目前的机器人公司已经达到了26401家&#xff0c;近一年…

VSCode通过SSH连接Docker环境进行开发

文章目录 VSCode 插件Docker 镜像构建镜像部署环境 VSCode 连接本地Docker容器VSCode SSH连接Docker容器VSCode 打开容器内目录文件 VSCode 插件 Remote - SSH Docker 镜像 https://hub.docker.com/_/golang # Golang 镜像 docker pull golang:1.22构建镜像 Dockerfile F…

AVT Prosilica GC Vision Cameras 相机视觉说明使用安装。具体详情内容可参看PDF目录内容。

AVT Prosilica GC Vision Cameras 相机视觉说明使用安装。具体详情内容可参看PDF目录内容。

3.1日学习打卡----初学FastDFS(一)

3.1日学习打卡 目录: 3.1日学习打卡一. 为什么要使用分布式文件系统二. FastDFS简介核心概念上传机制下载机制FastDFS环境搭建_LinuxFastDFS指令 一. 为什么要使用分布式文件系统 单机时代 初创时期由于时间紧迫&#xff0c;在各种资源有限的情况下&#xff0c;通常就直接在项…

varFormatter 数据格式化库 以性能优先的 快速的 内存对象格式转换

varFormatter 数据格式化 技术 开源技术栏 对象/变量格式化工具库&#xff0c;其支持将一个对象进行按照 JSON XML HTML 等格式进行转换&#xff0c;并获取到结果字符串&#xff01; 目录 文章目录 varFormatter 数据格式化 技术目录介绍获取方式 使用实例格式化组件的基本使…

查看网络连接的netstat

netstat是一个监控TCP/IP网络的非常有用的工具&#xff0c;可以显示路由表、实际的网络连接&#xff0c;以及每一个网络接口设备的状态信息&#xff0c;可以让用户得知目前都有哪些网络连接正在运作。netstat用户显示与IP、TCP、UDP和ICMP协议相关的统计数据&#xff0c;一般用…

ESP32 web 对接华为云平台--MQTT协议

文章目录 前言一、MQTT协议二、如何使用MQTT协议对接华为云1.注册华为云账号2.设备接入中创建资源空间3.如何连接4.通过MQTT.fx工具做初步对接4.1 设置连接信息4.2 连接平台 5.查看平台设备信息 三. 设备测对接平台1.ESP测引入MQTT库2.编码2.1前端编码修改2.2 后端接口修改 3.M…

28.HarmonyOS App(JAVA)多页签的实现(Tab)

HarmonyOS App(JAVA)多页签的实现&#xff08;Tab&#xff09; 页面可左右滑动&#xff0c;点击界面1,2,3切换到对应界面 PageSlider的创建和使用 在layout目录下的xml文件中创建PageSlider。 <PageSlider ohos:id"$id:page_slider" ohos:height"300vp&…

UCSF DOCK 分子对接详细案例(01)- rigid, fixed anchor, flexible dock

欢迎浏览我的CSND博客&#xff01; Blockbuater_drug …点击进入 文章目录 前言一、操作环境二、研究背景三、受体-配体结构文件准备3.1准备文件夹DOCK_workdir, 下载晶体结构3.1.1 来自湿实验的受体配体共晶结构&#xff1a;3.1.2 来自深度学习和语言模型推理预测的蛋白结构&a…