首頁 >資料庫 >Redis >redis是單線程還是多線程,為什麼

redis是單線程還是多線程,為什麼

青灯夜游
青灯夜游原創
2020-12-18 15:08:2621106瀏覽

Redis4.0之前是單執行緒運行的;Redis4.0後開始支援多執行緒。 Redis4.0之前使用單執行緒的原因:1、單執行緒模式方便開發和調試;2、Redis內部使用了基於epoll的多路復用;3、Redis主要的效能瓶頸是記憶體或網路頻寬。

redis是單線程還是多線程,為什麼

(學習影片分享:redis影片教學

不同版本的Redis是不同的,在Redis4. 0之前,Redis是單執行緒運行的,但單執行緒並不代表效率低,像Nginx、Nodejs也是單執行緒程序,但是它們的效率並不低。

原因是Redis是基於記憶體的,它的瓶頸在於機器的記憶體、網路頻寬,而不是CPU,在CPU還沒達到瓶頸時機器記憶體可能就滿了、或頻寬達到瓶頸了。因此CPU不是主要原因,那麼自然就採用單線程了,況且使用多執行緒比較麻煩。

但是在Redis4.0的時候,已經開始支援多執行緒了,像是後台刪除等功能。

簡單來說,Redis在4.0之前使用單執行緒的模式是因為以下三個原因:

  • 使用單執行緒模式的Redis,其開發和維護更簡單,因為單線程模式方便開發和調試。

  • 即使使用單執行緒模型也能夠並發地處理多客戶端的請求,主要是因為Redis內部使用了基於epoll的多路復用。

  • 對Redis來說,主要的效能瓶頸是記憶體或網路頻寬,而非CPU。

但Redis在4.0以及之後的版本中引入了惰性刪除(也叫非同步刪除),意思是我們可以使用非同步的方式對Redis中的資料進行刪除操作,例如:

  • unlink key:和del key類似,刪除指定的key,若key不存在則key被跳過。但是del會產生阻塞,而unlink指令會在另一個執行緒中回收內存,即它是非阻塞的【http://www.redis.cn/commands/unlink.html】;

  • flushdb async:刪除目前資料庫的所有資料【http://www.redis.cn/commands/flushdb.html】;

  • flushall async:刪除所有資料庫中的數據【http://www.redis.cn/commands/flushall.html】。

這樣處理的好處是不會使Redis的主執行緒卡頓,會把這些操作交給後台執行緒來執行。

【通常情況下使用del指令可以很快的刪除數據,但是當被刪除的key是一個非常大的物件時,例如:刪除的時包含成千上萬個元素的hash集合時,那麼del指令就會造成Redis主執行緒卡頓,因此使用惰性刪除可以有效避免Redis卡頓問題。 】

考點分析:

關於Redis執行緒模型的問題(單執行緒或多執行緒)幾乎是Redis必問的問題之一,但回答好的人卻不多,大部分只能回答上來Redis是單線程的以及說出來單線程的眾多好處,但對於Redis4.0和Redis6.0中,尤其是Redis6.0中多線程的特點,能夠準確回答上來的人非常少。關於單線和多執行緒的相關知識,還有以下面試題。

1.Redis主線程既然是單線程,為什麼還這個快?

2.介紹Redis中的IO多路復用?

3.介紹Redis6.0中的多執行緒?

1.Redis為什麼這麼快?

原因有以下幾點:

a.基於記憶體運算:Redis的所有資料都存在記憶體中,因此所有的運算都是記憶體等級的,所以它的效能比較高。

b.資料結構簡單:Redis的資料結構比較簡單,是為Redis專門設計的,而這些簡單的資料結構的查找和操作的時間複雜度都是O(1)。

c.多路復用和非阻塞IO:Redis使用IO多路復用功能來監聽多個socket連接的客戶端,這樣就可以使用一個線程來處理多個情況,從而減少線程切換帶來的開銷,同時也避免了IO阻塞操作,大大提升了Redis的效能。

d.避免上下文切換:因為是單執行緒模型,因此就避免了不必要的上下文切換和多執行緒競爭,這就省去了多執行緒切換帶來的時間和效能上的開銷,而且單線不會導致死鎖的問題發生。

官方使用的基準測試結果表明,單執行緒的Redis可以達到10W/S的吞吐量。

2.IO多路復用是什麼?

套接字的讀寫方法預設是阻塞的,例如當呼叫讀取操作read方法時,緩衝區沒有任何數據,那麼這個執行緒會卡在這裡,直到緩衝區有數據或者連線被關閉時,read方法才會返回,該執行緒才能繼續處理其他業務。

但這樣顯然就降低了程式的執行效率,而Redis使用的時非阻塞的IO,這就意味著IO的讀寫流程不再是阻塞的,讀寫方法都是瞬間完成並且返回的,也就是它會採用能讀多少就讀多少、能寫多少就寫多少的策略來執行IO操作,這顯然更符合我們對性能的追求。

但這種非阻塞的IO也面臨一個問題,那就是當我們執行讀取操作時,有可能只讀取了一部分資料;同理寫資料也是這種情況,當緩衝區滿了,而我們的數據還沒寫完,那麼生效的數據何時寫就成了一個問題。

而IO的多路復用就是解決上面的這個問題的,使用IO多路復用最簡單的方式就是使用select函數,此函數是操作系統提供給用戶程序的API接口,用於監控多個檔案描述符的可讀和可寫情況的,這樣就可以監控到檔案描述符的讀寫事件了。當監控到對應的時間之後就可以通知執行緒處理對應的業務了,這樣就保證了Redis讀寫功能的正常執行。

【不過現在的作業系統基本上已經不適用select函數了,改為呼叫epoll函數(Linux)了,macOS則是使用Kqueue(繼承與Unix),因為select函數在檔案描述子非常多的時候性能非常差。 】

3.Redis6.0中的多執行緒?

Redis單執行緒的優點非常,不但降低了Redis內部實作的負責性,也讓所有操作都可以在無鎖的情況下進行,而且不存在死鎖和執行緒切換帶來的效能以及時間上的消耗;但是其缺點也很明顯,單線程機制導致Redis的QPS(Query Per Second,每秒查詢數)很難得到有效的提高(雖然夠快了,但人們畢竟還是要有更高的追求的)。

Redis雖然在4.0版本中引入了多線程,但是此版本的多線程只能用於大數據量的非同步刪除,對於非刪除操作的意義並不是很大。

如果我們使用Redis多執行緒就可以分攤Redis同步讀寫IO的壓力,以及充分利用多核心CPU資源,並且可以有效的提升Redis的QPS。在Redis中雖然使用了IO多路復用,並且是基於非阻塞的IO進行操作的,但是IO的讀寫本身是阻塞的。例如當socket中有資料時,Redis會先將資料從內核態空間拷貝到使用者態空間,然後再進行相關操作,而這個拷貝過程是阻塞的,並且當資料量越大時拷貝所需的時間就越多,而這些操作都是基於單執行緒完成的。

因此在Redis6.0中新增了多執行緒的功能來提高IO的讀寫效能,它的主要實作思路是將主執行緒的IO讀寫任務拆分給一組獨立的執行緒去執行,這樣就可以使用多個socket的讀寫並行化了,但Redis的命令依舊是主執行緒串列執行的。

但是注意:Redis6.0是預設禁用多執行緒的,但可以透過設定檔redis.conf中的io-threads-do-reads 等於 true 來開啟。但還不夠,除此之外我們還需要設定執行緒的數量才能正確地開啟多執行緒的功能,同樣是修改Redis的配置,例如設定 io-threads 4,表示開啟4個執行緒。

【關於執行緒數的設置,官方的建議是如果為4核心CPU,那麼設定執行緒數為2或3;如果為8核心CPU,那麼設定執行緒數為6.總之執行緒數一定要小於機器的CPU核數,執行緒數並不是越大越好。 】

關於Redis的效能,Redis的作者在2019年的RedisConf大會上提到,Redis6.0引入的多執行緒IO特性對效能的提升至少是一倍以上。國人也有在阿里雲使用4個線程的Redis版本和單線程的Redis進行比較測試,發現測試結果和Redis作者說的一致,性能基本上可以提高一倍。

總結:

本文介紹了Redis在4.0之前單執行緒依然快的原因:基於記憶體操作、資料結構簡單、IO多路復用和非阻塞IO、避免了不必要的線程上下文切換。並且在Redis4.0開始支援多線程,主要體現在大數據的非同步刪除方面,例如:unlink key、flushdb async、flushall async等。而Redis6.0的多執行緒則增加了對IO讀寫的並發能力,用於更好的提升Redis的效能。

更多程式相關知識,請造訪:程式設計教學! !

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

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn