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.pswp.cn/news/713495.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/713495.shtml
英文地址,請注明出處:http://en.pswp.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…

字符串函數 sscanf() 詳解

什么是 sscanf() 函數&#xff1f; sscanf() 函數是 C 語言中的一個標準庫函數&#xff0c;它的作用是從一個字符串中按照指定的格式提取數據&#xff0c;并將其存儲到對應的變量中。它的原型如下&#xff1a; int sscanf(const char *str, const char *format, ...);其中&am…

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_…