日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

今天,我們來探討一個很多人都很關心的問題:“為什么單線程的 redis 能那么快?”

首先,我要和你厘清一個事實,我們通常說,Redis 是單線程,主要是指 Redis 的網絡 IO 和鍵值對讀寫是由一個線程來完成的,這也是 Redis 對外提供鍵值存儲服務的主要流程。但 Redis 的其他功能,比如持久化、異步刪除、集群數據同步等,其實是由額外的線程執行的。

所以,嚴格來說,Redis 并不是單線程,但是我們一般把 Redis 稱為單線程高性能,這樣顯得“酷”些。接下來,我也會把 Redis 稱為單線程模式。而且,這也會促使你緊接著提問:“為什么用單線程?為什么單線程能這么快?”

要弄明白這個問題,我們就要深入地學習下 Redis 的單線程設計機制以及多路復用機制。之后你在調優 Redis 性能時,也能更有針對性地避免會導致 Redis 單線程阻塞的操作,例如執行復雜度高的命令。

好了,話不多說,接下來,我們就先來學習下 Redis 采用單線程的原因。

Redis 為什么用單線程?

要更好地理解 Redis 為什么用單線程,我們就要先了解多線程的開銷。

多線程的開銷

多線程可以增加系統吞吐率,多線程機制可以將一個程序分為多個獨立運行的線程,每個線程可以同時執行不同任務,避免了任務之間的互相等待,提高了系統的響應速度。通過合理管理和調度這些線程,可以更好地利用計算機的處理能力,在較短時間內處理更多的請求。

多線程還有助于提高系統的擴展性。通過將任務拆分為多個子任務,每個線程負責執行其中一部分,可以更容易地將工作負載分配到多個處理單元上。這樣,在需要擴展系統處理能力時,只需增加更多的線程,而不需要修改整體架構或重新設計系統。這種可伸縮性使得系統在應對不斷增長的需求時更具競爭力。

下面的左圖是我們采用多線程時所期待的結果。

但是,請你注意,通常情況下,在我們采用多線程后,如果沒有良好的系統設計,實際得到的結果,其實是右圖所展示的那樣。我們剛開始增加線程數時,系統吞吐率會增加,但是,再進一步增加線程時,系統吞吐率就增長遲緩了,有時甚至還會出現下降的情況。

線程數與系統吞吐率

為什么會出現這種情況呢?一個關鍵的瓶頸在于,系統中通常會存在被多線程同時訪問的共享資源,比如一個共享的數據結構。當有多個線程要修改這個共享資源時,為了保證共享資源的正確性,就需要有額外的機制進行保證,而這個額外的機制,就會帶來額外的開銷。

拿 Redis 來說,在上節課中,我提到過,Redis 有 List 的數據類型,并提供出隊(LPOP)和入隊(LPUSH)操作。假設 Redis 采用多線程設計,如下圖所示,現在有兩個線程 A 和 B,線程 A 對一個 List 做 LPUSH 操作,并對隊列長度加 1。同時,線程 B 對該 List 執行 LPOP 操作,并對隊列長度減 1。為了保證隊列長度的正確性,Redis 需要讓線程 A 和 B 的 LPUSH 和 LPOP 串行執行,這樣一來,Redis 可以無誤地記錄它們對 List 長度的修改。否則,我們可能就會得到錯誤的長度結果。這就是多線程編程模式面臨的共享資源的并發訪問控制問題。

多線程并發訪問Redis

并發訪問控制一直是多線程開發中的一個難點問題,如果沒有精細的設計,比如說,只是簡單地采用一個粗粒度互斥鎖,就會出現不理想的結果:即使增加了線程,大部分線程也在等待獲取訪問共享資源的互斥鎖,并行變串行,系統吞吐率并沒有隨著線程的增加而增加。

而且,采用多線程開發一般會引入同步原語來保護共享資源的并發訪問,這也會降低系統代碼的易調試性和可維護性。為了避免這些問題,Redis 直接采用了單線程模式。

講到這里,你應該已經明白了“Redis 為什么用單線程”,那么,接下來,我們就來看看,為什么單線程 Redis 能獲得高性能。

單線程 Redis 為什么那么快?

通常來說,單線程的處理能力要比多線程差很多,但是 Redis 卻能使用單線程模型達到每秒數十萬級別的處理能力,這是為什么呢?其實,這是 Redis 多方面設計選擇的一個綜合結果。

一方面,Redis 的大部分操作在內存上完成,再加上它采用了高效的數據結構,例如哈希表和跳表,這是它實現高性能的一個重要原因。另一方面,就是 Redis 采用了多路復用機制,使其在網絡 IO 操作中能并發處理大量的客戶端請求,實現高吞吐率。接下來,我們就重點學習下多路復用機制。

首先,我們要弄明白網絡操作的基本 IO 模型和潛在的阻塞點。畢竟,Redis 采用單線程進行 IO,如果線程被阻塞了,就無法進行多路復用了。

基本 IO 模型與阻塞點

阻塞模式

其實下面所說的Socket 網絡模型在早期的時候是沒有非阻塞設置的,因此會造成一直等待,也就阻塞了。

這也就是我們所說的BIO網絡模型,關于BIO這個最基本的IO模型,具體是怎么阻塞的想必大家都比較清楚,這里不再過多解釋,本號其他文章有關于io模型的介紹。

非阻塞模式

Socket 網絡模型的非阻塞模式設置,主要體現在三個關鍵的函數調用上,如果想要使用 socket 非阻塞模式,就必須要了解這三個函數的調用返回類型和設置模式。接下來,我們就重點學習下它們。

