Redis憑啥可以這麼快
在日常開發中,為瞭保證數據的一致性,我們一般都選擇關系型數據庫來存儲數據,如 MySQL
,Oracle
等,因為關系型數據庫有著事務的特性。然而在並發量比較大的業務場景,關系型數據庫卻又往往會成為系統瓶頸,無法完全滿足我們的需求,所以就需要使用到緩存,而非關系型數據庫,即 NoSQL
數據庫往往又會成為最佳選擇。
NoSQL
數據庫最常見的解釋是 non-relational
,也有人解釋為 Not Only SQL
。非關系型數據庫不保證事務,也就是不具備事務 ACID
特性,這也是非關系型數據庫和關系型數據庫最大的區別,而我們即將介紹的 Redis
就屬於 NoSQL
數據庫的一種。
什麼是 Redis
Redis
全稱是:REmote DIctionary Service
,即遠程字典服務。Redis
是一個開源的(遵守 BSD
協議)、支持網絡、可基於內存亦可持久化的日志型、Key-Value
數據庫。
Redis
具有以下特性:
1、支持豐富的數據類型:字符串(strings),散列(hashes),列表(lists),集合(sets),有序集合(sorted sets),位圖等。
2、功能豐富:提供瞭持久化機制,過期策略,訂閱/發佈等功能。
3、高性能,高可用且支持集群。
4、提供瞭多種語言的 API
。
Redis 的安裝
1、下載對應版本的安裝包,如:Redis 5.0.5 版本,其他版本也可以點擊這裡進行下載。
2、下載好之後傳到服務器指定目錄,執行命令 tar -zxvf redis-5.0.5.tar.gz
進行解壓。
3、解壓成功之後,進入 Redis
主目錄,執行命令 make && make install PREFIX=/xxx/xxx/redis-5.0.5
進行安裝,如果不指定目錄,則默認是安裝在 /usr/local
目錄下。
4、安裝成功之後可以看到 Redis
主目錄下多瞭一個 bin
目錄,bin
目錄內包含瞭一些可執行腳本。
5、回到 Redis
主目錄下,找到 redis.conf
配置文件,將其中的配置 daemonize no
修改為 daemonize yes
,表示在後臺啟動服務。
6、然後就可以執行命令 /xxx/xxx/redis-5.0.5/bin/redis-server /xxx/xxx/redis-5.0.5/redis.conf
啟動 Redis
服務。 Redis 到底有多快
大傢可能都知道 Redis
很快,可是 Redis
到底能有多快呢,比如 Redis
的吞吐量能達到多少?我想這就不是每一個人都能說的上來一個具體的數字瞭。
Redis
官方提供瞭一個測試腳本,可以供我們測試 Redis
的 吞吐量。
redis-benchmark -q -n 100000
可以測試常用命令的吞吐量。 redis-benchmark -t set,lpush -n 100000 -q
測試 Redis
處理 set
和 lpush
命令的吞吐量。 redis-benchmark -n 100000 -q script load "redis.call('set','foo','bar')"
測試 Redis
處理 Lua
腳本等吞吐量。
下圖就是我這邊執行第一條命令的自測結果,可以看到大部分命令的吞吐量都可以達到 4
萬以上,也就是說每秒鐘可以處理 4
萬次以上請求:
但是如果你以為這就是 Redis
的真實吞吐量,那就錯瞭。實際上,Redis
官方的測試結果是可以達到 10
萬的吞吐量,下圖就是官方提供的一個基準測試結果(縱坐標就是吞吐量,橫坐標是連接數):
Redis 是單線程還是多線程
這個問題比較經典,因為在很多人的認知裡,Redis
就是單線程的。然而 Redis
從 4.0
版本開始就有瞭多線程的概念,雖然處理命令請求的核心模塊確實是保證瞭單線程執行,然而在其他許多地方已經有瞭多線程,比如:在後臺刪除對象,通過 Redis
模塊實現阻塞命令,生成 dump
文件,以及 6.0
版本中網絡 I/O
實現瞭多線程等,而且在未來 Redis
應該會有越來越多的模塊實現多線程。
所謂的單線程,隻是說 Redis
的處理客戶端的請求(即執行命令)時,是單線程去執行的,並不是說整個 Redis
都是單線程。
Redis 為什麼選擇使用單線程來執行請求
Redis
為什麼會選擇使用單線程呢?這是因為 CPU
成為 Redis
瓶頸的情況並不常見,成為 Redis
瓶頸的通常是內存或網絡帶寬。例如,在一個普通的 Linux
系統上使用 pipelining
命令,Redis
可以每秒完成 100
萬個請求,所以如果我們的應用程序主要使用 O(N)
或 O(log(N))
復雜度的命令,它幾乎不會使用太多的 CPU
。
那麼既然 CPU
不會成為瓶頸,理所當然的就沒必要去使用多線程來執行命令,我們需要明確的一個問題就是多線程一定比單線程快嗎?答案是不一定。因為多線程也是有代價的,最直接的兩個代價就是線程的創建和銷毀線程(當然可以通過線程池來一定程度的減少頻繁的創建線程和銷毀線程)以及線程的上下文切換。
在我們的日常系統中,主要可以區分為兩種:CPU
密集型 和 IO
密集型。
CPU 密集型:
這種系統就說明 CPU
的利用率很高,那麼使用多線程反而會增加上下文切換而帶來額外的開銷,所以使用多線程效率可能會不升反降。舉個例子:假如你現在在幹活,你一直不停的在做一件事,需要 1
分鐘可以做完,但是你中途總是被人打斷,需要花 1
秒鐘時間步行到旁邊去做另一件事,假如這件事也需要 1
分鐘,那麼你因為反復切換做兩件事,每切換一次就要花 1
秒鐘,最後做完這 2
件事的時間肯定大於 2
分鐘(取決於中途切換的次數),但是如果中途不被打斷,你做完一件事再去做另一件事,那麼你最多隻需要切換 1
次,也就是 2
分 1
秒就能做完。
IO 密集型:
IO
操作也可以分為磁盤 IO
和網絡 IO
等操作。大部分 IO
操作的特點是比較耗時且 CPU
利用率不高,所以 Redis 6.0
版本網絡 IO
會改進為多線程。至於磁盤 IO
,因為 Redis
中的數據都存儲在內存(也可以持久化),所以並不會過多的涉及到磁盤操作。舉個例子:假如你現在給樹苗澆水,你每澆完一次水之後就需要等別人給你加水之後你才能繼續澆,那麼假如這個等待過程需要 5
秒鐘,也就是說你澆完一次水就可以休息 5
秒鐘,而你切換去做另一件事來回隻需要 2
秒,那麼你完全可以先去做另一件事,做完之後再回來,這樣就可以充分利用你空閑的 5
秒鐘時間,從而提升瞭效率。
使用多線程還會帶來一個問題就是數據的安全性,所以多線程編程都會涉及到鎖競爭,由此也會帶來額外的開銷。
什麼是 I/O 多路復用
I/O
指的是網絡 I/O
, 多路指的是多個 TCP
連接(如 Socket
),復用指的是復用一個或多個線程。I/O
多路復用的核心原理就是不再由應用程序自己來監聽連接,而是由服務器內核替應用程序監聽。
在 Redis
中,其多路復用有多種實現,如:select
,epoll
,evport
,kqueue
等。
我們用去餐廳吃飯為的例子來解釋一下 I/O
多路復用機制(點餐人相當於客戶端,餐廳的廚房相當於服務器,廚師就是線程)。
阻塞 IO
:張三去餐廳吃飯,點瞭一道菜,這時候他啥事也不幹瞭,就是一直等,等到廚師炒好菜,他就把菜端走開始吃飯瞭。也就是在菜被炒好之前,張三被阻塞瞭,這就是 BIO
(阻塞 IO
),效率會非常低下。
非阻塞 IO
:張三去餐廳吃飯,點瞭一道菜,這時候張三他不會一直等,找瞭個位置坐下,刷刷抖音,打打電話,做點其他事,然後每隔一段時間就去廚房問一下自己的菜好瞭沒有。這種就屬於非阻塞 IO
,這種方式雖然可以提高性能,但是如果有大量 IO
都來定期輪詢,也會給服務器造成非常大的負擔。
事件驅動機制:張三去餐廳吃飯,點瞭一道菜,這時候他找瞭個位置坐下來等: 廚房那邊菜做好瞭就會把菜端出來瞭,但是並不知道這道菜是誰的,於是就挨個詢問顧客,這就是多路復用中的 select
模型,不過 select
模型最多隻能監聽 1024
個 socket
(poll
模型解決瞭這個限制問題)。廚房做好瞭菜直接把菜放在窗口上,大喊一聲,某某菜做好瞭,是誰的快過來拿,這時候聽到通知的人就會自己去拿,這就是多路復用中的 epoll
模型。
需要註意的是在 IO
多路復用機制下,客戶端可以阻塞也可以選擇不阻塞(大部分場景下是阻塞 IO
),這個要具體情況具體分析,但是在多路復用機制下,服務端就可以通過多線程(上面示例中可以多幾個廚師同時炒菜)來提升並發效率。
Redis 中 I/O 多路復用的應用
Redis
服務器是一個事件驅動程序,服務器需要處理兩類事件:文件事件和時間事件。
文件事件:Redis
服務器和客戶端(或其他服務器)進行通信會產生相應的文件事件,然後服務器通過監聽並處理這些事件來完成一系列的通信操作。
時間事件:Redis
內部的一些在給定時間之內需要進行的操作。
Redis
的文件事件處理器以單線程的方式運行,其內部使用瞭 I/O
多路復用程序來同時監聽多個套接字(Socket
)連接,提升瞭性能的同時又保持瞭內部單線程設計的簡單性。下圖就是文件事件處理器的示意圖:
I/O
多路復用程序雖然會同時監聽多個 Socket
連接,但是其會將監聽的 Socket
都放到一個隊列裡面,然後通過這個隊列有序的,同步的將每個 Socket
對應的事件傳送給文件事件分派器,再由文件事件分派器分派給對應的事件處理器進行處理,隻有當一個 Socket
所對應的事件被處理完畢之後,I/O
多路復用程序才會繼續向文件事件分派器傳送下一個 Socket
所對應的事件,這也可以驗證上面的結論,處理客戶端的命令請求是單線程的方式逐個處理,但是事件處理器內並不是隻有一個線程。
Redis 為什麼這麼快
Redis
為什麼這麼快的原因前面已經基本提到瞭,現在我們再進行總結一下:
1、Redis
是一款純內存結構,避免瞭磁盤 I/O
等耗時操作。
2、Redis
命令處理的核心模塊為單線程,減少瞭鎖競爭,以及頻繁創建線程和銷毀線程的代價,減少瞭線程上下文切換的消耗。
3、采用瞭 I/O
多路復用機制,大大提升瞭並發效率。
到此這篇關於Redis憑啥可以這麼快的文章就介紹到這瞭,更多相關Redis為什麼快內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 淺談為什麼單線程的redis那麼快
- Docker中redis安裝及測試教程
- Redis3.2.11在centos9安裝與卸載過程詳解
- Linux系統下安裝Redis數據庫過程
- Redis緩存IO模型的演進教程示例精講