首頁  >  文章  >  資料庫  >  Redis使用單線程為什麼還這麼快

Redis使用單線程為什麼還這麼快

PHPz
PHPz轉載
2023-05-26 09:56:061450瀏覽

    Redis為什麼要用單線程?

    多執行緒的開銷

    如果沒有良好的系統設計,採用多執行緒通常會導致如右圖所示的結果(注意縱座標)。剛開始增加執行緒數時,系統吞吐率會增加,再進一步增加執行緒時,系統吞吐率就成長遲緩了,甚至還會出現下降的情況。

    Redis使用單線程為什麼還這麼快

    關鍵瓶頸在於: 系統中通常會存在會被多執行緒同時存取的共享資源,為了確保共享資源的正確性,就需要有額外的機制保證線程安全性,例如加鎖,這會帶來額外的開銷。

    例如拿最常用的List類型來舉例吧,假設Redis採用多執行緒設計,有兩個執行緒A和B分別對List LPUSHLPUSH操作,為了使得每次執行都是相同的結果,即【B執行緒取出A執行緒放入的資料】就需要讓這兩個過程串列執行。這就是多執行緒程式設計模式面臨的共享資源的並發存取控制問題。

    Redis使用單線程為什麼還這麼快

    並發存取控制一直是多執行緒開發中的一個困難問題:如果只是簡單地採用一個互斥鎖,就會出現即使增加了線程,大部分線程也在等待取得互斥鎖,並行變串行,系統吞吐率並沒有隨著執行緒的增加而增加。

    同時加入並發存取控制後也會降低系統程式碼的可讀性和可維護性,所以Redis乾脆直接採用了單執行緒模式。

    Redis使用單執行緒為什麼還這麼快?

    之所以使用單執行緒是Redis設計者多面向測量的結果。

    • Redis的大部分操作在記憶體上完成

    • #採用了高效的資料結構,例如哈希表和跳表

    • 採用了多路復用機制,使其在網路IO操作中能並發處理大量的客戶端請求,實現高吞吐率

    既然Redis使用單執行緒進行IO,如果執行緒被阻塞了就無法進行多路復用了,所以不難想像,Redis肯定還針對網路和IO操作的潛在阻塞點進行了設計。

    網路與IO操作的潛在阻塞點

    在網路通訊裡,伺服器為了處理一個Get請求,需要監聽客戶端請求(bind/listen),和客戶端建立連線(accept),從socket中讀取請求(recv),解析客戶端發送請求(parse),最後給客戶端傳回結果(send)。

    最基本的一種單執行緒實作是依序執行上面的操作。

    Redis使用單線程為什麼還這麼快

    上面標紅的accept和recv操作都是潛在的阻塞點:

    • 當Redis監聽到有連接請求,但卻一直無法成功建立起連線時,就會阻塞在accept()函數這裡,其他客戶端此時也無法和Redis建立連線

    • 當Redis透過recv()從一個客戶端讀取資料時,如果資料一直沒有到達,也會一直阻塞

    ##基於多路復用的高效能IO模型

    為了解決IO中的阻塞問題,Redis採用了Linux的IO多路復用機制,該機制允許核心中,同時存在多個監聽套接字和已連接套接字(

    select/epoll)。

    核心會一直監聽這些套接字上的連線或資料請求。 Redis會處理到達的請求,從而實現了一個執行緒處理多個IO流的效果。

    Redis使用單線程為什麼還這麼快

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

    回呼機制

    select/epoll一旦監測到FD上有請求到達時,就會觸發對應的事件被放進一個佇列裡,Redis執行緒對該事件佇列不斷進行處理,所以就實現了基於事件的回調。

    例如,Redis會對Accept和Read事件註冊

    acceptget回呼函數。當Linux核心監聽到有連接請求或讀取資料請求時,就會觸發Accept事件和Read事件,此時​​,核心就會回呼Redis對應的acceptget函數進行處理。

    Redis的效能瓶頸點

    經過上面的分析,雖然透過多工機制可以同時監聽多個客戶端的請求,但Redis仍然有一些效能瓶頸點,這也是我們平時編程需要極力避免的情況。

    1. 耗時操作

    如果任何一個請求在Redis中耗時較長,就會對整個伺服器的效能產生影響。後面的請求都要等前面這個耗時請求處理完成,自己才能被處理到。

    這一點需要我們在設計業務場景時去規避;Redis的lazy-free機制也把釋放記憶體的耗時操作放在了非同步執行緒中去執行了。

    2. 高並發場景

    並發量非常大時,單執行緒讀寫客戶端IO資料存在效能瓶頸,雖然採用IO多路復用機制,但還是只能單執行緒依序讀取客戶端的數據,無法利用到CPU多核心。

    Redis在6.0可以利用CPU多核心多執行緒讀寫客戶端數據,但只是針對客戶端的讀寫是並行的,每個命令的真正操作還是單線程。

    其他Redis相關的有趣問題

    藉此機會也提幾個和redis相關的有趣的問題。

    Redis使用單線程為什麼還這麼快

    • 為什麼要用Redis,直接存取記憶體不好嗎?

    這一條其實並沒有很明確的界定,對於一些不常變動的數據,可以直接放到記憶體裡,不一定要放到Redis裡,可以放到記憶體裡。在更新資料時可能存在一致性問題,即可能只有某一台伺服器上的資料被修改,因此資料僅在本機記憶體中存在。存取Redis伺服器可以解決一致性問題,用Redis的話。

    • 資料太多內存放不下怎麼辦?例如我要快取100G的數據,怎麼辦?

    這裡也要打一個廣告Tair是淘寶開源的分散式KV快取系統,它從Redis繼承了豐富的操作,理論上總資料量無限制,針對可用性、可擴充性、可靠性也進行了升級,有興趣的夥伴們可以了解一下~

    以上是Redis使用單線程為什麼還這麼快的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除