探索Redis是否為單線程的奧秘(文末送書)

在這里插入圖片描述
🌈個人主頁:聆風吟
🔥系列專欄:數據結構、網絡奇遇記
🔖少年有夢不應止于心動,更要付諸行動。


文章目錄

  • 📋前言
  • 一. Redis中的多線程
  • 二. I/O多線程
  • 三. Redis中的多進程
  • 四. 結論
  • 五. 書籍推薦
    • 5.1 書籍介紹
    • 5.2 作者簡介
    • 5.3 粉絲福利

參與活動方式文末詳見。

📋前言

很多人都遇到過這么一道面試題:Redis是單線程還是多線程?這個問題既簡單又復雜。說他簡單是因為大多數人都知道Redis是單線程,說復雜是因為這個答案其實并不準確。

難道Redis不是單線程?我們啟動一個Redis實例,驗證一下就知道了。Redis安裝部署方式如下所示:

// 下載
wget https://download.redis.io/redis-stable.tar.gz
tar -xzvf redis-stable.tar.gz// 編譯安裝
cd redis-stable
make// 驗證是否安裝成功
./src/redis-server -v
Redis server v=7.2.4

接下來啟動Redis實例,使用命令ps查看所有線程,如下所示:

// 啟動Redis實例
./src/redis-server ./redis.conf// 查看實例進程ID
ps aux | grep redis
root     385806  0.0  0.0 245472 11200 pts/2    Sl+  17:32   0:00 ./src/redis-server 127.0.0.1:6379// 查看所有線程
ps -L -p 385806PID    LWP TTY          TIME CMD
385806 385806 pts/2    00:00:00 redis-server
385806 385809 pts/2    00:00:00 bio_close_file
385806 385810 pts/2    00:00:00 bio_aof
385806 385811 pts/2    00:00:00 bio_lazy_free
385806 385812 pts/2    00:00:00 jemalloc_bg_thd
385806 385813 pts/2    00:00:00 jemalloc_bg_thd

竟然有6個線程!不是說Redis是單線程嗎?怎么會有這么多線程呢?

這6個線程的含義你可能不太了解,但是通過這個示例至少說明Redis并不是單線程。



一. Redis中的多線程

接下來我們逐個介紹上述6個線程的作用:

  1. redis-server:
    主線程,用于接收并處理客戶端請求。

  2. jemalloc_bg_thd:
    jemalloc 是新一代的內存分配器,Redis底層使用他管理內存。

  3. bio_xxx:
    以bio前綴開始的都是異步線程,用于異步執行一些耗時任務。其中,線程bio_close_file用于異步刪除文件,線程bio_aof用于異步將AOF文件刷到磁盤,線程bio_lazy_free用于異步刪除數據(懶刪除)。

需要說明的是,主線程是通過隊列將任務分發給異步線程的,并且這一操作是需要加鎖的。主線程與異步線程的關系如下圖所示:
在這里插入圖片描述
這里我們以懶刪除為例,講解為什么要使用異步線程。Redis是一款內存數據庫,支持多種數據類型,包括字符串、列表、哈希表、集合等。思考一下,刪除(DEL)列表類型數據的流程是怎樣的呢?第一步從數據庫字典中刪除該鍵值對,第二步遍歷并刪除列表中的所有元素(釋放內存)。想想如果列表中的元素數目非常多呢?這一步將非常耗時。這種刪除方式稱為同步刪除,流程如下圖所示:
在這里插入圖片描述

針對上述問題,Redis提出了懶刪除(異步刪除),主線程在收到刪除命令(UNLINK)時,首先從數據庫字典中刪除該鍵值對,隨后再將刪除任務分發給異步線程bio_lazy_free,由異步線程執行第二步耗時邏輯。這時候的流程如下圖所示:
在這里插入圖片描述



二. I/O多線程

難道Redis是多線程?那為什么我們老說Redis是單線程呢?這是因為讀取客戶端命令請求,執行命令以及向客戶端返回結果都是在主線程完成的。不然的話,多線程同時操作內存數據庫,并發問題如何解決?如果每次操作之前都加鎖,那和單線程又有什么區別呢?

當然這一流程在Redis6.0版本也發生了改變,Redis官方指出,Redis是基于內存的鍵值對數據庫,執行命令的過程是非常快的,讀取客戶端命令請求和向客戶端返回結果(即網絡I/O)通常會成為Redis的性能瓶頸。

因此,在Redis 6.0版本,作者加入了多線程I/O的能力,即可以開啟多個I/O線程,并行讀取客戶端命令請求,并行向客戶端返回結果。I/O多線程能力使得Redis性能提升至少一倍。

為了開啟多線程I/O能力,需要先修改配置文件redis.conf:

io-threads-do-reads yes
io-threads 4

