眾所周知,Redis是一個單執行緒架構的NoSQL資料庫,但是是單執行緒模型的Redis為什么性能如此之高?這就是我們接下來要探究學習的內容,
1、Redis的單執行緒架構
1.1、Redis單執行緒簡介
首先要明白,Redis的單執行緒指的是執行命令時的單執行緒,
Redis客戶端與服務端的模型可以簡化成下圖,每次客戶端呼叫都經歷了發送命令、執行命令、回傳結果三個程序,

我們說的單執行緒就是在第二步執行命令,一條命令從從客戶端達到服務端不會立刻被執行,而是會進入一個佇列中等待,每次只會有一條指令被選中執行,

發送命令、回傳結果、命令排隊這些就不是那么簡單了,例如Redis使用了I/O多路復用技術來解決I/O的問題,
1.2、Redis為什么要使用單執行緒
這是官方的解釋:https://redis.io/topics/faq

官方FAQ表示,因為Redis是基于記憶體的操作,CPU成為Redis的瓶頸的情況很少見,Redis的瓶頸最有可能是記憶體的大小或者網路限制,
如果想要最大程度利用CPU,可以在一臺機器上啟動多個Redis實體,
值得一提的,網路上存在這樣的觀點:吐槽官方的解釋有些敷衍,其實就是歷史原因,開發者嫌多執行緒麻煩,后來這個CPU的利用問題就被拋給了使用者,
同時FAQ里還提到了, Redis 4.0 之后開始變成多執行緒,除了主執行緒外,它也有后臺執行緒在處理一些較為緩慢的操作,例如清理臟資料、無用連接的釋放、大 Key 的洗掉等等,
1.3、為什么單執行緒還能這么快
通常來講,單執行緒處理能力要比多執行緒差,那么為什么Redis使用單執行緒模型會達到每秒萬級別的處理能力呢?可以將其歸結為三點:
-
第一:純記憶體訪問,Redis將所有資料放在記憶體中,記憶體的回應時長大約為100納秒,這是Redis達到每秒萬級別訪問的最重要的基礎,
-
第二:非阻塞I/O,Redis使用epoll作為I/O多路復用技術的實作,再加上Redis自身的事件處理模型將epoll中的連接、讀寫、關閉都轉換為事件,不在網路I/O上浪費過多的時間

這里再擴展一下I/O多路復用:
參考知乎上一個高贊的回答來解釋什么是I/O多路復用,假設你是一個老師,讓30個學生解答一道題目,然后檢查學生做的是否正確,你有下面幾個選擇:
-
第一種選擇:按順序逐個檢查,先檢查A,然后是B,之后是C、D,,,這中間如果有一個學生卡主,全班都會被耽誤,這種模式就好比,你用回圈挨個處理socket,根本不具有并發能力,
-
第二種選擇:你創建30個分身,每個分身檢查一個學生的答案是否正確, 這種類似于為每一個用戶創建一個行程或者執行緒處理連接,
-
第三種選擇,你站在講臺上等,誰解答完誰舉手,這時C、D舉手,表示他們解答問題完畢,你下去依次檢查C、D的答案,然后繼續回到講臺上等,此時E、A又舉手,然后去處理E和A,
第一種就是阻塞IO模型,第三種就是I/O復用模型,Linux下的select、poll和epoll就是干這個的,將用戶socket對應的fd注冊進epoll,然后epoll幫你監聽哪些socket上有訊息到達,這樣就避免了大量的無用操作,此時的socket應該采用非阻塞模式,
這樣,整個程序只在呼叫select、poll、epoll這些呼叫的時候才會阻塞,收發客戶訊息是不會阻塞的,整個行程或者執行緒就被充分利用起來,這就是事件驅動,所謂的reactor模式,

- 第三:單執行緒避免了執行緒切換和競態產生的消耗,
我們繼續來看Redis單執行緒卻很快的最后一條原因,在多執行緒開發中,存在執行緒的切換和競爭,這樣一來,是有時間的消耗的,對于需要磁盤I/O的程式來講,磁盤I/O是一個比較耗時的操作,所以對于需要進行磁盤I/O的程式,我們可以使用多執行緒,在某個執行緒進行I/O時,CPU切換到當前程式的其他執行緒執行,以此減少CPU的等待時間,
那么問題來了,Redis的資料存放在記憶體中,將記憶體中的資料讀入CPU時,CPU不是依然需要等待嗎,為什么不能在等待資料從記憶體讀入CPU期間執行其他執行緒,以此提高CPU的使用率呢?這個問題的答案很簡單,記憶體的讀些速度雖然比CPU慢很多,但是也是非常快的,CPU切換執行緒需要花費一定的時間,而多次切換執行緒所花費的時間,可能比直接使用單執行緒執行相同的任務,花費的時間要更多,這是非常不劃算的,
單執行緒也會有一個問題:對于每個命令的執行時間是有要求的,如果某個命令執行過長,會造成其他命令的阻塞,對于Redis這種高性能的服務來說是致命的,所以Redis是面向快速執行場景的資料庫,
2、支持多執行緒的Redis6.0
“Redis不是單執行緒嗎?怎么又支持多執行緒了?”
相信學到了這里,這已經不是一個問題了,
Redis6.0引入了多執行緒的特性,這個多執行緒是在哪里呢?——是對處理網路請求程序采用了多執行緒,
Redis 6.0采用多個IO執行緒來處理網路請求,網路請求的決議可以由其他執行緒完成,然后把決議后的請求交由主執行緒進行實際的記憶體讀寫,提升網路請求處理的并行度,進而提升整體性能,

那么多并發的執行緒安全問題存在嗎?——當然不存在,
Redis 的多 IO 執行緒只是用來處理網路請求的,對于命令的執行,Redis 仍然使用單執行緒來處理,
參考:
【1】:《Redis開發與運維》
【2】:支持多執行緒的Redis6.0來了
【3】:為什么 Redis 選擇單執行緒模型
【4】:Redis 和 I/O 多路復用
【5】:I/O多路復用技術(multiplexing)是什么?
【6】:一文搞懂I/O多路復用及其技術
【7】:Redis為什么是單執行緒的
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/280673.html
標籤:其他
