欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

聊聊單線程的Redis為何會快到飛起

 更新時間:2022年02月24日 14:41:06   作者:小王曾是少年  
Redis想必大家都或多或少聽過吧,我們在工作學(xué)習(xí)中通常用它來作為緩存使用,既然是作為緩存,大家的第一反應(yīng)肯定是:這家伙很快

實(shí)際上它確實(shí)也很快 : ),但Redis底層卻是單線程的!有同學(xué)可能就要有疑問了,為什么單線程的Redis卻能夠快到飛起?

別急,我盡量用通俗易懂的語言來給各位說道說道~~

Redis是單線程,主要是指Redis的網(wǎng)絡(luò)IO和讀寫是由一個線程來完成的,但Redis的其他功能,比如持久化、異步刪除、集群數(shù)據(jù)同步等,其實(shí)是由額外的線程執(zhí)行的。這不是本文討論的重點(diǎn),有個印象即可

Redis為什么用單線程?

多線程的開銷

通常情況下,在采用多線程后,如果沒有良好的系統(tǒng)設(shè)計,其實(shí)是右圖所展示的那樣(注意縱坐標(biāo))。剛開始增加線程數(shù)時,系統(tǒng)吞吐率會增加,再進(jìn)一步增加線程時,系統(tǒng)吞吐率就增長遲緩了,甚至還會出現(xiàn)下降的情況。

關(guān)鍵瓶頸在于: 系統(tǒng)中通常會存在會被多線程同時訪問的共享資源,為了保證共享資源的正確性,就需要有額外的機(jī)制保證線程安全性,例如加鎖,這會帶來額外的開銷。

比如拿最常用的List類型來舉例吧,假設(shè)Redis采用多線程設(shè)計,有兩個線程A和B分別對ListLPUSHLPUSH操作,為了使得每次執(zhí)行都是相同的結(jié)果,即【B線程取出A線程放入的數(shù)據(jù)】就需要讓這兩個過程串行執(zhí)行。這就是多線程編程模式面臨的共享資源的并發(fā)訪問控制問題。

并發(fā)訪問控制一直是多線程開發(fā)中的一個難點(diǎn)問題:如果只是簡單地采用一個互斥鎖,就會出現(xiàn)即使增加了線程,大部分線程也在等待獲取互斥鎖,并行變串行,系統(tǒng)吞吐率并沒有隨著線程的增加而增加。

同時加入并發(fā)訪問控制后也會降低系統(tǒng)代碼的可讀性和可維護(hù)性,所以Redis干脆直接采用了單線程模式。

Redis使用單線程為什么還這么快?

之所以使用單線程是Redis設(shè)計者多方面衡量的結(jié)果。

  • Redis的大部分操作在內(nèi)存上完成
  • 采用了高效的數(shù)據(jù)結(jié)構(gòu),例如哈希表和跳表
  • 采用了多路復(fù)用機(jī)制,使其在網(wǎng)絡(luò)IO操作中能并發(fā)處理大量的客戶端請求,實(shí)現(xiàn)高吞吐率

既然Redis使用單線程進(jìn)行IO,如果線程被阻塞了就無法進(jìn)行多路復(fù)用了,所以不難想象,Redis肯定還針對網(wǎng)絡(luò)和IO操作的潛在阻塞點(diǎn)進(jìn)行了設(shè)計。

網(wǎng)絡(luò)與IO操作的潛在阻塞點(diǎn)

在網(wǎng)絡(luò)通信里,服務(wù)器為了處理一個Get請求,需要監(jiān)聽客戶端請求(bind/listen),和客戶端建立連接(accept),從socket中讀取請求(recv),解析客戶端發(fā)送請求(parse),最后給客戶端返回結(jié)果(send)。

最基本的一種單線程實(shí)現(xiàn)是依次執(zhí)行上面的操作。