這兩個配置含義如下:

  • io-threads-do-reads:是否開啟多線程I/O能力,默認為"no";

  • io-threads:I/O線程數目,默認為1,即只使用主線程執行網絡I/O,線程數最大為128;該配置應該根據CPU核數設置,作者建議,4核CPU設置2~3個I/O線程,8核CPU設置6個I/O線程。

開啟多線程I/O能力之后,重新啟動Redis實例,查看所有線程,結果如下:

ps -L -p 104648PID    LWP TTY          TIME CMD
104648 104648 pts/1    00:00:00 redis-server
104648 104654 pts/1    00:00:00 io_thd_1
104648 104655 pts/1    00:00:00 io_thd_2
104648 104656 pts/1    00:00:00 io_thd_3
……

由于我們設置了io-threads等于4,所以會創建4個線程用于執行I/O操作(包括主線程),上述結果符合預期。

當然,只有I/O階段才使用了多線程,處理命令請求還是單線程,畢竟多線程操作內存數據存在并發問題。

最后,開啟了I/O多線程之后,命令的執行流程如下圖所示:
在這里插入圖片描述



三. Redis中的多進程

Redis還有多進程?是的。在某些場景下,Redis也會創建多個子進程來執行一些任務。以持久化為例,Redis支持兩種類型的持久化:

  • AOF(Append Only File):可以看作是命令的日志文件,Redis會將每一個寫命令都追加到AOF文件。

  • RDB(Redis Database):以快照的方式存儲Redis內存中的數據。命令SAVE用于手動觸發RDB持久化。想想如果Redis中的數據量非常大,持久化操作必然耗時比較長,而Redis是單線程處理命令請求,那么當命令SAVE的執行時間過長時,必然會影響其他命令的執行。

命令SAVE有可能會阻塞其他請求,為此,Redis又引入了命令BGSAVE,該命令會創建一個子進程來執行持久化操作,這樣就不會影響主進程執行其他請求了。

我們可以手動執行命令BGSAVE驗證。首先,使用GDB跟蹤Redis進程,添加斷點,讓子進程阻塞在持久化邏輯。如下所示:

// 查詢Redis進程ID
ps aux | grep redis
root     448144  0.1  0.0 270060 11520 pts/1    tl+  17:00   0:00 ./src/redis-server 127.0.0.1:6379// GDB跟蹤進程
gdb -p 448144// 跟蹤創建的子進程(默認GDB只跟蹤主進程,需手動設置)
(gdb) set follow-fork-mode child// 函數rdbSaveDb用于持久化數據快照
(gdb) b rdbSaveDb
Breakpoint 1 at 0x541a10: file rdb.c, line 1300.
(gdb) c

設置好斷點之后,使用Redis客戶端發送命令BGSAVE,結果如下:

// 請求立即返回
127.0.0.1:6379> bgsave
Background saving started// GDB輸出以下信息
[New process 452541]
Breakpoint 1, rdbSaveDb (...) at rdb.c:1300

可以看到,GDB目前跟蹤的是子進程,進程ID是452541。也可以通過Linux命令 ps 查看所有進程,結果如下:

ps aux | grep redis
root     448144  0.0  0.0 270060 11520 pts/1    Sl+  17:00   0:00 ./src/redis-server 127.0.0.1:6379
root     452541  0.0  0.0 270064 11412 pts/1    t+   17:19   0:00 redis-rdb-bgsave 127.0.0.1:6379

可以看到子進程的名稱是redis-rdb-bgsave,也就是該進程將所有數據的快照持久化在RDB文件。

最后再思考兩個問題。
問題1:為什么采用子進程而不是子線程呢?
????因為RDB是將數據快照持久化存儲,如果采用子線程,主線程與子線程將會共享內存數據,主線程在持久化的同時還會修改內存數據,這有可能導致數據不一致。而主進程與子進程的內存數據是完全隔離的,不存在此問題。

問題2:假設Redis內存中存儲了10GB的數據,在創建子進程執行持久化操作之后,此時子進程也需要10GB的內存嗎?復制10GB的內存數據,也會比較耗時吧?另外如果系統只有15GB的內存,還能執行BGSAVE命令嗎?
????這里有一個概念叫寫時復制(copy on write),在使用系統調用fork創建子進程之后,主進程與子進程的內存數據暫時還是共享的,但是當主進程需要修改內存數據時,系統會自動將該內存塊復制一份,以此實現內存數據的隔離。
命令BGSAVE的執行流程如下圖所示:
在這里插入圖片描述



四. 結論

Redis的進程模型/線程模型還是比較復雜的,這里也只是簡單介紹了部分場景下的多線程以及多進程,其他場景下的多線程、多進程還有待讀者自己研究。



五. 書籍推薦

5.1 書籍介紹

