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