當前位置:
首頁 > 知識 > 為什麼Redis這麼快?

為什麼Redis這麼快?

什麼是redis?

Redis是一個開源的使用ANSI C語言編寫、支持網路、可基於內存亦可持久化的日誌型、Key-Value 資料庫。

我們得知,redis是一個基於內存的,支持網路的資料庫。我們從中看出,最重要的一點,那麼就是它是一個基於內存的資料庫,從存儲位置來講,就已經比磁碟型傳統資料庫例如:mysql快得多。

為什麼Redis這麼快?


redis的內存模型分析

Redis內存劃分

Redis作為內存資料庫,在內存中存儲的內容主要是數據(鍵值對);通過前面的敘述可以知道,除了數據以外,Redis的其他部分也會佔用內存。

Redis的內存佔用主要可以劃分為以下幾個部分:

1、數據

作為資料庫,數據是最主要的部分;這部分佔用的內存會統計在used_memory中。

Redis使用鍵值對存儲數據,其中的值(對象)包括5種類型,即字元串、哈希、列表、集合、有序集合。這5種類型是Redis對外提供的,實際上,在Redis內部,每種類型可能有2種或更多的內部編碼實現;此外,Redis在存儲對象時,並不是直接將數據扔進內存,而是會對對象進行各種包裝:如redisObject、SDS等;這篇文章後面將重點介紹Redis中數據存儲的細節。

2、進程本身運行需要的內存

Redis主進程本身運行肯定需要佔用內存,如代碼、常量池等等;這部分內存大約幾兆,在大多數生產環境中與Redis數據佔用的內存相比可以忽略。這部分內存不是由jemalloc分配,因此不會統計在used_memory中。

補充說明:除了主進程外,Redis創建的子進程運行也會佔用內存,如Redis執行AOF、RDB重寫時創建的子進程。當然,這部分內存不屬於Redis進程,也不會統計在used_memory和used_memory_rss中。

3、緩衝內存

緩衝內存包括客戶端緩衝區、複製積壓緩衝區、AOF緩衝區等;其中,客戶端緩衝存儲客戶端連接的輸入輸出緩衝;複製積壓緩衝用於部分複製功能;AOF緩衝區用於在進行AOF重寫時,保存最近的寫入命令。在了解相應功能之前,不需要知道這些緩衝的細節;這部分內存由jemalloc分配,因此會統計在used_memory中。

4、內存碎片

內存碎片是Redis在分配、回收物理內存過程中產生的。例如,如果對數據的更改頻繁,而且數據之間的大小相差很大,可能導致redis釋放的空間在物理內存中並沒有釋放,但redis又無法有效利用,這就形成了內存碎片。內存碎片不會統計在used_memory中。

內存碎片的產生與對數據進行的操作、數據的特點等都有關;此外,與使用的內存分配器也有關係:如果內存分配器設計合理,可以儘可能的減少內存碎片的產生。後面將要說到的jemalloc便在控制內存碎片方面做的很好。

如果Redis伺服器中的內存碎片已經很大,可以通過安全重啟的方式減小內存碎片:因為重啟之後,Redis重新從備份文件中讀取數據,在內存中進行重排,為每個數據重新選擇合適的內存單元,減小內存碎片。

下面看看網上的技術文章對這個問題的解答

1、完全基於內存,絕大部分請求是純粹的內存操作,非常快速。數據存在內存中,類似於HashMap,HashMap的優勢就是查找和操作的時間複雜度都是O(1);

2、數據結構簡單,對數據操作也簡單,Redis中的數據結構是專門進行設計的;

3、採用單線程,避免了不必要的上下文切換和競爭條件,也不存在多進程或者多線程導致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因為可能出現死鎖而導致的性能消耗;

4、使用多路I/O復用模型,非阻塞IO;

5、使用底層模型不同,它們之間底層實現方式以及與客戶端之間通信的應用協議不一樣,Redis直接自己構建了VM 機制 ,因為一般的系統調用系統函數的話,會浪費一定的時間去移動和請求;

以上幾點都比較好理解,下邊我們針對多路 I/O 復用模型進行簡單的探討:

多路I/O復用模型是利用 select、poll、epoll 可以同時監察多個流的 I/O 事件的能力,在空閑的時候,會把當前線程阻塞掉,當有一個或多個流有 I/O 事件時,就從阻塞態中喚醒,於是程序就會輪詢一遍所有的流(epoll 是只輪詢那些真正發出了事件的流),並且只依次順序的處理就緒的流,這種做法就避免了大量的無用操作。

這裡「多路」指的是多個網路連接,「復用」指的是復用同一個線程。採用多路 I/O 復用技術可以讓單個線程高效的處理多個連接請求(盡量減少網路 IO 的時間消耗),且 Redis 在內存中操作數據的速度非常快,也就是說內存內的操作不會成為影響Redis性能的瓶頸,主要由以上幾點造就了 Redis 具有很高的吞吐量。


