redis為什么這么快?

1 redis的數(shù)據(jù)時(shí)存儲(chǔ)在內(nèi)存中

讀取的時(shí)候?qū)儆诩儍?nèi)存操作,不需要進(jìn)行磁盤的io,時(shí)間復(fù)雜度O(1)

要實(shí)現(xiàn)高的并發(fā)性能,redis是不是要?jiǎng)?chuàng)建非常多的線程呢,恰恰相反,redis是單線程的。

redis為什么是單線程的?
官方解釋說,因?yàn)閱尉€程已經(jīng)夠用了,CPU 不是 redis 的瓶頸。Redis 的瓶頸最有可能是機(jī)器內(nèi)存或者網(wǎng)絡(luò)帶寬。

單線程為什么這么快?
因?yàn)閞edis是基于內(nèi)存操作的。

2 redis是單線程的

單線程有如下好處:
不需要頻繁創(chuàng)建和銷毀線程
單線程保證了系統(tǒng)沒有線程的上下文切換
避免線程之間的資源競(jìng)爭(zhēng),比如加鎖釋放鎖死鎖等

3 異步非阻塞IO,多路復(fù)用處理并發(fā)連接

傳統(tǒng) I/O 數(shù)據(jù)拷貝

以讀操作為例:當(dāng)應(yīng)用程序執(zhí)行 read 系統(tǒng)調(diào)用讀取文件描述符(FD)的時(shí)候,如果這塊數(shù)據(jù)已經(jīng)存在于用戶進(jìn)程的頁內(nèi)存中,就直接從內(nèi)存中讀取數(shù)據(jù)。如果數(shù)據(jù)不存在,則先將數(shù)據(jù)從磁盤加載數(shù)據(jù)到內(nèi)核緩沖區(qū)中,再?gòu)膬?nèi)核緩沖區(qū)拷貝到用戶進(jìn)程的頁內(nèi)存中。(兩次拷貝,兩次 user 和 kernel 的上下文切換)。

I/O 的阻塞到底阻塞在哪里?

當(dāng)使用 read 或 write 對(duì)某個(gè)文件描述符進(jìn)行過讀寫時(shí),如果當(dāng)前 FD 不可讀,系統(tǒng)就不會(huì)對(duì)其他的操作做出響應(yīng)。從設(shè)備復(fù)制數(shù)據(jù)到內(nèi)核緩沖區(qū)是阻塞的,從內(nèi)核緩沖區(qū)拷貝到用戶空間,也是阻塞的,直到 copy complete,內(nèi)核返回結(jié)果,用戶進(jìn)程才解除block 的狀態(tài)。

為了解決阻塞的問題,我們有幾個(gè)思路。

  1. 在服務(wù)端創(chuàng)建多個(gè)線程或者使用線程池,但是在高并發(fā)的情況下需要的線程會(huì)很多,系統(tǒng)無法承受,而且創(chuàng)建和釋放線程都需要消耗資源。
  2. 由請(qǐng)求方定期輪詢,在數(shù)據(jù)準(zhǔn)備完畢后再?gòu)膬?nèi)核緩存緩沖區(qū)復(fù)制數(shù)據(jù)到用戶空間(非阻塞式 I/O),這種方式會(huì)存在一定的延遲。

能不能用一個(gè)線程處理多個(gè)客戶端請(qǐng)求?

I/O 多路復(fù)用(I/O Multiplexing)

  • I/O 指的是網(wǎng)絡(luò) I/O。
  • 多路指的是多個(gè) TCP 連接(Socket 或 Channel)。
  • 復(fù)用指的是復(fù)用一個(gè)或多個(gè)線程。

它的基本原理就是不再由應(yīng)用程序自己監(jiān)視連接,而是由內(nèi)核替應(yīng)用程序監(jiān)視文件描述符。

客戶端在操作的時(shí)候,會(huì)產(chǎn)生具有不同事件類型的 socket。在服務(wù)端,I/O 多路復(fù)用程序(I/O Multiplexing Module)會(huì)把消息放入隊(duì)列中,然后通過文件事件分派器(File event Dispatcher),轉(zhuǎn)發(fā)到不同的事件處理器中。