在這里插入圖片描述
全書主要分為三部分介紹Redis。

  • 第一部分介紹Redis6中使用的數據結構,包括動態字符串、跳躍表、壓縮列表、字典、整數集合和快速鏈表,詳細介紹其基本結構及常見操作。
  • 第二部分為本書核心篇章,首先介紹了Redis6的啟動流程,命令解析流程,之后對Redis6中的命令實現進行了全面的介紹,包括鍵命令、字符串命令、哈希表命令、列表命令、集合及有序集合命令、地理位置相關的GEO命令、統計相關的HyperLogLog命令。
  • 第三部分,主要介紹了Redis6的一些特性及使用,包括事務、持久化、主從復制以及集群等。

5.2 作者簡介

李樂:好未來Golang開發專家、西安電子科技大學碩士,曾就職于滴滴,樂于鉆研技術與源碼,合著有《高效使用Redis:一書學透數據存儲與高可用集群》《Redis5設計與源碼分析》《Nginx底層設計與源碼分析》。


5.3 粉絲福利

送書規則:

  • ?參與方式:關注博主、點贊、收藏、評論(每人最多評論三次)

  • ??本次送書1~5本【取決于閱讀量,閱讀量越多,送的越多】

  • 📆 活動截止時間:2024-2-27 19:00:00 | 由博主動態公布抽獎結果

🔥注:活動結束后,會私信中獎粉絲的,各位注意查看私信哦!
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/696125.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/696125.shtml
英文地址,請注明出處:http://en.pswp.cn/news/696125.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

高效時間管理法則

你是否天天在忙,是否忙的不得要領,認真領會時間管理的四象限工作法,它會讓你的工作變得高效。 目錄 一、時間管理的誤區 二、時間是如何被浪費的? 內部因素 外部因素 三、時間管理的5個階段 1.公雞型時間管理: …

第一個Qt程序中的秘密

創建第一個程序 首先我們打開Qt Creator 打開文件->New Projects... 菜單,創建我們的第一個Qt項目 選擇 Qt Widgets Application,點擊選擇...按鈕 之后,輸入項目名稱QtLearning,并選擇創建路徑, 在build system中選…

ConnectWise ScreenConnect 身份驗證繞過漏洞復現可RCE(CVE-2024-1709)

0x01 產品簡介 ConnectWise ScreenConnect ,是一款自托管的遠程桌面軟件應用,該款軟件允許用戶自行托管,可以在自己的服務器、個人電腦、虛擬機或虛擬專用服務器上運行。 0x02 漏洞概述 ConnectWise ScreenConnect低于23.9.8 版本的產品中,SetupWizard.aspx接口處存在身…

Android14 InputManager-焦點窗口的更新

設置焦點時需要 先設置焦點APP mFo-cusedApp是一個AppWindowToken,在WMS中用來表示當前處于Resume狀態的Activity。它是由AMS在開始啟動一個Activity時調用WMS的setFocusedApp()函數設置的。 考慮以下應用場景,當用戶從Launche…

內存管理——線性內存,進程空間

低2G為進程空間 開始地址結束地址大小屬性00xFFFFF1M保留0x1000000x102FFF棧不固定位置、大小0x1030000x143FFF堆不固定位置、大小0x400000主程序文件不固定位置、大小加載dll不固定位置、大小0x7ffdd000TIB位置,大小編譯時固定0x7FFFE000系統與用戶共享數據塊位置…

[newstarctf2023] --RE wp

