官網的說法
我們先來認真看一下官網的說法。翻譯過來大意如下:
CPU並不是您使用Redis的瓶頸,因為通常Redis要么受內存限制,要么受網絡限制。例如,使用在一般Linux系統上運行的流水線Redis每秒可以發送一百萬個請求,因此,如果您的應用程序主要使用O(N)或O(log(N))命令,則幾乎不會使用過多的CPU 。
但是,為了最大程度地利用CPU,您可以在同一服務器上啟動多個Redis實例,並將它們視為不同的服務器。在某個時候,單個實例可能還不夠,因此,如果您要使用多個CPU,則可以開始考慮更早地分片的某種方法。
但是,在Redis 4.0中,我們開始使Redis具有更多線程。目前,這僅限於在后台刪除對象,以及阻止通過Redis模塊實現的命令。對於將來的版本,計划是使Redis越來越線程化。
既然redis的瓶頸不是cpu,那么在單線程可以實現的情況下,自然就使用單線程了。
自己的解讀
我們知道redis是基於內存的。那么我們接下來要了解一個問題多線程cpu和內存直接操作差多少?
多線程操作就是使用多個cpu模擬多個線程,對redis進行操作。這樣會造成一個巨大的問題,就是cpu的上下文切換問題。cpu的上下文切換的效率比直接在內存中進行讀取差的很多。redis使用單個cpu綁定一個內存,針對內存的處理就是單線程的,這樣避免了上下文的切換,所以非常的快。
一次cpu的切換時間大約是1500ns。從內存中讀取1mb的連續數據,耗時大約是250us。如果1mb的數據被多個線程讀取了1000次。那么就是有1000次時間的上下文切換。於是就是1500ns*1000=1500us。結果顯而易見。1500us和250us差的還是很多的。
那么redis采取單線程還避免了很多問題。如果redis使用多線程來進行,那么就要考慮多線程帶來的數據安全問題,如果我們在操作redis的list,hash等數據結構的時候。多線程就可能存在數據不安全的情況,這是就要加鎖。一旦加鎖就影響了程序的執行速度。
磁盤讀取和內存讀取的區別
【IOPS(Input/Output Operations Per Second)是一個用於計算機存儲設備(如硬盤(HDD)、固態硬盤(SSD)或存儲區域網絡(SAN))性能測試的量測方式】
【吞吐量是指對網絡、設備、端口、虛電路或其他設施,單位時間內成功地傳送數據的數量(以比特、字節、分組等測量)】
內存是一個 IOPS 非常高的系統,因為我想申請一塊內存就申請一塊內存,銷毀一塊內存我就銷毀一塊內存,內存的申請和銷毀是很容易的。而且內存是可以動態的申請大小的。
磁盤的特性是:IPOS很低很低,但吞吐量很高。這就意味着,大量的讀寫操作都必須攢到一起,再提交到磁盤的時候,性能最高。為什么呢?
如果我有一個事務組的操作(就是幾個已經分開了的事務請求,比如寫讀寫讀寫,這么五個操作在一起),在內存中,因為IOPS非常高,我可以一個一個的完成,但是如果在磁盤中也有這種請求方式的話,
我第一個寫操作是這樣完成的:我先在硬盤中尋址,大概花費10ms,然后我讀一個數據可能花費1ms然后我再運算(忽略不計),再寫回硬盤又是10ms ,總共21ms
第二個操作去讀花了10ms, 第三個又是寫花費了21ms ,然后我再讀10ms, 寫21ms ,五個請求總共花費83ms,這還是最理想的情況下,這如果在內存中,大概1ms不到。
所以對於磁盤來說,它吞吐量這么大,那最好的方案肯定是我將N個請求一起放在一個buff里,然后一起去提交。
方法就是用異步:將請求和處理的線程不綁定,請求的線程將請求放在一個buff里,然后等buff快滿了,處理的線程再去處理這個buff。然后由這個buff 統一的去寫入磁盤,或者讀磁盤,這樣效率就是最高。
對於慢速設備,這種處理方式就是最佳的,慢速設備有磁盤,網絡 ,SSD 等等。
為什么單核cpu綁定一塊線程內存效率最高
我們不能任由操作系統負載均衡,因為我們自己更了解自己的程序,所以我們可以手動地為其分配CPU核,而不會過多地占用CPU”,默認情況下單線程在進行系統調用的時候會隨機使用CPU內核,為了優化Redis,我們可以使用工具為單線程綁定固定的CPU內核,減少不必要的性能損耗!
redis作為單進程模型的程序,為了充分利用多核CPU,常常在一台server上會啟動多個實例。而為了減少切換的開銷,有必要為每個實例指定其所運行的CPU。
Linux 上 taskset 可以將某個進程綁定到一個特定的CPU。你比操作系統更了解自己的程序,為了避免調度器愚蠢的調度你的程序,或是為了在多線程程序中避免緩存失效造成的開銷。
redis的多線程情況
一個redisserver運行的時候,不是單線程的,比如進行rdb備份的時候,就是fork出了一個子進程來進行實現。
可以通過 ps -ef | grep redis 來查看到redis的進程pid。
再使用ps -T -p pid 來查看當前pid下面的線程數。
ps命令的“-T”參數表示顯示線程(Show threads, possibly with SPID column.)“SPID”欄表示線程ID,而“CMD”欄則顯示了線程名稱。
redis的內存模式為什么比數據庫磁盤塊
磁盤數據庫的形式,當我們找數據的時候,先找到索引,通過索引然后關聯到磁盤的數據。如果使用內存的方式,可以直接從內存中讀取數據。減少了硬盤的io。不受硬盤的讀取速度影響。
redis的單線程到底有多快
redis的每秒查詢次數可以達到10w+。但是隨着連接數的增加,每秒的查詢數會進行減少。通一個服務器多個連接數導致。
為什么內存讀取比硬盤快
兩種的方式不同。內存是一種半導體的存儲器,是ram。內存中的數據是電,一旦斷電內存中的數據就會消失。內存沒有機械結構。
硬盤是一種機械結構。查找數據的時候,磁盤要運動到想應的位置。磁頭讀取磁盤里的數據。
redis單線程的優勢和劣勢
優勢
代碼更清晰,處理邏輯更簡單。
不用去考慮各種鎖的問題,不存在加鎖、釋放鎖操作,沒有因為可能出現死鎖而導致的性能消耗。
不存在“多進程或者多線程導致的切換”而消耗CPU。
劣勢
無法發揮多核CPU性能,不過可以通過在單機開多個Redis實例來完善。
redis的多路io復用
redis 采用網絡IO多路復用技術,來保證在多連接的時候系統的高吞吐量。
多路-指的是多個socket網絡連接,復用-指的是復用一個線程。多路復用主要有三種技術:select,poll,epoll。epoll是最新的、也是目前最好的多路復用技術。
采用多路I/O復用技術:其一,可以讓單個線程高效處理多個連接請求(盡量減少網絡IO的時間消耗)。其二,Redis在內存中操作數據的速度非常快(內存里的操作不會成為這里的性能瓶頸)。主要以上兩點造就了Redis具有很高的吞吐量。
采用多路 I/O 復用技術可以讓單個線程高效的處理多個連接請求。
redis為什么是單線程及為什么快的總結
1、Redis是純內存數據庫,一般都是簡單的存取操作,線程占用的時間很多,時間的花費主要集中在IO上,所以讀取速度快。
2、Redis使用的是非阻塞IO、IO多路復用,使用了單線程來輪詢描述符,將數據庫的開、關、讀、寫都轉換成了事件,減少了線程切換時上下文的切換和競爭。
3、Redis采用了單線程的模型,保證了每個操作的原子性,也減少了線程的上下文切換和競爭。
4、Redis避免了多線程的鎖的消耗。
5、Redis采用自己實現的事件分離器,效率比較高,內部采用非阻塞的執行方式,吞吐能力比較大。