1. 程式人生 > >為什麼redis是單執行緒的以及為什麼這麼快?

為什麼redis是單執行緒的以及為什麼這麼快?

### 官網的說法 ![](https://img2020.cnblogs.com/blog/1534147/202004/1534147-20200427212506756-1345118511.png) 我們先來認真看一下官網的說法。翻譯過來大意如下: 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下面的執行緒數。 ![](https://img2020.cnblogs.com/blog/1534147/202004/1534147-20200427214207181-296351537.png) ps命令的“-T”引數表示顯示執行緒(Show threads, possibly with SPID column.)“SPID”欄表示執行緒ID,而“CMD”欄則顯示了執行緒名稱。 ### redis的記憶體模式為什麼比資料庫磁碟塊 磁碟資料庫的形式,當我們找資料的時候,先找到索引,通過索引然後關聯到磁碟的資料。如果使用記憶體的方式,可以直接從記憶體中讀取資料。減少了硬碟的io。不受硬碟的讀取速度影響。 ### redis的單執行緒到底有多快 ![](https://img2020.cnblogs.com/blog/1534147/202004/1534147-20200427214820449-905516468.png) 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採用自己實現的事件分離器,效率比較高,內部採用非阻塞的執行方式,吞吐能力比