天天看點

從處理請求的核心流程談一談Redis到底是單線程還是多線程從處理請求的核心流程談一談Redis到底是單線程還是多線程

從處理請求的核心流程談一談Redis到底是單線程還是多線程

随着Redis版本的不斷更新,Redis在處理請求方面也在不斷的優化,由單線程的概念逐漸引入了多線程的概念。那麼Redis到底是單線程還是多線程呢?

在Redis 4.0版本之前,Redis完全是單線程,沒有引入多線程這個概念。因為Redis是完全是基于記憶體操作的,通常情況下CPU不會是redis的瓶頸,于是就采用單線程模型處理請求,如果使用多線程的話,反而會變得更複雜,同時還涉及到了多線程的上下文切換,帶來了額外的性能消耗。是以,Redis讀寫速度快,除了基于記憶體操作、優化了底層資料結構以外,單線程處理請求也是速度快的原因之一!

到了Redis 4.0版本時,才引入了多線程的概念,但是核心流程還是單線程的,這裡核心流程指的是Redis正常處理用戶端發送過來的請求,即接收指令 ==> 解析指令 ==> 執行指令 ==> 傳回結果。而所謂的多線程并沒有涉及到核心流程的處理,隻是用來做一些背景處理,比如删除對象等。

在Redis 6.0的時候,又一次加強了多線程的概念,這個版本多線程會涉及到了核心流程。Redis 6.0中多線程主要用于網絡的I/O階段,也就是接收指令和寫回結果階段,而執行指令階段還是單線程串行執行。

是以,說Redis是單線的這個說法從執行指令階段來看,是沒毛病的!

之是以Redis單線程也很快,是因為:

  1. 完全是基于記憶體操作的
  2. 單線程避免了多線程上下文切換帶來的額外消耗
  3. Redis對底層的資料結構做了優化
  4. I/O多路複用

接下來我們重點來講講Redis的I/O多路複用技術

我們要知道Redis伺服器是一個事件驅動程式,是需要靠事件觸發伺服器對請求的處理操作。伺服器需要處理的事件分為了兩類:

  • 檔案事件

    Redis伺服器是通過套接字與用戶端或者其它Redis伺服器進行連接配接的,而檔案事件實際上就是指的Redis伺服器與用戶端或者其它伺服器之間的互相通信的一種抽象概述。伺服器和用戶端之間通信會産生相應的檔案事件,伺服器通過監聽這些産生的檔案事件來完成一系列網絡通信操作。你可以了解為用戶端發送請求給伺服器,然後伺服器監聽到請求之後,處理請求并傳回。

  • 時間事件

    顧名思義,Redis伺服器的一些操作需要在特定的時間點才會觸發執行,而時間事件就是對這種定時操作的一種抽象概述。

平時我們操作最多的就是檔案事件,接下來我們主要來說說檔案事件

**Redis基于Reactor模式(事件驅動模型)開發了自己的檔案事件處理器。**由于檔案事件處理器是單線程執行的,也就是我們之前說的執行指令階段,是以說Redis是單線程模型是沒問題的。

那麼既然是單線程模型,那Redis是如何監聽大量的用戶端連接配接的呢?

Redis使用I/O多路複用技術實作了監聽用戶端大量連接配接,或者說監聽大量的套接字。當被監聽的套接字準備好執行連接配接應答、讀取、寫入、關閉等操作時,與操作相對應的檔案事件也就産生了,Redis伺服器會根據套接字目前階段執行的操作來為套接字關聯不同的檔案事件處理器,來處理這些事件。雖然檔案事件處理器以單線程方式運作,但通過使用IO多路複用技術來監聽多個套接字,檔案事件處理器既實作了高性能的網絡通信模型,又可以很好的與redis伺服器中其它同樣以單線程方式運作的子產品進行對接。

而這個檔案事件處理器由4個部分組成:

  • 套接字(用戶端與伺服器之間的連接配接)
  • I/O多路複用程式(監聽大量用戶端連接配接的關鍵技術)
  • 檔案事件分派器(socket映射到對應的檔案處理器)
  • 事件處理器(連接配接、請求、回複、關閉等處理器)
從處理請求的核心流程談一談Redis到底是單線程還是多線程從處理請求的核心流程談一談Redis到底是單線程還是多線程

其中I/O多路複用程式負責監聽多個套接字,并向檔案事件分派器傳送産生了事件的套接字。盡管多個檔案事件可能并發的發生,但是I/O多路複用程式會将所有産生事件的套接字都放到一個隊列裡面,然後通過這個隊列,有序、同步、每次一個的将套接字發送給檔案事件分派器。當一個套接字被對應所關聯的事件處理器執行完成之後,I/O多路複用程式才會繼續向檔案事件分派器傳送下一個套接字。

從處理請求的核心流程談一談Redis到底是單線程還是多線程從處理請求的核心流程談一談Redis到底是單線程還是多線程

以上就是Redis單線程且高效的原因!

我們說在Redis 4.0版本的時候才引入了多線程的概念,但是多線程不涉及核心流程,直到Redis 6.0版本再次了多線程的概念,涉及到了核心流程。Redis 6.0版本加入多線程 I/O 之後,處理指令的核心流程如下:

  1. 連接配接應答

    當伺服器監聽到套接字連接配接時,主線程将該用戶端連接配接放到全局等待讀隊列

  2. 讀取資料:

    1)主線程将等待讀隊列的用戶端連接配接通過輪詢排程算法配置設定給 I/O 線程處理;

    2)同時主線程也會自己負責處理一個用戶端連接配接的讀事件;

    3)當主線程處理完該連接配接的讀事件後,會自旋等待所有 I/O 線程處理完畢(這裡就有點像Java中基于AQS架構實作的并發工具類CountDownLatch)

  3. 指令執行:

    主線程按照事件被加入全局等待讀隊列的順序(保證了執行順序是正确的),串行執行用戶端指令,然後将用戶端連接配接放到全局等待寫隊列

  4. 寫回結果:

    跟等待讀隊列處理類似,主線程将等待寫隊列的用戶端連接配接使用輪詢排程算法配置設定給 I/O 線程處理,同時自己也會處理一個,當主線程處理完畢後,會自旋等待所有 I/O 線程處理完畢,最後清空隊列。

從處理請求的核心流程談一談Redis到底是單線程還是多線程從處理請求的核心流程談一談Redis到底是單線程還是多線程

我們可以看出來,Redis 6.0版本的多線程雖然涉及到了核心流程,但是指令執行階段還是單線程串行執行的,隻是在讀取資料和寫回結果這兩個階段采用了多線程I/O處理。說白了就是維護了兩個隊列,一個全局等待讀隊列,一個全局等待寫隊列,使得單線程轉專注于指令的執行,由多線程處理讀寫操作,進而提高了I/O讀寫性能。

以上就是為什麼說Redis又是多線程的原因!

繼續閱讀