AndroGenshin: rc4加密表,base64換表: 腳本梭就行 python username b"genshinimpact" base64_table [125, 239, 101, 151, 77, 163, 163, 110, 58, 230, 186, 206, 84, 84, 189, 193, 30, 63, 104, 178, 130, 211,164, 94, 75, 16, 32, 33…

發布 rust 源碼包 (crates.io)

rust 編程語言的包 (或者 庫, library) 叫做 crate, 也就是軟件中的一個組件. 一個完整的軟件通常由多個 crate 組成, rust 編譯器 (rustc) 一次編譯一整個 crate, 不同的 crate 可以同時并行編譯. rust 官方有一個集中發布開源包的網站 crates.io. 發布在這上面的 crate 可以…

uniapp微信公眾號H5分享

如果項目文件node_modules中沒有weixin-js-sdk文件&#xff0c;則直接使用本文章提供的&#xff1b; 如果不生效&#xff0c;則在template.h5.html中引入 <script src"https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> 首先引入weixin-js-…

vue3框架組件自動導入unplugin-vue-components

1.安裝 npm i unplugin-vue-components -save-dev 2.配置 我這里用的是Vue CLI&#xff0c;所以要在vue.config.js文件中添加配置&#xff0c;官網中有寫不同打包工具的配置寫法 框架我使用的是Element Plus&#xff0c;使用前去官網查看自己的框架是否支持&#xff0c;主流…

LLM之RAG實戰(二十七)| 如何評估RAG系統

有沒有想過今天的一些應用程序是如何看起來幾乎神奇地智能的&#xff1f;這種魔力很大一部分來自于一種叫做RAG和LLM的東西。把RAG&#xff08;Retrieval Augmented Generation&#xff09;想象成人工智能世界里聰明的書呆子&#xff0c;它會挖掘大量信息&#xff0c;準確地找到…

電腦黑屏什么都不顯示怎么辦 電腦開機黑屏不顯示任何東西的4種解決辦法

相信有很多網友都有經歷電腦開機黑屏不顯示任何東西&#xff0c;找了很多方法都沒處理好&#xff0c;其實關于這個的問題&#xff0c;首先還是要了解清楚開機黑屏的原因&#xff0c;才能夠對癥下藥&#xff0c;下面大家可以跟小編一起來看看怎么解決吧 電腦開機黑屏不顯示任何…

【無刷電機學習】基礎概念及原理介紹(持續更新中...)

目錄&#xff08;2024.02.22版&#xff09; 1 定義 2 各種電機優勢比較 2.1 有刷與無刷比較 2.2 交流與直流比較 2.3 內轉子與外轉子比較 2.4 低壓BLDC的一些優點 3 基本原理 3.1 單相無刷電機 3.2 三相無刷電機 4 驅動方法 4.1 六步換相控制 4.1.1 基本原理 4…

突發!AI獨角獸「竹間智能」被曝停工停產6個月

大家好我是二狗。 今天早上起來刷朋友圈&#xff0c;看到一張截圖——AI創企竹間智能&#xff0c;宣稱因為公司所處的經營環境艱難&#xff0c;部分部門和崗位將從即日起停工停產6個月。 圖源&#xff1a;&#xff08;企服科學&#xff09; 下面是文字版&#xff1a; 由于公司…

Web服務器基礎介紹

目錄 Web服務器基礎介紹 一、HTML是什么&#xff1f; 二、靜態網頁和動態網頁 1、靜態網頁 2、動態網頁 3、動態網頁語言 PHP JSP Python Ruby 三、HTTP協議 1、HTTP協議是什么&#xff1f; 2、HTTP請求訪問的方法 3、GET與POST比較 GET&#xff1a; POST&…

Linux網絡編程(三-UDP協議)

目錄 一、UDP概述 二、UDP的首部格式 三、UDP緩沖區 四、基于UDP的應用層協議 五、常見問題 一、UDP概述 UDP(User Datagram Protocol&#xff0c;用戶數據協議報)是傳輸層協議&#xff0c;提供不可靠服務&#xff0c;其特點包括&#xff1a; 無連接&#xff1a;知道對端…

CSP-202309-3-梯度求解

CSP-202309-3-梯度求解 作為一個算法小白&#xff0c;本人第一次接觸大模擬的題&#xff0c;本題的算法參考自&#xff1a;【CSP】202309-3 梯度求解 解題思路 1.輸入處理 getchar();&#xff1a;從標準輸入讀取一個字符。這里它的作用可能是用來“吃掉”&#xff08;消耗&a…

Kafka_04_Topic和日志

Kafka_04_Topic和日志 Topic/PartitionTopicPartition 日志存儲存儲格式日志清理刪除壓縮 Topic/Partition Topic/Partition: Kafka中消息管理的基礎單位 Topic和Partition并不實際存在(僅邏輯上的概念) 如: Topic和Partition關系 // 每個日志文件可對應多個日志分段, 其還可…

緩存篇—緩存擊穿

在很多場景下&#xff0c;我們的業務通常會有幾個數據會被頻繁地訪問&#xff0c;比如秒殺活動&#xff0c;這類被頻地訪問的數據被稱為熱點數據。 如果緩存中的某個熱點數據過期了&#xff0c;此時大量的請求訪問了該熱點數據&#xff0c;就無法從緩存中讀取&#xff0c;直接…

《UE5_C++多人TPS完整教程》學習筆記22 ——《P23 記錄加入的玩家(Couting Incoming Players)》

本文為B站系列教學視頻 《UE5_C多人TPS完整教程》 —— 《P23 記錄加入的玩家&#xff08;Couting Incoming Players&#xff09;》 的學習筆記&#xff0c;該系列教學視頻為 Udemy 課程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻譯版&#xff0c;UP主&#xff…

前端面試問題(jwt/布局/vue數組下標/扁平化/菜單樹形/url api/新版本)

前端面試問題(jwt/布局/vue數組下標/扁平化/菜單樹形/url api/新版本) 1. jwt鑒權邏輯 前端 JWT 鑒權邏輯通常涉及在發起請求時攜帶 JWT&#xff0c;并在接收到響應后處理可能的授權問題。 1. 用戶登錄&#xff1a; 用戶提供憑證&#xff1a; 用戶在登錄界面輸入用戶名和密碼…