Redis 存储原理和数据模型

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 和 valuedict *expires; // 存储所有过期的 keydict *blocking_keys; // 存储阻塞连接的 keydict *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.mzph.cn/news/713495.shtml

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

相关文章

一种基于三角剖分划分白区范围的白平衡算法

常规的白平衡算法中,一般会通过标准色温的R/G-B/G建议色温坐标系,然后在该坐标系中设定白区范围,对落入到白区范围的R/G/B进行加权统计处理,输出给到软件进行白平衡的增益计算。 所介绍的这篇专利利用三角剖分的算法,在划定的白区范围内,利用各个标准色温光源下所标定的白…

STM32------分析GPIO寄存器

一、初始LED原理图 共阴极led LED发光二极管&#xff0c;需要有电流通过才能点亮&#xff0c;当有电压差就会产生电流 二极管两端的电压差超过2.7v就会有电流通过 电阻的作用 由于公式IV/R 不加电阻容易造成瞬间电流无穷大 发光二极管工作电流为10-20MA 3.3v / 1kΩ 3.…

C#中什么是非托管代码?托管代码和非托管代码有什么区别

在C#中&#xff0c;托管代码和非托管代码是两种不同类型的代码&#xff0c;它们在内存管理和执行环境上有所不同。 托管代码&#xff08;Managed Code&#xff09;&#xff1a; 托管代码是由.NET运行时&#xff08;CLR&#xff0c;Common Language Runtime&#xff09;管理和执…

新能源汽车产业架构设计与实现:引领未来出行新风向

随着环保意识的增强和能源结构的转型&#xff0c;新能源汽车产业正迅速崛起成为汽车行业的新宠。构建一个完善的新能源汽车产业架构对于推动产业发展、提升竞争力至关重要。本文将从设计原则、关键技术、产业生态等方面&#xff0c;探讨如何设计与实现新能源汽车产业架构。 ##…

那些壁纸,不只是背景

1、方小童在线工具集 网址&#xff1a; 方小童 该网站是一款在线工具集合的网站&#xff0c;目前包含PDF文件在线转换、随机生成美女图片、精美壁纸、电子书搜索等功能&#xff0c;喜欢的可以赶紧去试试&#xff01;

【快速选择】解决TopK问题

目录 一、什么是TopK问题 二、优先级队列 优先级队列介绍 代码实现 三、使用优先级队列解决TopK问题 四、快速选择算法解决TopK问题 快速选择 图解快速选择 代码解决前k小个元素 五、优先级队列与快速选则算法比较 优先级队列 快速选择 一、什么是TopK问题 TopK问题…

Linux Seccomp 简介

文章目录 一、简介二、架构三、Original/Strict Mode四、Seccomp-bpf五、seccomp系统调用六、Linux Capabilities and Seccomp6.1 Linux Capabilities6.2 Linux Seccomp 参考资料 一、简介 Seccomp&#xff08;secure computing&#xff09;是Linux内核中的一项计算机安全功能…

软考 系统分析师系列知识点之需求获取(7)

所属章节&#xff1a; 第11章. 软件需求工程 第2节. 需求获取 需求获取是一个确定和理解不同的项目干系人的需求和约束的过程。需求获取是一件看上去很简单、做起来却很难的事情。需求获取是否科学、准备是否充分&#xff0c;对获取出来的结果影响很大&#xff0c;这是因为大部…

Leetcode刷题(十八)

一、203. 移除链表元素 代码&#xff1a; class Solution:def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:while head and head.val val:head head.nextpre, cur head, headwhile cur:if cur.val val:pre.next cur.nextelse:p…

全闪存加速信创数据库数仓一体机解决方案

立足行业&#xff0c;深度解读 在新的大数据生态中&#xff0c;传统数据库/数据仓库技术和产品成为大数据生态中的组成部分&#xff0c;对结构化数据的存储和计算进行支撑。 数据库&数据仓库一体机是高端、核心数据管理产品&#xff0c;在我国党政、银行、交通等领域广泛…

nginx出现 “414 request-uri too large”

nginx出现 “414 request-uri too large” 1.修改传参方式 POST 2.字段能变成后端获取就自己获取&#xff0c;不用前端传 3.修改nginx配置&#xff0c;添加client_header_buffer_size 512k;large_client_header_buffers 4 512k;配置

2022年CSP-J认证 CCF信息学奥赛C++ 中小学初级组 第一轮真题-完善程序题解析

2022CCF认证第一轮&#xff08;CSP-J&#xff09;真题 三、完善程序题 第一题 枚举因数 从小到大打印正整数n的所有正因数。试补全枚举程序 #include <iostream> using namespace std;int main(){int n;cin >> n;vector<int> fac;fac.reserve((int)ceil(…

C++的引用

目录 引用 常引用 指针与引用的关系 小拓展 引用的价值 做形参 传值、传引用的效率比较 做返回值 函数传值返回 函数传引用返回&#xff08;错误示范&#xff09; 野引用&#xff08;错误示范&#xff09; 引用的正常应用 值和引用作为返回值类型的性能比较 引用和…

spring-boot-starter-parent和spring-boot-dependencies介绍

springboot项目的pom文件中&#xff0c;我们经常看见这样(下图)两种springboot的版本依赖管理方式&#xff1b;图片中的这两种依赖声明方式任意用其中一种都可以。文章后面会简单阐述一下区别和使用场景。 事例中完整的pom文件 <?xml version"1.0" encoding&quo…

阿尔卡特Adixen ADP/ADS 系列 2 干泵使用说明

阿尔卡特Adixen ADP/ADS 系列 2 干泵使用说明

HTML教程(3)——常用标签(1)

一、图片标签 1.场景&#xff1a;在网页中显示图片 2.基本写法&#xff1a; <img src""> 3.特点&#xff1a;单标签&#xff0c;img标签需要展示对应的效果&#xff0c;需要借助其属性进行设置 4常用属性&#xff1a; src&#xff1a;其属性值为目标图片…

【框架】Spring 框架重点解析

Spring 框架重点解析 1. Spring 框架中的单例 bean 是线程安全的吗&#xff1f; 不是线程安全的 Spring 框架中有一个 Scope 注解&#xff0c;默认的值是 singleton&#xff0c;即单例的&#xff1b;因为一般在 Spring 的 bean 对象都是无状态的&#xff08;在生命周期中不被…

解决Mybatis报Type interface *.*Mapper is not known to the MapperRegis

解决Mybatis报Type interface *.*Mapper is not known to the MapperRegis 问题发现问题解决方法一&#xff1a;检查Mapper文件的namespace路径是否正确方法二&#xff1a;使用其他方法是否正确 问题发现 在学习MyBatis框架的时候&#xff0c;不使用 XML 构建 SqlSessionFacto…

Project_Euler-44 题解

Project_Euler-44 题解 题目 思路 题目给出了一个性质&#xff0c;让我在对应性质的数据中找出目标值&#xff0c;这种问题首先想到的就是枚举。 我们可以枚举 P k P_k Pk​ &#xff0c;对于每一个 P k P_k Pk​ &#xff0c;我们再枚举 P j P_j Pj​&#xff0c; P j P_…

【ue5】滑铲系统蓝图笔记

大致逻辑如下&#xff1a; 一、导入动画 滑铲蹲待机蹲行走 导入到文件夹中 可以右键设置颜色&#xff0c;便于区分。 二、调整动画 1.启动根运动 启动根运动后&#xff0c;人物才可以位移&#xff0c;不然只能在原地。 打开动画序列&#xff0c;勾选启用根运动Enabled…