redis 为什么这么快,你真的知道吗?
前言
相信大家在面試過程中,都被面試官問到過這樣一個問題,緩存中間件大名鼎鼎的 redis 速度為什么這么快呢?
針對于面試過程中的痛點問題,筆者昨晚熬夜收集資料,并且通過走訪大量使用者,整理出如下的結論。我敢保證,你看了這篇文章,再問你這個問題,保準把面試官虐哭。
分析原因
這里就不賣關子了,先說結論,我們再對原因進行抽絲剝繭。
redis快的原因
純內存操作(最主要條件)
首先最主要的原因一定是:redis 的數據操作是基于內存的。
眾所周知,內存的訪問速度是遠遠大于硬盤訪問速度的。我們來做個對比,拿數據庫(硬盤)和 redis (內存)對比,一個操作對應磁盤,一個操作對應內存。他們兩個的的訪問速度差了一個數量級。
可能大家對數量級沒有什么該概念。那可是整整 1000 倍啊!現在大家知道了吧。
下面是我精心為大家準備的訪問速度圖,已經驗證,方便大家食用。
有的小伙伴要問了,redis不是有數據持久化嗎?
怎么會是只操作內存呢?
這位同學一看就是對 redis 理解的不夠透徹,redis 持久化線程和操作內存數據的線程,并不是一個線程,我們這里說的 redis 快,只是針對操作內存的線程來說的,操作很快。因為操作是直接客戶端響應時間息息相關的。
合適的線程模型
🥈單線程誤區
先說一個常見誤區,就是大家在其他的文章或者面試題中,通過一段時間的以訛傳訛。
大家都認為 redis 速度快,有一個原因是單線程!!!
在這里先反駁一下這個觀點,這個結論是個明顯的因果顛倒,主次不分的結論!!
為什么這么說呢?
首先單線程 redis 不是純粹的單線程,我們所知曉的 redis 單線程只有 網絡請求模塊和 數據操作模塊是單線程的。
你要知道多線程的出現,本身就是為了解決多核心 CPU 利用率不足。如果單線程是快的原因的話,那么我們還要多線程干什么,小伙伴你說是吧!
那為什么不用多線程呢?
因為沒有必要,我們先來想一下多線程適用場景有哪些?
在這之前,我們先了解一下計算機在運行過程中的主要操作分為以下兩種:
多線程的出現以及適用場景目的就是為了提升單線程在 cpu利用率和io利用率之間的不足
那么redis怎么不需要多線程呢?
我們從上面的計算機操作分類來說
結論:redis 在 CPU 計算上不存在瓶頸,性能瓶頸只存在于網絡 IO 中
但是~想要提高網絡 IO 的利用率,不是只有多線程一條路。
所以說千萬不要陷入 redis 是單線程所以快,現在是 CPU 多核心時代,最差的情況 CPU 一個核心綁定一個線程,不同線程之間也不處理競態資源,也比你只用到一個核心處理快的多了吧!
我這里總結一下我的看法,redis 6.0版本 之前采用單線程處理網絡 IO 原因如下:
那為什么redis 6.0 還是采用了多線程處理網絡 IO ?
不是說多路復用技術已經大大的提升了網絡 IO 利用率了么,為啥還需要多線程?
主要是因為我們對 Redis 有著更高的要求。
據測算,redis 將所有數據放在內存中,內存的響應時長大約為 100 納秒,對于小數據包,redis 服務器可以處理 80,000 到 100,000 QPS,這么高的對于 80% 的公司來說,單線程的 redis 已經足夠使用了。
但隨著越來越復雜的業務場景,有些公司動不動就上億的交易量,因此需要更大的 QPS。
為了提升 QPS,很多公司的做法是部署 redis集群,并且盡可能提升 redis 機器數。但是這種做法的資源消耗是巨大的。
而經過分析,限制 redis 的性能的主要瓶頸出現在網絡IO的處理上,雖然之前采用了多路復用技術。但是我們前面也提到過,多路復用的 IO 模型本質上仍然是同步阻塞型 IO 模型。
下圖是 redis 網絡 IO 多線程和單線程的速度對比圖,大家都來看一下
從上面可以看到 `GET/SET` 命令在 4 線程 IO 時性能相比單線程是幾乎是翻倍了。
結論:redis 6.0 多線程網絡 IO 性能提升巨大,不嚴謹條件下幾乎翻倍
🥈單線程模式下快的原因
說完了對于單線程的誤區,下面我們就要說一下在單線程模式下,redis 快的原因
用四個字就可以來形容這個原因,那就是 多路復用
多路復用是啥呢?
簡單理解就是 單個線程同時檢測若干個網絡連接(Socket)是否可以執行IO操作的能力,就是將多個進程的網絡 IO Socket注冊到同一個管道上。 用最少的資源,干最多的事情。
比較傳統的方式是使用多線程模型,每來一個客戶端連接,就分配一個線程,然后后續的讀寫都在對應的進程/線程,這種方式處理 100 個客戶端沒問題,但是當客戶端增大到 10000 個時,10000 個進程/線程的調度、上下文切換以及它們占用的內存,都會成為瓶頸。
為了解決上面這個問題,就出現了 I/O 的多路復用,可以只在一個進程里處理多個文件的 I/O
所有的 io 操作 由這一個管道來和內核進行統一交互 管道中的io請求數據準備好之后 管道會將數據 拷貝到用戶空間中。
在 redis 中,每當一個套接字準備好執行連接應答、寫入、讀取、關閉等操作時,就會產生一個文件事件。因為一個服務器通常會連接多個套接字,所以多個文件事件有可能會并發地出現。
但是出現的個數不可能有很多,這樣我們用一個線程來監聽這些消息,然后分派去給操作線程執行就可以。
多路復用模型顯而易見的好處:
優秀的數據結構
redis 在存儲數據結構的設計上還是花了一番心思的 不然這也不會成為它快的一點原因。
它擁有五種常用的數據類型,幫助我們在各種各樣的場景上都能靈活應對,并且也能用對應的數據類型做出很多很有意思的事情。
redis的五種數據類型主要有:
舉個例子,我們都知道redis底層使用c語言寫的,但是 String 實現并不是簡單的用c語言的字符串實現的,而是采用一個 SDS(簡單動態字符串) 的結構體來實現的。
Tips:這里給大家補充一個知識點,C語言的String是以 “\0” 結尾的,并且計算長度是得通過遍歷獲取,時間復雜度是 O(n) , 而且在數據中不能有 “\0” 出現,會導致計算錯誤。
redis String 的 SDS 結構體,不僅讓長度的獲取是 O(1) 操作,而且二進制安全,沒有特殊字符的限制,可以存儲視頻圖片的數據。
這里我想讓大家知道,redis 數據結構設計考慮還是很優秀的。
數據結構的詳細解析,我們在這里就不多贅述了,我們放到我們的下一篇。
歡迎小伙伴們前去圍觀,也歡迎批評指正。
合理的數據編碼方式
怎么理解這個合理的編碼方式呢?
我們還是以 redis 中的 string 結構舉例子,redis為了存儲不同大小的字符串,精心設計了 5 種類型。
sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64
它們的不同就是,本身的數據類型不同,舉個例子
結論: 通過這種能夠靈活存儲不同大小的字符串,有效的節省了內存。字符串大的時候分配大的內存空間,小的時候就分配小的,高效利用內存。
當然,在這里只是舉個例子,想了解更多redis 底層細節的小伙伴,我們放到了后續的系列。
歡迎小伙伴們前去圍觀,也歡迎批評指正。
總結
至此,我們知道了這個 redis 經典面試題的答案了。
原因無他。分別是:
不知道大家學會了嗎? 學會的小伙伴們,再也不怕那些刁鉆的面試官了,我敢保證,你看了這篇文章,再問你這個問題,保準把面試官虐哭。
總結
以上是生活随笔為你收集整理的redis 为什么这么快,你真的知道吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大数据分析李俊
- 下一篇: 汽车电路的主要构成元件和电路图种类