Redis憑啥可以這么快
在日常開(kāi)發(fā)中,為了保證數(shù)據(jù)的一致性,我們一般都選擇關(guān)系型數(shù)據(jù)庫(kù)來(lái)存儲(chǔ)數(shù)據(jù),如 MySQL
,Oracle
等,因?yàn)殛P(guān)系型數(shù)據(jù)庫(kù)有著事務(wù)的特性。然而在并發(fā)量比較大的業(yè)務(wù)場(chǎng)景,關(guān)系型數(shù)據(jù)庫(kù)卻又往往會(huì)成為系統(tǒng)瓶頸,無(wú)法完全滿足我們的需求,所以就需要使用到緩存,而非關(guān)系型數(shù)據(jù)庫(kù),即 NoSQL
數(shù)據(jù)庫(kù)往往又會(huì)成為最佳選擇。
NoSQL
數(shù)據(jù)庫(kù)最常見(jiàn)的解釋是 non-relational
,也有人解釋為 Not Only SQL
。非關(guān)系型數(shù)據(jù)庫(kù)不保證事務(wù),也就是不具備事務(wù) ACID
特性,這也是非關(guān)系型數(shù)據(jù)庫(kù)和關(guān)系型數(shù)據(jù)庫(kù)最大的區(qū)別,而我們即將介紹的 Redis
就屬于 NoSQL
數(shù)據(jù)庫(kù)的一種。
什么是 Redis
Redis
全稱是:REmote DIctionary Service
,即遠(yuǎn)程字典服務(wù)。Redis
是一個(gè)開(kāi)源的(遵守 BSD
協(xié)議)、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value
數(shù)據(jù)庫(kù)。
Redis
具有以下特性:
1、支持豐富的數(shù)據(jù)類(lèi)型:字符串(strings),散列(hashes),列表(lists),集合(sets),有序集合(sorted sets),位圖等。
2、功能豐富:提供了持久化機(jī)制,過(guò)期策略,訂閱/發(fā)布等功能。
3、高性能,高可用且支持集群。
4、提供了多種語(yǔ)言的 API
。
Redis 的安裝
1、下載對(duì)應(yīng)版本的安裝包,如:Redis 5.0.5 版本,其他版本也可以點(diǎn)擊這里進(jìn)行下載。
2、下載好之后傳到服務(wù)器指定目錄,執(zhí)行命令 tar -zxvf redis-5.0.5.tar.gz
進(jìn)行解壓。
3、解壓成功之后,進(jìn)入 Redis
主目錄,執(zhí)行命令 make && make install PREFIX=/xxx/xxx/redis-5.0.5
進(jìn)行安裝,如果不指定目錄,則默認(rèn)是安裝在 /usr/local
目錄下。
4、安裝成功之后可以看到 Redis
主目錄下多了一個(gè) bin
目錄,bin
目錄內(nèi)包含了一些可執(zhí)行腳本。
5、回到 Redis
主目錄下,找到 redis.conf
配置文件,將其中的配置 daemonize no
修改為 daemonize yes
,表示在后臺(tái)啟動(dòng)服務(wù)。
6、然后就可以執(zhí)行命令 /xxx/xxx/redis-5.0.5/bin/redis-server /xxx/xxx/redis-5.0.5/redis.conf
啟動(dòng) Redis
服務(wù)。 Redis 到底有多快
大家可能都知道 Redis
很快,可是 Redis
到底能有多快呢,比如 Redis
的吞吐量能達(dá)到多少?我想這就不是每一個(gè)人都能說(shuō)的上來(lái)一個(gè)具體的數(shù)字了。
Redis
官方提供了一個(gè)測(cè)試腳本,可以供我們測(cè)試 Redis
的 吞吐量。
redis-benchmark -q -n 100000
可以測(cè)試常用命令的吞吐量。 redis-benchmark -t set,lpush -n 100000 -q
測(cè)試 Redis
處理 set
和 lpush
命令的吞吐量。 redis-benchmark -n 100000 -q script load "redis.call('set','foo','bar')"
測(cè)試 Redis
處理 Lua
腳本等吞吐量。
下圖就是我這邊執(zhí)行第一條命令的自測(cè)結(jié)果,可以看到大部分命令的吞吐量都可以達(dá)到 4
萬(wàn)以上,也就是說(shuō)每秒鐘可以處理 4
萬(wàn)次以上請(qǐng)求:
但是如果你以為這就是 Redis
的真實(shí)吞吐量,那就錯(cuò)了。實(shí)際上,Redis
官方的測(cè)試結(jié)果是可以達(dá)到 10
萬(wàn)的吞吐量,下圖就是官方提供的一個(gè)基準(zhǔn)測(cè)試結(jié)果(縱坐標(biāo)就是吞吐量,橫坐標(biāo)是連接數(shù)):
Redis 是單線程還是多線程
這個(gè)問(wèn)題比較經(jīng)典,因?yàn)樵诤芏嗳说恼J(rèn)知里,Redis
就是單線程的。然而 Redis
從 4.0
版本開(kāi)始就有了多線程的概念,雖然處理命令請(qǐng)求的核心模塊確實(shí)是保證了單線程執(zhí)行,然而在其他許多地方已經(jīng)有了多線程,比如:在后臺(tái)刪除對(duì)象,通過(guò) Redis
模塊實(shí)現(xiàn)阻塞命令,生成 dump
文件,以及 6.0
版本中網(wǎng)絡(luò) I/O
實(shí)現(xiàn)了多線程等,而且在未來(lái) Redis
應(yīng)該會(huì)有越來(lái)越多的模塊實(shí)現(xiàn)多線程。
所謂的單線程,只是說(shuō) Redis
的處理客戶端的請(qǐng)求(即執(zhí)行命令)時(shí),是單線程去執(zhí)行的,并不是說(shuō)整個(gè) Redis
都是單線程。
Redis 為什么選擇使用單線程來(lái)執(zhí)行請(qǐng)求
Redis
為什么會(huì)選擇使用單線程呢?這是因?yàn)?CPU
成為 Redis
瓶頸的情況并不常見(jiàn),成為 Redis
瓶頸的通常是內(nèi)存或網(wǎng)絡(luò)帶寬。例如,在一個(gè)普通的 Linux
系統(tǒng)上使用 pipelining
命令,Redis
可以每秒完成 100
萬(wàn)個(gè)請(qǐng)求,所以如果我們的應(yīng)用程序主要使用 O(N)
或 O(log(N))
復(fù)雜度的命令,它幾乎不會(huì)使用太多的 CPU
。
那么既然 CPU
不會(huì)成為瓶頸,理所當(dāng)然的就沒(méi)必要去使用多線程來(lái)執(zhí)行命令,我們需要明確的一個(gè)問(wèn)題就是多線程一定比單線程快嗎?答案是不一定。因?yàn)槎嗑€程也是有代價(jià)的,最直接的兩個(gè)代價(jià)就是線程的創(chuàng)建和銷(xiāo)毀線程(當(dāng)然可以通過(guò)線程池來(lái)一定程度的減少頻繁的創(chuàng)建線程和銷(xiāo)毀線程)以及線程的上下文切換。
在我們的日常系統(tǒng)中,主要可以區(qū)分為兩種:CPU
密集型 和 IO
密集型。
CPU 密集型:
這種系統(tǒng)就說(shuō)明 CPU
的利用率很高,那么使用多線程反而會(huì)增加上下文切換而帶來(lái)額外的開(kāi)銷(xiāo),所以使用多線程效率可能會(huì)不升反降。舉個(gè)例子:假如你現(xiàn)在在干活,你一直不停的在做一件事,需要 1
分鐘可以做完,但是你中途總是被人打斷,需要花 1
秒鐘時(shí)間步行到旁邊去做另一件事,假如這件事也需要 1
分鐘,那么你因?yàn)榉磸?fù)切換做兩件事,每切換一次就要花 1
秒鐘,最后做完這 2
件事的時(shí)間肯定大于 2
分鐘(取決于中途切換的次數(shù)),但是如果中途不被打斷,你做完一件事再去做另一件事,那么你最多只需要切換 1
次,也就是 2
分 1
秒就能做完。
IO 密集型:
IO
操作也可以分為磁盤(pán) IO
和網(wǎng)絡(luò) IO
等操作。大部分 IO
操作的特點(diǎn)是比較耗時(shí)且 CPU
利用率不高,所以 Redis 6.0
版本網(wǎng)絡(luò) IO
會(huì)改進(jìn)為多線程。至于磁盤(pán) IO
,因?yàn)?Redis
中的數(shù)據(jù)都存儲(chǔ)在內(nèi)存(也可以持久化),所以并不會(huì)過(guò)多的涉及到磁盤(pán)操作。舉個(gè)例子:假如你現(xiàn)在給樹(shù)苗澆水,你每澆完一次水之后就需要等別人給你加水之后你才能繼續(xù)澆,那么假如這個(gè)等待過(guò)程需要 5
秒鐘,也就是說(shuō)你澆完一次水就可以休息 5
秒鐘,而你切換去做另一件事來(lái)回只需要 2
秒,那么你完全可以先去做另一件事,做完之后再回來(lái),這樣就可以充分利用你空閑的 5
秒鐘時(shí)間,從而提升了效率。
使用多線程還會(huì)帶來(lái)一個(gè)問(wèn)題就是數(shù)據(jù)的安全性,所以多線程編程都會(huì)涉及到鎖競(jìng)爭(zhēng),由此也會(huì)帶來(lái)額外的開(kāi)銷(xiāo)。
什么是 I/O 多路復(fù)用
I/O
指的是網(wǎng)絡(luò) I/O
, 多路指的是多個(gè) TCP
連接(如 Socket
),復(fù)用指的是復(fù)用一個(gè)或多個(gè)線程。I/O
多路復(fù)用的核心原理就是不再由應(yīng)用程序自己來(lái)監(jiān)聽(tīng)連接,而是由服務(wù)器內(nèi)核替應(yīng)用程序監(jiān)聽(tīng)。
在 Redis
中,其多路復(fù)用有多種實(shí)現(xiàn),如:select
,epoll
,evport
,kqueue
等。
我們用去餐廳吃飯為的例子來(lái)解釋一下 I/O
多路復(fù)用機(jī)制(點(diǎn)餐人相當(dāng)于客戶端,餐廳的廚房相當(dāng)于服務(wù)器,廚師就是線程)。
阻塞 IO
:張三去餐廳吃飯,點(diǎn)了一道菜,這時(shí)候他啥事也不干了,就是一直等,等到廚師炒好菜,他就把菜端走開(kāi)始吃飯了。也就是在菜被炒好之前,張三被阻塞了,這就是 BIO
(阻塞 IO
),效率會(huì)非常低下。
非阻塞 IO
:張三去餐廳吃飯,點(diǎn)了一道菜,這時(shí)候張三他不會(huì)一直等,找了個(gè)位置坐下,刷刷抖音,打打電話,做點(diǎn)其他事,然后每隔一段時(shí)間就去廚房問(wèn)一下自己的菜好了沒(méi)有。這種就屬于非阻塞 IO
,這種方式雖然可以提高性能,但是如果有大量 IO
都來(lái)定期輪詢,也會(huì)給服務(wù)器造成非常大的負(fù)擔(dān)。
事件驅(qū)動(dòng)機(jī)制:張三去餐廳吃飯,點(diǎn)了一道菜,這時(shí)候他找了個(gè)位置坐下來(lái)等: 廚房那邊菜做好了就會(huì)把菜端出來(lái)了,但是并不知道這道菜是誰(shuí)的,于是就挨個(gè)詢問(wèn)顧客,這就是多路復(fù)用中的 select
模型,不過(guò) select
模型最多只能監(jiān)聽(tīng) 1024
個(gè) socket
(poll
模型解決了這個(gè)限制問(wèn)題)。廚房做好了菜直接把菜放在窗口上,大喊一聲,某某菜做好了,是誰(shuí)的快過(guò)來(lái)拿,這時(shí)候聽(tīng)到通知的人就會(huì)自己去拿,這就是多路復(fù)用中的 epoll
模型。
需要注意的是在 IO
多路復(fù)用機(jī)制下,客戶端可以阻塞也可以選擇不阻塞(大部分場(chǎng)景下是阻塞 IO
),這個(gè)要具體情況具體分析,但是在多路復(fù)用機(jī)制下,服務(wù)端就可以通過(guò)多線程(上面示例中可以多幾個(gè)廚師同時(shí)炒菜)來(lái)提升并發(fā)效率。
Redis 中 I/O 多路復(fù)用的應(yīng)用
Redis
服務(wù)器是一個(gè)事件驅(qū)動(dòng)程序,服務(wù)器需要處理兩類(lèi)事件:文件事件和時(shí)間事件。
文件事件:Redis
服務(wù)器和客戶端(或其他服務(wù)器)進(jìn)行通信會(huì)產(chǎn)生相應(yīng)的文件事件,然后服務(wù)器通過(guò)監(jiān)聽(tīng)并處理這些事件來(lái)完成一系列的通信操作。
時(shí)間事件:Redis
內(nèi)部的一些在給定時(shí)間之內(nèi)需要進(jìn)行的操作。
Redis
的文件事件處理器以單線程的方式運(yùn)行,其內(nèi)部使用了 I/O
多路復(fù)用程序來(lái)同時(shí)監(jiān)聽(tīng)多個(gè)套接字(Socket
)連接,提升了性能的同時(shí)又保持了內(nèi)部單線程設(shè)計(jì)的簡(jiǎn)單性。下圖就是文件事件處理器的示意圖:
I/O
多路復(fù)用程序雖然會(huì)同時(shí)監(jiān)聽(tīng)多個(gè) Socket
連接,但是其會(huì)將監(jiān)聽(tīng)的 Socket
都放到一個(gè)隊(duì)列里面,然后通過(guò)這個(gè)隊(duì)列有序的,同步的將每個(gè) Socket
對(duì)應(yīng)的事件傳送給文件事件分派器,再由文件事件分派器分派給對(duì)應(yīng)的事件處理器進(jìn)行處理,只有當(dāng)一個(gè) Socket
所對(duì)應(yīng)的事件被處理完畢之后,I/O
多路復(fù)用程序才會(huì)繼續(xù)向文件事件分派器傳送下一個(gè) Socket
所對(duì)應(yīng)的事件,這也可以驗(yàn)證上面的結(jié)論,處理客戶端的命令請(qǐng)求是單線程的方式逐個(gè)處理,但是事件處理器內(nèi)并不是只有一個(gè)線程。
Redis 為什么這么快
Redis
為什么這么快的原因前面已經(jīng)基本提到了,現(xiàn)在我們?cè)龠M(jìn)行總結(jié)一下:
1、Redis
是一款純內(nèi)存結(jié)構(gòu),避免了磁盤(pán) I/O
等耗時(shí)操作。
2、Redis
命令處理的核心模塊為單線程,減少了鎖競(jìng)爭(zhēng),以及頻繁創(chuàng)建線程和銷(xiāo)毀線程的代價(jià),減少了線程上下文切換的消耗。
3、采用了 I/O
多路復(fù)用機(jī)制,大大提升了并發(fā)效率。
到此這篇關(guān)于Redis憑啥可以這么快的文章就介紹到這了,更多相關(guān)Redis為什么快內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文搞懂Redis中的慢查詢?nèi)罩竞捅O(jiān)視器
我們都知道MySQL有慢查詢?nèi)罩?但Redis也有慢查詢?nèi)罩?可用于監(jiān)視和優(yōu)化查詢,本文給大家詳細(xì)介紹了Redis中的慢查詢?nèi)罩竞捅O(jiān)視器,文章通過(guò)代碼示例講解的非常詳細(xì),需要的朋友可以參考下2024-04-04window手動(dòng)操作清理redis緩存的技巧總結(jié)
在本篇文章中小編給大家分享了關(guān)于window環(huán)境手動(dòng)操作清理redis緩存的方法和技巧,有興趣的朋友們可以跟著學(xué)習(xí)下。2019-07-07深入理解Redis內(nèi)存回收和內(nèi)存淘汰機(jī)制
Redis使用多種過(guò)期策略和內(nèi)存淘汰機(jī)制來(lái)管理內(nèi)存,本文主要介紹了深入理解Redis內(nèi)存回收和內(nèi)存淘汰機(jī)制, 具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06Django使用Redis進(jìn)行緩存詳細(xì)步驟
這篇文章主要介紹了Django使用Redis進(jìn)行緩存詳細(xì)流程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08