上面標(biāo)紅的accept和recv操作都是潛在的阻塞點(diǎn):

  • 當(dāng)Redis監(jiān)聽到有連接請求,但卻一直不能成功建立起連接時,就會阻塞在accept()函數(shù)這里,其他客戶端此時也無法和Redis建立連接
  • 當(dāng)Redis通過recv()從一個客戶端讀取數(shù)據(jù)時,如果數(shù)據(jù)一直沒有到達(dá),也會一直阻塞

基于多路復(fù)用的高性能IO模型

為了解決IO中的阻塞問題,Redis采用了Linux的IO多路復(fù)用機(jī)制,該機(jī)制允許內(nèi)核中,同時存在多個監(jiān)聽套接字和已連接套接字(select/epoll)。

內(nèi)核會一直監(jiān)聽這些套接字上的連接或數(shù)據(jù)請求。一旦有請求到達(dá),就會交給Redis處理,這就實(shí)現(xiàn)了一個Redis線程處理多個IO流的效果。

此時,Redis線程就不會阻塞在某一個特定的客戶端請求處理上,所以它可以同時和多個客戶端連接并處理請求。

回調(diào)機(jī)制

select/epoll一旦監(jiān)測到FD上有請求到達(dá)時,就會觸發(fā)相應(yīng)的事件被放進(jìn)一個隊列里,Redis線程對該事件隊列不斷進(jìn)行處理,所以就實(shí)現(xiàn)了基于事件的回調(diào)。

例如,Redis會對Accept和Read事件注冊acceptget回調(diào)函數(shù)。當(dāng)Linux內(nèi)核監(jiān)聽到有連接請求或讀數(shù)據(jù)請求時,就會觸發(fā)Accept事件和Read事件,此時,內(nèi)核就會回調(diào)Redis相應(yīng)的acceptget函數(shù)進(jìn)行處理。

Redis的性能瓶頸點(diǎn)

經(jīng)過上面的分析,雖然通過多路復(fù)用機(jī)制可以同時監(jiān)聽多個客戶端的請求,但Redis仍然有一些性能瓶頸點(diǎn),這也是我們平時編程需要極力避免的情況。

1. 耗時操作

任意一個請求在Redis中一旦耗時較久,都會影響整個server的性能。后面的請求都要等前面這個耗時請求處理完成,自己才能被處理到。

這一點(diǎn)需要我們在設(shè)計業(yè)務(wù)場景時去規(guī)避;Redis的lazy-free機(jī)制也把釋放內(nèi)存的耗時操作放在了異步線程中去執(zhí)行了。

2. 高并發(fā)場景

并發(fā)量非常大時,單線程讀寫客戶端IO數(shù)據(jù)存在性能瓶頸,雖然采用IO多路復(fù)用機(jī)制,但還是只能單線程依次讀取客戶端的數(shù)據(jù),無法利用到CPU多核。

Redis在6.0可以利用CPU多核多線程讀寫客戶端數(shù)據(jù),但只是針對客戶端的讀寫是并行的,每個命令的真正操作還是單線程。

其他Redis相關(guān)的有趣問題

借此機(jī)會也提幾個和redis相關(guān)的有意思的問題。

  • 為什么要用Redis,直接訪問內(nèi)存不好嗎?

這一條其實(shí)并沒有很明確的界定,對于一些不經(jīng)常變動的數(shù)據(jù),可以直接放到內(nèi)存里,不一定要放到Redis里,可以放到內(nèi)存里。一致性問題:如果一個數(shù)據(jù)被修改了,數(shù)據(jù)在本地內(nèi)存里的話,可能只有一臺服務(wù)器上的數(shù)據(jù)被修改了。如果用Redis里面的話,我們訪問Redis服務(wù)器,可以解決一致性問題。

  • 數(shù)據(jù)太多內(nèi)存放不下怎么辦?比如我要緩存100G的數(shù)據(jù),怎么辦?