在 socket 模型中,不同操作調用后會返回不同的套接字類型。socket() 方法會返回主動套接字,然后調用 listen() 方法,將主動套接字轉化為監聽套接字,此時,可以監聽來自客戶端的連接請求。最后,調用 accept() 方法接收到達的客戶端連接,并返回已連接套接字。

Redis套接字類型與非阻塞設置

針對監聽套接字,我們可以設置非阻塞模式:當 Redis 調用 accept() 但一直未有連接請求到達時,Redis 線程可以返回處理其他操作,而不用一直等待。但是,你要注意的是,調用 accept() 時,已經存在監聽套接字了。

雖然 Redis 線程可以不用繼續等待,但是總得有機制繼續在監聽套接字上等待后續連接請求,并在有請求時通知 Redis。

類似的,我們也可以針對已連接套接字設置非阻塞模式:Redis 調用 recv() 后,如果已連接套接字上一直沒有數據到達,Redis 線程同樣可以返回處理其他操作。我們也需要有機制繼續監聽該已連接套接字,并在有數據達到時通知 Redis。

這樣才能保證 Redis 線程,既不會像基本 IO 模型中一直在阻塞點等待,也不會導致 Redis 無法處理實際到達的連接請求或數據。

到此,linux 中的 IO 多路復用機制就要登場了。

基于多路復用的高性能 I/O 模型

Linux 中的 IO 多路復用機制是指一個線程處理多個 IO 流,就是我們經常聽到的 select/epoll 機制。簡單來說,在 Redis 只運行單線程的情況下,該機制允許內核中,同時存在多個監聽套接字和已連接套接字。內核會一直監聽這些套接字上的連接請求或數據請求。一旦有請求到達,就會交給 Redis 線程處理,這就實現了一個 Redis 線程處理多個 IO 流的效果。

下圖就是基于多路復用的 Redis IO 模型。圖中的多個 FD 就是剛才所說的多個套接字。Redis 網絡框架調用 epoll 機制,讓內核監聽這些套接字。此時,Redis 線程不會阻塞在某一個特定的監聽或已連接套接字上,也就是說,不會阻塞在某一個特定的客戶端請求處理上。正因為此,Redis 可以同時和多個客戶端連接并處理請求,從而提升并發性。

基于多路復用的Redis高性能IO模型

為了在請求到達時能通知到 Redis 線程,select/epoll 提供了基于事件的回調機制,即針對不同事件的發生,調用相應的處理函數。

那么,回調機制是怎么工作的呢?其實,select/epoll 一旦監測到 FD 上有請求到達時,就會觸發相應的事件。

這些事件會被存放在一個事件隊列中,Redis 單線程會不斷地處理這個事件隊列。這種方法使得 Redis 不必持續輪詢是否有請求發生,有效地減少了對 CPU 資源的浪費。同時,Redis 在處理事件隊列中的事件時,會觸發相應的處理函數,從而實現了基于事件的回調機制。由于 Redis 不斷地處理事件隊列,因此能夠迅速響應客戶端請求,提高了 Redis 的響應性能。

為了更好地理解,我以連接請求和讀數據請求為例,進一步解釋這個過程。

這兩個請求對應著 Accept 事件和 Read 事件,Redis 分別注冊了 accept 和 get 回調函數來處理這兩類事件。當 Linux 內核檢測到連接請求或讀取數據請求時,就會觸發 Accept 事件和 Read 事件,此時內核會回調 Redis 的相應 accept 和 get 函數來處理這些事件。

這就好比病人前往醫院就醫。在醫生實際進行診斷之前,每位病人(類似于請求)都需要經歷分診、測量體溫、填寫登記表等過程。如果所有這些工作都由醫生親自完成,那醫生的效率將會很低。因此,醫院通常設置了分診臺,分診臺會專門處理這些在診斷之前的任務(類似于 Linux 內核監聽請求),然后再將病人交給醫生進行實際診斷。這種方式,即使只有一個醫生(相當于 Redis 單線程),效率也能夠顯著提高。

需要注意的是,多路復用機制是適用于各種操作系統的,即使你的應用在不同操作系統上運行,多路復用機制依然有效。這是因為多路復用機制的具體實現方式有多種,包括基于 Linux 系統的 select 和 epoll 實現、基于 FreeBSD 的 kqueue 實現,以及基于 Solaris 的 evport 實現,因此你可以根據 Redis 運行的實際操作系統,選擇合適的多路復用實現方式。

小結

在前面的學習中,我們重點探討了 Redis 線程背后的三個關鍵問題,即“Redis是否真的只使用單線程?”、“為什么堅持使用單線程?”以及“為何Redis的單線程如此高效?”

現在,我們已經理解,Redis的單線程指的是它采用單一線程來處理網絡I/O和數據讀寫操作,而采用單線程的核心原因之一是為了避免多線程開發中的復雜并發控制問題。Redis的單線程性能卓越,與其采用的多路復用I/O模型密切相關,因為這有助于規避accept()和send()/recv()等潛在的網絡I/O操作阻塞問題。

通過深入理解這些問題,您已經走在了許多人的前沿。如果您的朋友或同事還對這些問題感到困惑,不妨與他們分享這些見解,幫助他們消除疑慮。

此外,我來透露一下,您可能已經注意到,于2020年5月,Redis 6.0發布了其穩定版本,其中引入了多線程模型。那么,這個多線程模型與我們在本課程中討論的I/O模型是否有關系?它是否會引入復雜的并發控制問題?又是否將如何提升Redis 6.0的性能表現?關于這些問題,我將在接下來的課程中為您詳細介紹。

分享到:
標簽:Redis
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定