《【面試突擊】— Redis篇》-- Redis的執行緒模型瞭解嗎?為啥單執行緒效率還這麼高?
能堅持別人不能堅持的,才能擁有別人未曾擁有的。
關注程式設計大道
公眾號,讓我們一同堅持心中所想,一起成長!!
《【面試突擊】— Redis篇》-- Redis的執行緒模型瞭解嗎?為啥單執行緒效率還這麼高?
在這個系列裡,我會整理一些面試題與大家分享,幫助年後和我一樣想要在金三銀四準備跳槽的同學。
我們一起鞏固、突擊面試官常問的一些面試題,加油!!
1、面試題
Redis和Memcached有什麼區別?
Redis的執行緒模型是什麼?
為什麼Redis是單執行緒的但是還可以支撐高併發?
2、面試官心理分析
問這個的時候就是問你Redis的原理了,看你是不是思考過,研究過。Redis最基本的一個內部原理和特點,就是Redis實際上是個單執行緒工作模型。你要是連這個都不知道,那後面在使用Redis的時候,如果出了問題豈不是什麼都不知道,無從下手?
還有可能面試官會問問你Redis和Memcached的區別。不過說實話,近幾年,面試官都不太喜歡這麼問了。因為memcached是早些年各大網際網路公司常用的快取方案,但是現在近幾年基本都是Redis,沒什麼公司用memcached了。
3、溫馨提醒
如果你要是現在還不知道redis和memcached是啥,那你趕緊百度一下redis入門和memcahced入門,找兩篇部落格教程什麼的簡單啟動一下,然後試一下幾個簡單操作,先感受一下,跟這個部落格跟著教程做個demo程式,1小時以內就搞定了,就能初步瞭解和入門了。然後接著回來繼續看。
另外一個友情提示,要弄明白redis的執行緒模型的話,前提是你需要了解socket網路相關的基本知識。如果你socket都不瞭解的話那我覺得你java沒學好吧。初學者都該學習java的socket網路通訊相關知識的。
4、面試題剖析
(1)redis和memcached有啥區別
這個問題,其實可以比較出很多區別,但是這裡還是採取redis作者給出的幾個來進行比較吧,畢竟面試不是背部落格,不是說的越多越好,你只要答出來關鍵的那幾點其實就可以了。但是並不是說除了這裡列出來的幾個你就不需要知道別的了,你可以說:要說二者的區別其實還挺多的,這裡我就挑幾個最典型的說吧。
1)Redis支援伺服器端的資料操作:Redis相比Memcached來說,擁有更多的資料結構和並支援更豐富的資料操作,通常在Memcached裡,你需要將資料拿到客戶端來進行類似的修改再set回去。這大大增加了網路IO的次數和資料體積。在Redis中,這些複雜的操作通常和一般的GET/SET一樣高效。所以,如果需要快取能夠支援更復雜的結構和操作,那麼Redis會是不錯的選擇。
2)記憶體使用效率對比:使用簡單的key-value儲存的話,Memcached的記憶體利用率更高,而如果Redis採用hash結構來做key-value儲存,由於其組合式的壓縮,其記憶體利用率會高於Memcached。
3)效能對比:由於Redis只使用單核,而Memcached可以使用多核,所以平均每一個核上Redis在儲存小資料時比Memcached效能更高。而在100k以上的資料中,Memcached效能要高於Redis,雖然Redis最近也在儲存大資料的效能上進行優化,但是比起Memcached,還是稍有遜色。
4)叢集模式:memcached沒有原生的叢集模式,需要依靠客戶端來實現往叢集中分片寫入資料;但是redis目前是原生支援cluster模式的,redis官方就是支援redis cluster叢集模式的,比memcached來說要更好。
其實第2、3個說不說都可以,關鍵是1和4。
(2)redis的執行緒模型
問這個原理性的問題,其實你可以結合著圖來給面試官講這個問題,邊畫圖邊講最有說服力,面試官在心裡會給你默默地豎起大拇指。
1)檔案事件處理器
Redis基於Reactor模式開發了網路事件處理器,這個處理器叫做檔案事件處理器 file event handler。這個檔案事件處理器,它是單執行緒的,所以 Redis 才叫做單執行緒的模型,它採用IO多路複用機制來同時監聽多個Socket,根據Socket上的事件型別來選擇對應的事件處理器來處理這個事件。
如果被監聽的 Socket 準備好執行accept、read、write、close等操作的時候,跟操作對應的檔案事件就會產生,這個時候檔案事件處理器就會呼叫之前關聯好的事件處理器來處理這個事件。
檔案事件處理器是單執行緒模式執行的,但是通過IO多路複用機制監聽多個Socket,可以實現高效能的網路通訊模型,又可以跟內部其他單執行緒的模組進行對接,保證了 Redis 內部的執行緒模型的簡單性。
檔案事件處理器的結構包含4個部分:多個Socket、IO多路複用程式、檔案事件分派器以及事件處理器(命令請求處理器、命令回覆處理器、連線應答處理器等)。
多個 Socket 可能併發的產生不同的操作,每個操作對應不同的檔案事件,但是IO多路複用程式會監聽多個 Socket,會將 Socket 放入一個佇列中排隊,每次從佇列中取出一個 Socket 給事件分派器,事件分派器把 Socket 給對應的事件處理器。
然後一個 Socket 的事件處理完之後,IO多路複用程式才會將佇列中的下一個 Socket 給事件分派器。檔案事件分派器會根據每個 Socket 當前產生的事件,來選擇對應的事件處理器來處理。
2)檔案事件
當 Socket 變得可讀時(比如客戶端對redis執行write操作,或者close操作),或者有新的可以應答的 Sccket 出現時(客戶端對redis執行connect操作),Socket就會產生一個AE_READABLE事件。
當 Socket 變得可寫的時候(客戶端對redis執行read操作),Socket 會產生一個AE_WRITABLE事件。
IO 多路複用程式可以同時監聽 AE_REABLE 和 AE_WRITABLE 兩種事件,如果一個Socket同時產生了這兩種事件,那麼檔案事件分派器優先處理 AE_READABLE 事件,然後才是 AE_WRITABLE 事件。
3)檔案事件處理器
如果是客戶端要連線redis,那麼會為 Socket 關聯連線應答處理器。
如果是客戶端要寫資料到redis,那麼會為 Socket 關聯命令請求處理器。
如果是客戶端要從redis讀資料,那麼會為 Socket 關聯命令回覆處理器。
4)客戶端與redis通訊的一次流程
在 Redis 啟動初始化的時候,Redis 會將連線應答處理器跟 AE_READABLE 事件關聯起來,接著如果一個客戶端跟Redis發起連線,此時會產生一個 AE_READABLE 事件,然後由連線應答處理器來處理跟客戶端建立連線,建立客戶端對應的 Socket,同時將這個 Socket 的 AE_READABLE 事件跟命令請求處理器關聯起來。
當客戶端向Redis發起請求的時候(不管是讀請求還是寫請求,都一樣),首先就會在 Socket 產生一個 AE_READABLE 事件,然後由對應的命令請求處理器來處理。這個命令請求處理器就會從Socket中讀取請求相關資料,然後進行執行和處理。
接著Redis這邊準備好了給客戶端的響應資料之後,就會將Socket的AE_WRITABLE事件跟命令回覆處理器關聯起來,當客戶端這邊準備好讀取響應資料時,就會在 Socket 上產生一個 AE_WRITABLE 事件,會由對應的命令回覆處理器來處理,就是將準備好的響應資料寫入 Socket,供客戶端來讀取。
命令回覆處理器寫完之後,就會刪除這個 Socket 的 AE_WRITABLE 事件和命令回覆處理器的關聯關係。
(3)為啥Redis單執行緒模型也能效率這麼高?
1)純記憶體操作
Redis 將所有資料放在記憶體中,記憶體的響應時長大約為 100 納秒,這是 redis 的 QPS 過萬的重要基礎。
2)核心是基於非阻塞的IO多路複用機制
有了非阻塞 IO 意味著執行緒在讀寫 IO 時可以不必再阻塞了,讀寫可以瞬間完成然後執行緒可以繼續幹別的事了。
redis 需要處理多個 IO 請求,同時把每個請求的結果返回給客戶端。由於 redis 是單執行緒模型,同一時間只能處理一個 IO 事件,於是 redis 需要在合適的時間暫停對某個 IO 事件的處理,轉而去處理另一個 IO 事件,這就需要用到IO多路複用技術了, 就好比一個管理者,能夠管理個socket的IO事件,當選擇了哪個socket,就處理哪個socket上的 IO 事件,其他 IO 事件就暫停處理了。
3)單執行緒反而避免了多執行緒的頻繁上下文切換帶來的效能問題。(百度多執行緒上下文切換)
-
第一,單執行緒可以簡化資料結構和演算法的實現。併發資料結構實現不但困難而且開發測試比較麻
-
第二,單執行緒避免了執行緒切換和競態產生的消耗,對於服務端開發來說,鎖和執行緒切換通常是效能殺手。
-
單執行緒的問題:對於每個命令的執行時間是有要求的。如果某個命令執行過長,會造成其他命令的阻塞,所以 redis 適用於那些需要快速執行的場景。
本系列文章在於面試突擊,不是教程,要是細挖,Redis執行緒模型能講好多,而面試你只需要把這個原理說出來就行了。
該系列文章在於快速突擊,快速拾遺,溫習。