其實這是問題的關鍵

從本質上來講,其實redis並不是單線程的!!!

從本質上來講,其實redis並不是單線程的!!!

從本質上來講,其實redis並不是單線程的!!!

僅僅是因為它的非阻塞多路復用是單線程的,而真正的事件處理,是由文件事件分派器來做的,因此,對於多路復用的IO來說,僅僅需要把事件發送給隊列就可以了,這不需要太多的cpu。另外官方FAQ表示,因為Redis是基於內存的操作,CPU不是Redis的瓶頸,Redis的瓶頸最有可能是機器內存的大小或者網路帶寬。既然單線程容易實現,而且CPU不會成為瓶頸,那就順理成章地採用單線程的方案了,各位小夥伴們可以試想一下,如果文件事件分派器能夠產生多個處理器線程,那麼redis速度是不是還會提高哪?。

從Redis 4.0版本開始會支持多線程的方式,但是,只是在某一些操作上進行多線程的操作!所以該篇文章在以後的版本中是否還是單線程的方式需要讀者考證!

<hr/>

下面附上事件處理模型

文件事件處理器的構成

圖 IMAGE_CONSTRUCT_OF_FILE_EVENT_HANDLER 展示了文件事件處理器的四個組成部分, 它們分別是套接字、 I/O 多路復用程序、 文件事件分派器(dispatcher)、 以及事件處理器。

為什麼Redis這麼快?

文件事件是對套接字操作的抽象, 每當一個套接字準備好執行連接應答(accept)、寫入、讀取、關閉等操作時, 就會產生一個文件事件。 因為一個伺服器通常會連接多個套接字, 所以多個文件事件有可能會並發地出現。

I/O 多路復用程序負責監聽多個套接字, 並向文件事件分派器傳送那些產生了事件的套接字。

儘管多個文件事件可能會並發地出現, 但 I/O 多路復用程序總是會將所有產生事件的套接字都入隊到一個隊列裡面, 然後通過這個隊列, 以有序(sequentially)、同步(synchronously)、每次一個套接字的方式向文件事件分派器傳送套接字: 當上一個套接字產生的事件被處理完畢之後(該套接字為事件所關聯的事件處理器執行完畢), I/O 多路復用程序才會繼續向文件事件分派器傳送下一個套接字, 如圖 IMAGE_DISPATCH_EVENT_VIA_QUEUE 。

為什麼Redis這麼快?

文件事件分派器接收 I/O 多路復用程序傳來的套接字, 並根據套接字產生的事件的類型, 調用相應的事件處理器。

伺服器會為執行不同任務的套接字關聯不同的事件處理器, 這些處理器是一個個函數, 它們定義了某個事件發生時, 伺服器應該執行的動作。

I/O 多路復用程序的實現

Redis 的 I/O 多路復用程序的所有功能都是通過包裝常見的 select 、 epoll 、 evport 和 kqueue 這些 I/O 多路復用函數庫來實現的, 每個 I/O 多路復用函數庫在 Redis 源碼中都對應一個單獨的文件, 比如 ae_select.c 、 ae_epoll.c 、 ae_kqueue.c , 諸如此類。

因為 Redis 為每個 I/O 多路復用函數庫都實現了相同的 API , 所以 I/O 多路復用程序的底層實現是可以互換的, 如圖 IMAGE_MULTI_LIB 所示。

為什麼Redis這麼快?

Redis 在 I/O 多路復用程序的實現源碼中用 #include 宏定義了相應的規則, 程序會在編譯時自動選擇系統中性能最高的 I/O 多路復用函數庫來作為 Redis 的 I/O 多路復用程序的底層實現:

/* Include the best multiplexing layer supported by this system.
* The following should be ordered by performances, descending. */
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
#ifdef HAVE_KQUEUE
#include "ae_kqueue.c"
#else
#include "ae_select.c"
#endif
#endif
#endif

事件的類型

I/O 多路復用程序可以監聽多個套接字的 ae.h/AE_READABLE 事件和 ae.h/AE_WRITABLE 事件, 這兩類事件和套接字操作之間的對應關係如下:

  • 當套接字變得可讀時(客戶端對套接字執行 write 操作,或者執行 close 操作), 或者有新的可應答(acceptable)套接字出現時(客戶端對伺服器的監聽套接字執行 connect 操作), 套接字產生 AE_READABLE 事件。
  • 當套接字變得可寫時(客戶端對套接字執行 read 操作), 套接字產生 AE_WRITABLE 事件。

I/O 多路復用程序允許伺服器同時監聽套接字的 AE_READABLE 事件和 AE_WRITABLE 事件, 如果一個套接字同時產生了這兩種事件, 那麼文件事件分派器會優先處理 AE_READABLE 事件, 等到 AE_READABLE 事件處理完之後, 才處理 AE_WRITABLE 事件。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 青峰科技 的精彩文章:

vmware虛擬化解決方案配置(實驗)

TAG:青峰科技 |