這里也要打一個廣告Tair是淘寶開源的分布式KV緩存系統(tǒng),它從Redis繼承了豐富的操作,理論上總數(shù)據(jù)量無限制,針對可用性、可擴(kuò)展性、可靠性也進(jìn)行了升級,感興趣的小伙伴們可以了解一下~

到此這篇關(guān)于聊聊單線程的Redis為何會快到飛起的文章就介紹到這了,更多相關(guān)Java Redis內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot + devtools(熱部署)實(shí)例教程

    springboot + devtools(熱部署)實(shí)例教程

    devtools是boot的一個熱部署工具,當(dāng)我們修改了classpath下的文件(包括類文件、屬性文件、頁面等)時,會重新啟動應(yīng)用。本文通過實(shí)例給大家介紹springboot+devtools熱部署,感興趣的朋友一起看看吧
    2017-04-04
  • 在SpringBoot中配置日志級別和輸出格式的教程詳解

    在SpringBoot中配置日志級別和輸出格式的教程詳解

    在開發(fā)一個應(yīng)用程序時,日志記錄是非常重要的一環(huán),SpringBoot提供了多種日志輸出方式和配置選項,本文將介紹如何在SpringBoot應(yīng)用程序中配置日志級別和輸出格式,需要的朋友可以參考下
    2023-06-06
  • 詳解Java動態(tài)字節(jié)碼技術(shù)

    詳解Java動態(tài)字節(jié)碼技術(shù)

    Java字節(jié)碼增強(qiáng)指的是在Java字節(jié)碼生成之后,對其進(jìn)行修改,增強(qiáng)其功能,可減少冗余代碼,提高性能等。本文將詳細(xì)介紹Java動態(tài)字節(jié)碼技術(shù)。
    2021-05-05
  • Hibernate雙向多對多映射關(guān)系配置代碼實(shí)例

    Hibernate雙向多對多映射關(guān)系配置代碼實(shí)例

    這篇文章主要介紹了Hibernate雙向多對多映射關(guān)系配置代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-10-10
  • Java實(shí)現(xiàn)五子棋單機(jī)版

    Java實(shí)現(xiàn)五子棋單機(jī)版

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)五子棋單機(jī)版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • MyBatis中#和$的區(qū)別小結(jié)

    MyBatis中#和$的區(qū)別小結(jié)

    ${} 和 #{} 都是 MyBatis 中用來替換參數(shù)的,它們都可以將用戶傳遞過來的參數(shù),替換到 MyBatis 最終生成的 SQL 中,但它們區(qū)別卻是很大的,接下來我們一起來看
    2023-09-09
  • SpringMVC異步處理操作(Callable和DeferredResult)

    SpringMVC異步處理操作(Callable和DeferredResult)

    這篇文章主要介紹了SpringMVC異步處理操作(Callable和DeferredResult),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • Java游戲服務(wù)器系列之Netty相關(guān)知識總結(jié)

    Java游戲服務(wù)器系列之Netty相關(guān)知識總結(jié)

    今天帶大家來學(xué)習(xí)Java游戲服務(wù)器的相關(guān)知識,文中對Netty作了非常詳細(xì)的介紹,對正在學(xué)習(xí)java的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-05-05
  • Java并發(fā)之Semaphore工具類r的全面解析

    Java并發(fā)之Semaphore工具類r的全面解析

    Semaphore 是 java.util.concurrent中非常有用的并發(fā)編程工具類,它通常被用于限制對某個資源或資源池的并發(fā)訪問數(shù)量,下面我們就來深入了解一下Semaphore的具體使用吧
    2024-02-02
  • Spring?Data?JPA查詢方式及方法名查詢規(guī)則介紹

    Spring?Data?JPA查詢方式及方法名查詢規(guī)則介紹

    這篇文章主要介紹了Spring?Data?JPA查詢方式及方法名查詢規(guī)則,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11

最新評論