多路復(fù)用有很多的實(shí)現(xiàn),以 select 為例,當(dāng)用戶進(jìn)程調(diào)用了多路復(fù)用器,進(jìn)程會(huì)被阻塞。內(nèi)核會(huì)監(jiān)視多路復(fù)用器負(fù)責(zé)的所有 socket,當(dāng)任何一個(gè) socket 的數(shù)據(jù)準(zhǔn)備好了,多路復(fù)用器就會(huì)返回。這時(shí)候用戶進(jìn)程再調(diào)用 read 操作,把數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到用戶空間。

所以,I/O 多路復(fù)用的特點(diǎn)是通過一種機(jī)制一個(gè)進(jìn)程能同時(shí)等待多個(gè)文件描述符,而這些文件描述符(套接字描述符)其中的任意一個(gè)進(jìn)入讀就緒(readable)狀態(tài),select()函數(shù)就可以返回。

4 高效的數(shù)據(jù)結(jié)構(gòu),合理的數(shù)據(jù)編碼

在 Redis 中,常用的 5 種數(shù)據(jù)結(jié)構(gòu)和應(yīng)用場(chǎng)景如下:

String:緩存、計(jì)數(shù)器、分布式鎖等。
List:鏈表、隊(duì)列、微博關(guān)注人時(shí)間軸列表等。
Hash:用戶信息、Hash 表等。
Set:去重、贊、踩、共同好友等。
Zset:訪問量排行榜、點(diǎn)擊量排行榜等。

具體數(shù)據(jù)結(jié)構(gòu)如何體現(xiàn)出高效,數(shù)據(jù)編碼又如何體現(xiàn)出合理,此處先留個(gè)坑,有待后面進(jìn)行填上。

5 過期數(shù)據(jù)的刪除對(duì) Redis 性能影響

當(dāng)我們對(duì)某些 key 設(shè)置了 expire 時(shí),數(shù)據(jù)到了時(shí)間會(huì)自動(dòng)刪除。如果一個(gè)鍵過期了,它會(huì)在什么時(shí)候刪除呢?

下面介紹三種刪除策略:

定時(shí)刪除:在這是鍵的過期時(shí)間的同時(shí),創(chuàng)建一個(gè)定時(shí)器 Timer,讓定時(shí)器在鍵過期時(shí)間來臨時(shí)立即執(zhí)行對(duì)過期鍵的刪除。
惰性刪除:鍵過期后不管,每次讀取該鍵時(shí),判斷該鍵是否過期,如果過期刪除該鍵返回空。
定期刪除:每隔一段時(shí)間對(duì)數(shù)據(jù)庫(kù)中的過期鍵進(jìn)行一次檢查。
定時(shí)刪除:對(duì)內(nèi)存友好,對(duì) CPU 不友好。如果過期刪除的鍵比較多的時(shí)候,刪除鍵這一行為會(huì)占用相當(dāng)一部分 CPU 性能,會(huì)對(duì) Redis 的吞吐量造成一定影響。

惰性刪除:對(duì) CPU 友好,內(nèi)存不友好。如果很多鍵過期了,但在將來很長(zhǎng)一段時(shí)間內(nèi)沒有很多客戶端訪問該鍵導(dǎo)致過期鍵不會(huì)被刪除,占用大量?jī)?nèi)存空間。

定期刪除:是定時(shí)刪除和惰性刪除的一種折中。每隔一段時(shí)間執(zhí)行一次刪除過期鍵的操作,并且限制刪除操作執(zhí)行的時(shí)長(zhǎng)和頻率。

具體的操作如下:

Redis 會(huì)將每一個(gè)設(shè)置了 expire 的鍵存儲(chǔ)在一個(gè)獨(dú)立的字典中,以后會(huì)定時(shí)遍歷這個(gè)字典來刪除過期的 key。除了定時(shí)遍歷外,它還會(huì)使用惰性刪除策略來刪除過期的 key。
Redis 默認(rèn)每秒進(jìn)行十次過期掃描,過期掃描不會(huì)掃描所有過期字典中的 key,而是采用了一種簡(jiǎn)單的貪心策略。
從過期字典中隨機(jī)選擇 20 個(gè) key;刪除這 20 個(gè) key 中已過期的 key;如果過期 key 比例超過 1/4,那就重復(fù)步驟 1。
同時(shí),為了保證在過期掃描期間不會(huì)出現(xiàn)過度循環(huán),導(dǎo)致線程卡死,算法還增加了掃描時(shí)間上限,默認(rèn)不會(huì)超過 25ms。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容