最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis網(wǎng)絡(luò)I/O模型的使用及說(shuō)明

 更新時(shí)間:2025年12月24日 08:39:30   作者:姓蔡小朋友  
文章詳細(xì)介紹了Redis的網(wǎng)絡(luò)模型,包括單線程和多線程模型,以及I/O多路復(fù)用、事件通知機(jī)制、信號(hào)驅(qū)動(dòng)I/O和異步I/O等技術(shù),同時(shí)簡(jiǎn)要介紹了Redis的通信協(xié)議RESP

聲明:這里的I/O表示redis從網(wǎng)卡讀來(lái)自客戶端的數(shù)據(jù)(包括指令、數(shù)據(jù))

一、阻塞I/O

阻塞I/O讀取數(shù)據(jù)過(guò)程:

  • 應(yīng)用程序(服務(wù)器)接收到客戶端的三次握手,通過(guò)accept()建立連接,得到一個(gè)socket文件描述符,用于與客戶端通信。
  • 應(yīng)用程序進(jìn)程調(diào)用read()函數(shù),觸發(fā)系統(tǒng)調(diào)用,進(jìn)程從用戶態(tài)切換到內(nèi)核態(tài),自動(dòng)執(zhí)行內(nèi)核中的系統(tǒng)調(diào)用處理函數(shù) sys_read()。
  • 內(nèi)核態(tài)的該進(jìn)程檢查客戶端的數(shù)據(jù)是否已經(jīng)在內(nèi)核空間的緩存中(第一次進(jìn)入內(nèi)核態(tài)肯定不在),如果數(shù)據(jù)在緩存中,直接拷貝到用戶空間,然后切換回用戶態(tài)。
  • 如果未命中說(shuō)明客戶端發(fā)送的數(shù)據(jù)尚未到達(dá),進(jìn)程進(jìn)入阻塞隊(duì)列讓出CPU,持續(xù)等待客戶端數(shù)據(jù)。
  • 客戶端發(fā)起HTTP請(qǐng)求,并攜帶數(shù)據(jù)。
  • 客戶端發(fā)送的網(wǎng)絡(luò)數(shù)據(jù)包到達(dá)服務(wù)器網(wǎng)卡,網(wǎng)卡通過(guò)DMA將數(shù)據(jù)包寫(xiě)入內(nèi)核緩沖區(qū),然后觸發(fā)硬件中斷,搶占CPU,執(zhí)行中斷處理程序。
  • 中斷處理程序將數(shù)據(jù)進(jìn)行網(wǎng)絡(luò)協(xié)議棧的處理(如IP、TCP處理),最終將數(shù)據(jù)放入對(duì)應(yīng)socket的接收緩沖區(qū),CPU會(huì)返回到被中斷的地方繼續(xù)執(zhí)行,喚醒等待在該socket上的進(jìn)程,進(jìn)入就緒態(tài)
  • 當(dāng)該進(jìn)程被調(diào)度器選中再次運(yùn)行時(shí),它從之前阻塞的地方繼續(xù)執(zhí)行,此時(shí)數(shù)據(jù)已經(jīng)在內(nèi)核緩存中,于是將數(shù)據(jù)從內(nèi)核緩存拷貝到socket的用戶空間
  • 系統(tǒng)調(diào)用返回,進(jìn)程從內(nèi)核態(tài)切換回用戶態(tài),并繼續(xù)執(zhí)行用戶態(tài)代碼。
  • 處理完成后,進(jìn)程重新進(jìn)入內(nèi)核態(tài)阻塞。

對(duì)于單線程I/O模型,服務(wù)器只有一個(gè)線程用來(lái)監(jiān)聽(tīng)所有客戶端,線程每次調(diào)用recvfrom系統(tǒng)調(diào)用只能監(jiān)聽(tīng)一個(gè)客戶端的數(shù)據(jù),如果該客戶端沒(méi)有數(shù)據(jù)會(huì)一直阻塞直到該客戶端的數(shù)據(jù)到達(dá)內(nèi)核緩沖區(qū),無(wú)法處理其他客戶端早已寫(xiě)入內(nèi)核緩沖區(qū)的數(shù)據(jù),性能差。

二、非阻塞I/O

非阻塞I/O相較于阻塞I/O優(yōu)勢(shì)在于不需要阻塞在某一個(gè)客戶端,而是可以輪詢所有客戶端的狀態(tài),當(dāng)某一客戶端的數(shù)據(jù)可用會(huì)調(diào)用recvfrom讀數(shù)據(jù)到用戶空間,并處理該數(shù)據(jù)。

非阻塞I/O讀取過(guò)程:和阻塞I/O的主要區(qū)別在步驟4和8

  • 應(yīng)用程序(服務(wù)器)接收到客戶端的三次握手,通過(guò)accept()建立連接,得到一個(gè)socket文件描述符,用于與客戶端通信。
  • 應(yīng)用程序進(jìn)程調(diào)用read()函數(shù),觸發(fā)系統(tǒng)調(diào)用,進(jìn)程從用戶態(tài)切換到內(nèi)核態(tài),自動(dòng)執(zhí)行內(nèi)核中的系統(tǒng)調(diào)用處理函數(shù) sys_read()。
  • 內(nèi)核態(tài)的該進(jìn)程檢查客戶端的數(shù)據(jù)是否已經(jīng)在內(nèi)核空間的緩存中,如果數(shù)據(jù)在緩存中,直接拷貝到用戶空間,然后切換回用戶態(tài)。
  • 如果未命中說(shuō)明客戶端發(fā)送的數(shù)據(jù)尚未到達(dá),進(jìn)程返回用戶態(tài)并輪詢1~4步,此時(shí)進(jìn)程不會(huì)阻塞,也不會(huì)讓出CPU
  • 客戶端發(fā)起HTTP請(qǐng)求,并攜帶數(shù)據(jù)。
  • 客戶端發(fā)送的網(wǎng)絡(luò)數(shù)據(jù)包到達(dá)服務(wù)器網(wǎng)卡,網(wǎng)卡通過(guò)DMA將數(shù)據(jù)包寫(xiě)入內(nèi)核緩沖區(qū),然后觸發(fā)硬件中斷,搶占CPU,執(zhí)行中斷處理程序。
  • 中斷處理程序將數(shù)據(jù)進(jìn)行網(wǎng)絡(luò)協(xié)議棧的處理(如IP、TCP處理),最終將數(shù)據(jù)放入對(duì)應(yīng)socket的接收緩沖區(qū),CPU會(huì)返回到被中斷的地方繼續(xù)執(zhí)行,喚醒等待在該socket上的進(jìn)程,進(jìn)入就緒態(tài)。
  • 進(jìn)程輪詢調(diào)用read()時(shí),進(jìn)程從用戶態(tài)切換到內(nèi)核態(tài),此時(shí)數(shù)據(jù)已經(jīng)在內(nèi)核緩存中,于是將數(shù)據(jù)從內(nèi)核緩存拷貝到socket的用戶空間。
  • 系統(tǒng)調(diào)用返回,進(jìn)程從內(nèi)核態(tài)切換回用戶態(tài),并繼續(xù)執(zhí)行用戶態(tài)代碼。
  • 處理完成后,進(jìn)程重新進(jìn)入內(nèi)核態(tài)阻塞。

非阻塞I/O雖然不需要阻塞等待某一客戶端的請(qǐng)求,并且可以同時(shí)while輪詢監(jiān)聽(tīng)多個(gè)客戶端的請(qǐng)求,但是while輪詢會(huì)查詢所有客戶端的數(shù)據(jù)是否到達(dá),線程一直占用CPU,導(dǎo)致CPU利用率低,且輪詢所有客戶端所以性能也較差。

三、I/O多路復(fù)用*

I/O多路復(fù)用是利用單個(gè)線程同時(shí)監(jiān)聽(tīng)多個(gè)文件描述符(每個(gè)文件描述符對(duì)應(yīng)一個(gè)socket,也就是客戶端),也就是說(shuō)單線程可以 同時(shí) 監(jiān)聽(tīng)多個(gè)socket的網(wǎng)絡(luò)I/O請(qǐng)求。與阻塞I/O不同的是,I/O多路復(fù)用使用的是select、poll、epoll系統(tǒng)調(diào)用函數(shù),該函數(shù)在阻塞狀態(tài)下可以同時(shí)監(jiān)聽(tīng)多個(gè)socket[監(jiān)聽(tīng)socket、客戶端socket],當(dāng)某一個(gè)socket的數(shù)據(jù)到達(dá)時(shí),就會(huì)喚醒阻塞線程回到用戶態(tài),線程調(diào)用recvfrom來(lái)讀取已經(jīng)到達(dá)的數(shù)據(jù)到用戶空間并處理。與非阻塞I/O不同的是,當(dāng)沒(méi)有數(shù)據(jù)可到達(dá)時(shí)線程會(huì)阻塞并讓出CPU。

1.select系統(tǒng)調(diào)用

select為讀請(qǐng)求、寫(xiě)請(qǐng)求、異常事件創(chuàng)建三個(gè)獨(dú)立的數(shù)組,每個(gè)數(shù)組有32個(gè)元素,每個(gè)元素占32bit,使用bit位作為標(biāo)記,因此每個(gè)數(shù)組可監(jiān)聽(tīng)1024個(gè)請(qǐng)求,其中請(qǐng)求來(lái)自不同的線程。通過(guò)timeout設(shè)定select阻塞等待的超時(shí)時(shí)間。

單線程下select I/O多路復(fù)用讀取網(wǎng)絡(luò)數(shù)據(jù)過(guò)程:

  • 單線程(服務(wù)器)創(chuàng)建監(jiān)聽(tīng)socket,用來(lái)監(jiān)聽(tīng)新客戶端的連接請(qǐng)求,將監(jiān)聽(tīng)socket的fd記錄到read數(shù)組中的某個(gè)bit位上。
  • 單線程調(diào)用select()函數(shù),觸發(fā)系統(tǒng)調(diào)用,進(jìn)程從用戶態(tài)切換到內(nèi)核態(tài),并將三個(gè)數(shù)組拷貝到內(nèi)核態(tài)。
  • 內(nèi)核態(tài)的該線程檢查緩存中是否有數(shù)據(jù),如果有,判斷數(shù)據(jù)是哪個(gè)fd的,并判斷是讀請(qǐng)求、寫(xiě)請(qǐng)求還是出現(xiàn)異常,將fd對(duì)應(yīng)數(shù)組位置的bit置0,切換回用戶態(tài),并將修改后的數(shù)組拷貝到用戶態(tài)。
  • 如果未命中說(shuō)明沒(méi)有客戶端發(fā)送數(shù)據(jù),線程進(jìn)入阻塞隊(duì)列讓出CPU,持續(xù)監(jiān)聽(tīng)客戶端數(shù)據(jù)。
  • 如果阻塞時(shí)間超過(guò)了timeout,那么喚醒該線程,回到用戶態(tài)。
  • 客戶端A或B發(fā)起HTTP請(qǐng)求,并攜帶數(shù)據(jù)。
  • 客戶端A或B發(fā)送的網(wǎng)絡(luò)數(shù)據(jù)包到達(dá)服務(wù)器網(wǎng)卡,網(wǎng)卡通過(guò)DMA將數(shù)據(jù)包寫(xiě)入內(nèi)核緩沖區(qū),然后觸發(fā)硬件中斷,搶占CPU,執(zhí)行中斷處理程序。
  • 中斷處理程序將數(shù)據(jù)進(jìn)行網(wǎng)絡(luò)協(xié)議棧的處理(如IP、TCP處理),最終將數(shù)據(jù)放入對(duì)應(yīng)socket的接收緩沖區(qū),修改對(duì)應(yīng)socket的三個(gè)數(shù)組,CPU會(huì)返回到被中斷的地方繼續(xù)執(zhí)行,喚醒單線程,進(jìn)入就緒態(tài)
  • 當(dāng)該線程被調(diào)度器選中再次運(yùn)行時(shí),回到用戶態(tài),并將修改后的數(shù)組拷貝到用戶態(tài)。
  • 線程遍歷三個(gè)數(shù)組篩選出哪些客戶端的數(shù)據(jù)已經(jīng)在內(nèi)核空間的緩存中,記為t[fd1,fd3…]。
  • 如果t[]中有監(jiān)聽(tīng)socket的fd,說(shuō)明有新的客戶端請(qǐng)求連接,那么單線程調(diào)用accept()系統(tǒng)調(diào)用與客戶端建立連接,得到一個(gè)客戶端socket文件描述符fd,用于與該客戶端通信,將fd添加到三個(gè)數(shù)組的某個(gè)bit位上。
  • t[]中除了監(jiān)聽(tīng)socket之外的其他fd說(shuō)明是已建立連接的客戶端發(fā)送的數(shù)據(jù),單線程對(duì)t[]調(diào)用read()函數(shù),進(jìn)程從用戶態(tài)切換到內(nèi)核態(tài),此時(shí)t[]的數(shù)據(jù)一定在內(nèi)核緩存中,直接拷貝到對(duì)應(yīng)fd的用戶空間,然后切換回用戶態(tài)。
  • 系統(tǒng)調(diào)用返回,線程從內(nèi)核態(tài)切換回用戶態(tài),并繼續(xù)執(zhí)行用戶態(tài)代碼。
  • 處理完成后,線程傳入添加新客戶端fd后的三個(gè)數(shù)組重新進(jìn)入內(nèi)核態(tài)阻塞監(jiān)聽(tīng)。

select缺點(diǎn):

  • fd_set由于可變,需要頻繁的修改用戶空間和內(nèi)核空間中的fd_set
  • 在用戶態(tài)需要遍歷原fd_set和修改后的fd_set才能知道那些數(shù)據(jù)就緒
  • 監(jiān)聽(tīng)數(shù)量不超過(guò)1024

2.poll系統(tǒng)調(diào)用

poll仍然使用數(shù)組的方式監(jiān)聽(tīng)不同的請(qǐng)求,但數(shù)組中每個(gè)元素都是一個(gè)結(jié)構(gòu)體,記錄了請(qǐng)求的文件描述符fd(socket、客戶端)、請(qǐng)求類型events、返回值revents。單線程在內(nèi)核中監(jiān)聽(tīng)時(shí)(等待中斷響應(yīng)),在超時(shí)時(shí)間內(nèi)若監(jiān)聽(tīng)到某個(gè)文件描述符的中斷響應(yīng),就將響應(yīng)類型記錄到revents中,否則revents置0表示未收到相應(yīng)。

單線程下poll I/O多路復(fù)用讀取網(wǎng)絡(luò)數(shù)據(jù)過(guò)程:

  • 監(jiān)聽(tīng)socket接收到客戶端A的三次握手,通過(guò)accept()建立連接,得到一個(gè)socket文件描述符,將文件描述符、要監(jiān)聽(tīng)的事件類型記錄到polled數(shù)組中。
  • 單線程調(diào)用poll()函數(shù),觸發(fā)系統(tǒng)調(diào)用,進(jìn)程從用戶態(tài)切換到內(nèi)核態(tài),并將polled數(shù)組拷貝到內(nèi)核態(tài)。
  • 內(nèi)核態(tài)的該線程檢查緩存中是否有數(shù)據(jù),如果有,判斷數(shù)據(jù)是哪個(gè)fd的,并判斷是讀請(qǐng)求、寫(xiě)請(qǐng)求還是出現(xiàn)異常,將fd對(duì)應(yīng)結(jié)構(gòu)體的revents置為相應(yīng)值,切換回用戶態(tài),并將修改后的數(shù)組拷貝到用戶態(tài),返回就緒文件描述符數(shù)量n
  • 線程判斷n是否大于0,大于0則遍歷polled數(shù)組,找到就緒的文件描述符。
  • …同select

3.epoll系統(tǒng)調(diào)用

epoll維護(hù)一棵紅黑樹(shù)和一個(gè)鏈表,紅黑樹(shù)記錄要監(jiān)聽(tīng)的文件描述符fd,就緒鏈表記錄已就緒的文件描述符fd。與select最大的不同在于,epoll在內(nèi)核態(tài)初始化epoll_create系統(tǒng)調(diào)用),不會(huì)頻繁的在用戶態(tài)和內(nèi)核態(tài)拷貝。

由于在內(nèi)核態(tài),所以當(dāng)線程通過(guò)accept()與客戶端建立連接后,需要通過(guò)epoll_ctl()系統(tǒng)調(diào)用向epoll的紅黑樹(shù)中添加fd。此外,添加時(shí)會(huì)對(duì)fd設(shè)置一個(gè)回調(diào)函數(shù),回調(diào)函數(shù)會(huì)在fd的數(shù)據(jù)寫(xiě)入內(nèi)核緩存(觸發(fā)中斷響應(yīng))時(shí)將fd添加到就緒鏈表中。

線程使用epoll_wait()系統(tǒng)調(diào)用持續(xù)監(jiān)聽(tīng)就緒鏈表,若在超時(shí)時(shí)間內(nèi)有fd加入到就緒鏈表中那么喚醒該線程,將就緒的fd拷貝到用戶空間

epoll方案很好的解決了select的三個(gè)問(wèn)題。

單線程下epoll I/O多路復(fù)用讀取網(wǎng)絡(luò)數(shù)據(jù)過(guò)程:

  • 單線程調(diào)用epoll_create系統(tǒng)調(diào)用進(jìn)入內(nèi)核態(tài),在內(nèi)核態(tài)初始化一棵紅黑樹(shù)和一個(gè)鏈表,回到用戶態(tài)。
  • 單線程(服務(wù)器)創(chuàng)建監(jiān)聽(tīng)socket,用來(lái)監(jiān)聽(tīng)新客戶端的連接請(qǐng)求。
  • 單線程調(diào)用epoll_ctl()系統(tǒng)調(diào)用進(jìn)入內(nèi)核態(tài),向紅黑樹(shù)中添加監(jiān)聽(tīng)socket的fd,回到用戶態(tài)。
  • 單線程調(diào)用epoll_wait()函數(shù),觸發(fā)系統(tǒng)調(diào)用,進(jìn)程從用戶態(tài)切換到內(nèi)核態(tài)。
  • 內(nèi)核態(tài)的該線程檢查就緒鏈表中是否有fd,如果有,切換回用戶態(tài),并將就緒鏈表拷貝到用戶態(tài)。
  • 如果未命中說(shuō)明沒(méi)有客戶端發(fā)送數(shù)據(jù),線程進(jìn)入阻塞隊(duì)列讓出CPU,持續(xù)等待客戶端數(shù)據(jù)。
  • 如果阻塞時(shí)間超過(guò)了timeout,那么喚醒該線程,回到用戶態(tài)。
  • 客戶端A或B發(fā)起HTTP請(qǐng)求,并攜帶數(shù)據(jù)。
  • 客戶端A或B發(fā)送的網(wǎng)絡(luò)數(shù)據(jù)包到達(dá)服務(wù)器網(wǎng)卡,網(wǎng)卡通過(guò)DMA將數(shù)據(jù)包寫(xiě)入內(nèi)核緩沖區(qū),然后觸發(fā)硬件中斷,搶占CPU,執(zhí)行中斷處理程序
  • 中斷處理程序將數(shù)據(jù)進(jìn)行網(wǎng)絡(luò)協(xié)議棧的處理(如IP、TCP處理),最終將數(shù)據(jù)放入對(duì)應(yīng)socket的接收緩沖區(qū),CPU會(huì)返回到被中斷的地方繼續(xù)執(zhí)行,觸發(fā)回調(diào)函數(shù)將數(shù)據(jù)對(duì)應(yīng)的socket(fd)加入就緒鏈表,喚醒等待在該socket上的線程,進(jìn)入就緒態(tài)
  • 當(dāng)該線程被調(diào)度器選中再次運(yùn)行時(shí),切換回用戶態(tài),并將就緒鏈表拷貝到用戶態(tài)。
  • 如果就緒鏈表中有監(jiān)聽(tīng)socket的fd,說(shuō)明有新的客戶端請(qǐng)求連接,那么單線程調(diào)用accept()系統(tǒng)調(diào)用與客戶端建立連接,得到一個(gè)客戶端socket文件描述符fd,用于與該客戶端通信,調(diào)用epoll_ctl()系統(tǒng)調(diào)用向紅黑樹(shù)中添加新客戶端socket的fd,回到用戶態(tài)。
  • 就緒鏈表中除了監(jiān)聽(tīng)socket之外的其他fd說(shuō)明是已建立連接的客戶端發(fā)送的數(shù)據(jù),單線程對(duì)就緒鏈表中的fd調(diào)用read()函數(shù),進(jìn)程從用戶態(tài)切換到內(nèi)核態(tài),此時(shí)就緒鏈表中fd的數(shù)據(jù)一定在內(nèi)核緩存中,直接拷貝到對(duì)應(yīng)fd的用戶空間,然后切換回用戶態(tài)。
  • 系統(tǒng)調(diào)用返回,線程從內(nèi)核態(tài)切換回用戶態(tài),并繼續(xù)執(zhí)行用戶態(tài)代碼。
  • 線程解析并處理命令,然后將響應(yīng)結(jié)果寫(xiě)入客戶端socket(fd)的發(fā)送緩沖區(qū),調(diào)用write或send系統(tǒng)調(diào)用將響應(yīng)結(jié)果通過(guò)DMA從內(nèi)核的發(fā)送緩沖區(qū)由網(wǎng)卡發(fā)送出去。
  • 處理完成后,重新進(jìn)入內(nèi)核態(tài)阻塞。

思考:有沒(méi)有可能連接請(qǐng)求還沒(méi)有處理完,沒(méi)有為客戶端socket分配內(nèi)核緩沖區(qū),客戶端的讀請(qǐng)求就來(lái)了?

應(yīng)該不會(huì),這個(gè)連接請(qǐng)求應(yīng)該就是TCP三次握手,只有建立連接了,服務(wù)器向客戶端發(fā)送ACK,客戶端才能發(fā)讀請(qǐng)求到服務(wù)器。

事件通知機(jī)制:

LevelTriggered:LT,每次將就緒隊(duì)列中的fd拷貝到用戶空間時(shí)保留就緒隊(duì)列中的fd。

  • 可以實(shí)現(xiàn)重復(fù)讀取,適用于fd一次讀不全的情況。

EdgeTriggered:ET,每次將就緒隊(duì)列中的fd拷貝到用戶空間時(shí)清空就緒隊(duì)列。

  • 適用于一次性讀取fd的情況。
  • 可以通過(guò)epoll_ctl函數(shù)的修改功能手動(dòng)將fd添加回就緒鏈表實(shí)現(xiàn)LT的效果

不太理解為什么用LT,每次都從網(wǎng)絡(luò)讀到用戶內(nèi)存就算一次讀不全早晚也能讀完吧,沒(méi)必要每次都重復(fù)讀吧,可能是因?yàn)閿?shù)據(jù)在對(duì)應(yīng)用戶空間中不連續(xù)使用起來(lái)麻煩。

四、信號(hào)驅(qū)動(dòng)I/O

信號(hào)驅(qū)動(dòng)I/O通過(guò)sigaction()系統(tǒng)調(diào)用對(duì)fd設(shè)置回調(diào)函數(shù),此時(shí)線程立即返回用戶態(tài)執(zhí)行其他任務(wù)。當(dāng)fd的數(shù)據(jù)到達(dá)內(nèi)核緩存時(shí)會(huì)觸發(fā)回調(diào)函數(shù),將就緒的fd拷貝到用戶態(tài)的信號(hào)隊(duì)列通知線程。線程調(diào)用recvfrom進(jìn)入內(nèi)核態(tài)將數(shù)據(jù)拷貝到用戶內(nèi)存。

與非阻塞I/O不同的是,線程返回用戶態(tài)后不會(huì)輪詢數(shù)據(jù)是否準(zhǔn)備好,而是去執(zhí)行其他任務(wù),收到來(lái)自內(nèi)核的通知才調(diào)用read讀數(shù)據(jù)。

由于信號(hào)驅(qū)動(dòng)I/O下每有一個(gè)fd就緒內(nèi)核線程都會(huì)執(zhí)行內(nèi)核態(tài)與用戶態(tài)切換通知用戶線程,影響性能;而多路復(fù)用I/O可以一次性獲取多個(gè)就緒的fd才切換到用戶態(tài),性能更好(多路復(fù)用I/O中雖然每次fd到達(dá)就緒鏈表都會(huì)喚醒線程,但是線程獲得CPU前仍然可以有fd進(jìn)入就緒鏈表,且調(diào)用wait()進(jìn)入內(nèi)核態(tài)時(shí)如果就緒鏈表有多個(gè)fd也可以一次性返回)。因此多路復(fù)用I/O的性能更好更適用于有高并發(fā)需求的redis。

五、異步I/O

異步I/O整個(gè)過(guò)程都是非阻塞的,用戶進(jìn)程調(diào)用aio_read()系統(tǒng)調(diào)用函數(shù)聲明要讀的fd和讀到用戶內(nèi)存空間的地址就直接返回到用戶態(tài),進(jìn)行其他任務(wù)。而讀取數(shù)據(jù)和將數(shù)據(jù)從內(nèi)核緩存拷貝到用戶內(nèi)存的任務(wù)完全交由內(nèi)核線程來(lái)完成。

異步I/O比I/O多路復(fù)用更高效,因?yàn)橛脩艟€程對(duì)I/O操作完全解耦,可以實(shí)現(xiàn)更高并發(fā)的處理請(qǐng)求,使用頻率較高但是不如多路復(fù)用I/O:由于所有任務(wù)都交由內(nèi)核完成,每個(gè)任務(wù)內(nèi)核都要開(kāi)辟新線程來(lái)處理(內(nèi)核是多線程,CPU也是多核,只有用戶應(yīng)用redis是單線程),對(duì)內(nèi)核負(fù)載太大。解決方法是在用戶應(yīng)用進(jìn)行并發(fā)控制,限制單位時(shí)間向內(nèi)核分配的任務(wù)數(shù)量。

六、Redis網(wǎng)絡(luò)模型*

1.純單線程模型

Redis通過(guò)I/O多路復(fù)用來(lái)提高網(wǎng)絡(luò)性能,支持各種不同的多路復(fù)用實(shí)現(xiàn),將這些實(shí)現(xiàn)封裝為統(tǒng)一的接口:

  • ae_epoll:LinuxOS多路復(fù)用實(shí)現(xiàn)方案
  • ae_kqueue:MacOS
  • ae_select:所有OS

Redis提供了通用的API接口,針對(duì)不同操作系統(tǒng)使用不同的實(shí)現(xiàn)方案。

#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

  • AddEvent():注冊(cè)fd,例如epoll_ctl()
  • Create():創(chuàng)建多路復(fù)用監(jiān)聽(tīng)器,例如epoll_create()
  • DelEvent():刪除fd,例如epoll_ctl()
  • Poll():監(jiān)聽(tīng)fd就緒,例如epoll_wait()、select()、poll()

Linux下redis(單線程) epoll 處理網(wǎng)絡(luò)數(shù)據(jù)過(guò)程:(相較于三.3沒(méi)有內(nèi)核態(tài)的切換,但是流程更完整)

  • 單線程調(diào)用epoll_create在內(nèi)核態(tài)初始化一棵紅黑樹(shù)和一個(gè)鏈表。
  • 單線程創(chuàng)建監(jiān)聽(tīng)socket,用來(lái)監(jiān)聽(tīng)新客戶端的連接請(qǐng)求。
  • 單線程調(diào)用epoll_ctl向紅黑樹(shù)中添加監(jiān)聽(tīng)socket的fd,設(shè)為可讀狀態(tài),并添加回調(diào)函數(shù)。
  • 為監(jiān)聽(tīng)socket添加監(jiān)聽(tīng)讀處理函數(shù)。
  • 單線程調(diào)用epoll_wait,阻塞并監(jiān)聽(tīng)就緒鏈表,讓出CPU。
  • 如果阻塞時(shí)間超過(guò)了timeout,那么喚醒該線程。
  • 新客戶端A和B發(fā)起HTTP請(qǐng)求,請(qǐng)求建立連接。
  • DMA和中斷處理程序?qū)⒖蛻舳薃的數(shù)據(jù)讀到監(jiān)聽(tīng)socket的接收緩沖區(qū),觸發(fā)回調(diào)函數(shù)將監(jiān)聽(tīng)socket的fd加入就緒鏈表,標(biāo)記為讀請(qǐng)求。
  • 單線程監(jiān)聽(tīng)到就緒鏈表中數(shù)據(jù)發(fā)生變化,進(jìn)入就緒態(tài)。
  • 單線程就緒態(tài)到運(yùn)行態(tài)過(guò)程中可能DMA和中斷處理程序?qū)⒖蛻舳薆的數(shù)據(jù)讀到監(jiān)聽(tīng)socket的接收緩沖區(qū),觸發(fā)回調(diào)函數(shù)將監(jiān)聽(tīng)socket的fd加入就緒鏈表
  • 單線程獲取CPU,此時(shí)就緒鏈表中只有監(jiān)聽(tīng)socket的fd,會(huì)觸發(fā)監(jiān)聽(tīng)socket的監(jiān)聽(tīng)讀處理函數(shù):?jiǎn)尉€程調(diào)用accept()系統(tǒng)調(diào)用與客戶端A和B建立連接,分別得到客戶端A和B的socket文件描述符fd,為客戶端fd分別設(shè)為可讀狀態(tài)添加讀處理函數(shù),調(diào)用epoll_ctl系統(tǒng)調(diào)用向紅黑樹(shù)中添加客戶端A和B的fd。
  • 單線程調(diào)用epoll_wait,檢查就緒鏈表中是否有fd,如果有,說(shuō)明step11過(guò)程中有新的網(wǎng)絡(luò)數(shù)據(jù)到達(dá)了內(nèi)核緩存,切換回用戶態(tài)處理。
  • 如果未命中說(shuō)明沒(méi)有I/O請(qǐng)求,線程阻塞并監(jiān)聽(tīng)就緒鏈表,讓出CPU。
  • 客戶端A發(fā)來(lái)HTTP請(qǐng)求,并攜帶數(shù)據(jù)。
  • DMA和中斷處理程序?qū)⒖蛻舳薃的數(shù)據(jù)讀到客戶端A socket的接收緩沖區(qū),觸發(fā)回調(diào)函數(shù)將客戶端A的fd加入就緒鏈表,并設(shè)為讀請(qǐng)求。
  • 單線程監(jiān)聽(tīng)到就緒鏈表中數(shù)據(jù)發(fā)生變化,進(jìn)入就緒態(tài)。
  • 單線程獲取CPU,此時(shí)就緒鏈表中有客戶端A的fd,且為讀請(qǐng)求,會(huì)觸發(fā)客戶端A socket的讀處理函數(shù):?jiǎn)尉€程調(diào)用read系統(tǒng)調(diào)用將請(qǐng)求攜帶的數(shù)據(jù)從內(nèi)核緩沖區(qū)讀到為客戶端A分配的內(nèi)存輸入緩沖區(qū)中。
  • 單線程解析并執(zhí)行數(shù)據(jù)中的指令,然后將響應(yīng)結(jié)果寫(xiě)入redis的clients_pending_write鏈表,調(diào)用epoll_ctl將客戶端A的fd狀態(tài)修改為可讀+可寫(xiě)類型,并添加寫(xiě)處理函數(shù)。(只有內(nèi)核中該客戶端的緩沖區(qū)空間不夠了才放入鏈表,不然會(huì)直接發(fā)送到內(nèi)核緩沖區(qū),且不會(huì)添加寫(xiě)處理函數(shù)網(wǎng)卡會(huì)自動(dòng)發(fā)送數(shù)據(jù),step20不滿足,直接異步執(zhí)行step22,我理解的是數(shù)據(jù)先正常發(fā)送,只有發(fā)送空間滿了發(fā)不出去才設(shè)為可寫(xiě)類型表示還有未發(fā)出去的數(shù)據(jù),然后只有發(fā)送完后觸發(fā)的中斷處理程序檢查到有可寫(xiě)標(biāo)識(shí)才會(huì)將該fd的寫(xiě)請(qǐng)求加入就緒鏈表)
  • 單線程調(diào)用epoll_wait,檢查就緒鏈表中是否有fd…
  • 當(dāng)客戶端A的內(nèi)核發(fā)送緩沖區(qū)有空閑且可寫(xiě)狀態(tài)為true就會(huì)發(fā)出硬件中斷將客戶端A的fd加入到就緒鏈表,并設(shè)為寫(xiě)請(qǐng)求。
  • 單線程獲取CPU,此時(shí)就緒鏈表中有客戶端A的fd,且為寫(xiě)請(qǐng)求,會(huì)觸發(fā)客戶端A socket的寫(xiě)處理函數(shù):?jiǎn)尉€程調(diào)用send從redis的clients_pending_write鏈表中取數(shù)據(jù),將數(shù)據(jù)寫(xiě)入客戶端A的內(nèi)核發(fā)送緩沖區(qū),單線程檢查redis的clients_pending_write鏈表是否還有客戶端A的數(shù)據(jù),如果沒(méi)有那么調(diào)用epoll_ctl將客戶端A的fd狀態(tài)修改為可讀類型。
  • 內(nèi)核協(xié)議棧會(huì)將數(shù)據(jù)通過(guò)網(wǎng)卡發(fā)送出去,發(fā)送成功后觸發(fā)中斷將客戶端A的fd加入到就緒鏈表,并設(shè)為寫(xiě)請(qǐng)求(這個(gè)過(guò)程是異步的,由內(nèi)核和網(wǎng)卡負(fù)責(zé),不需要redis單線程參與)。
  • 單線程調(diào)用epoll_wait,檢查就緒鏈表中是否有fd…

因此,Redis中的I/O多路復(fù)用可以理解為:復(fù)用epoll同時(shí)監(jiān)聽(tīng)客戶端連接請(qǐng)求、客戶端讀請(qǐng)求、服務(wù)器向客戶端的寫(xiě)請(qǐng)求,就緒的任何類型的請(qǐng)求都會(huì)放到就緒鏈表中,并每隔一段時(shí)間接收多條請(qǐng)求,針對(duì)不同類型的請(qǐng)求使用不同的分支(監(jiān)聽(tīng)讀處理函數(shù)step11、讀處理函數(shù)step17+18、寫(xiě)處理函數(shù)step21)處理請(qǐng)求。

復(fù)用就緒鏈表接收三類不同的請(qǐng)求請(qǐng)求:

  • 客戶端連接請(qǐng)求從初始化epoll開(kāi)始,調(diào)用epoll_create在內(nèi)核態(tài)創(chuàng)建紅黑樹(shù)和就緒鏈表,創(chuàng)建監(jiān)聽(tīng)socket后調(diào)用epoll_ctl向紅黑樹(shù)中添加監(jiān)聽(tīng)socket的fd,設(shè)為可讀狀態(tài),并添加回調(diào)函數(shù)。當(dāng)連接請(qǐng)求由網(wǎng)卡到達(dá)內(nèi)核緩沖區(qū)會(huì)觸發(fā)中斷,觸發(fā)回調(diào)函數(shù)將監(jiān)聽(tīng)socket從紅黑樹(shù)添加到就緒鏈表,設(shè)為讀請(qǐng)求。
  • 客戶端讀請(qǐng)求在建立連接后會(huì)調(diào)用epoll_ctl將該客戶端的fd添加到紅黑樹(shù),設(shè)為可讀狀態(tài),該客戶端的讀請(qǐng)求到達(dá)后會(huì)被寫(xiě)入為該客戶端分配的內(nèi)核緩沖區(qū),觸發(fā)中斷觸發(fā)回調(diào)函數(shù)將該客戶端socket添加到就緒鏈表,設(shè)為讀請(qǐng)求。
  • 服務(wù)器向客戶端的寫(xiě)請(qǐng)求在線程接收客戶端讀請(qǐng)求并處理后,如果該客戶端的內(nèi)核發(fā)送緩沖區(qū)有足夠的空閑那么將數(shù)據(jù)直接發(fā)送到內(nèi)核緩沖區(qū),會(huì)異步發(fā)送數(shù)據(jù)給客戶端,否則就會(huì)將該客戶端socket加入到pending_write鏈表中,并調(diào)用epoll_ctl將紅黑樹(shù)中客戶端的fd并設(shè)為可讀+可寫(xiě)類型,當(dāng)內(nèi)核緩沖區(qū)有空閑也就是發(fā)送數(shù)據(jù)后會(huì)引發(fā)中斷觸發(fā)回調(diào)函數(shù)將客戶端的fd添加到就緒鏈表并設(shè)為寫(xiě)請(qǐng)求。

復(fù)用單線程處理三類不同的請(qǐng)求請(qǐng)求:

線程調(diào)用epoll_wait進(jìn)入內(nèi)核態(tài)檢查就緒鏈表是否有數(shù)據(jù),如果有直接將就緒鏈表拷貝到用戶態(tài)并處理,如果沒(méi)有回讓出CPU并阻塞,直到就緒鏈表有數(shù)據(jù)或超過(guò)等待時(shí)長(zhǎng)會(huì)進(jìn)入就緒態(tài)等待分配CPU回到用戶態(tài)?;氐接脩魬B(tài)會(huì)遍歷就緒鏈表依次處理每個(gè)請(qǐng)求:

  • 客戶端連接請(qǐng)求會(huì)調(diào)用之前添加的監(jiān)聽(tīng)處理函數(shù)建立連接,調(diào)用epoll_ctl將客戶端fd加入到紅黑樹(shù),設(shè)為可讀,并添加回調(diào)函數(shù)和讀處理函數(shù)。
  • 客戶端讀請(qǐng)求會(huì)調(diào)用之前添加的讀處理函數(shù),執(zhí)行數(shù)據(jù)中的命令例如"get key"并將結(jié)果發(fā)送到內(nèi)核發(fā)送緩沖區(qū),否則就會(huì)將該客戶端socket加入到pending_write鏈表中,調(diào)用epoll_ctl將紅黑樹(shù)中客戶端的fd并設(shè)為可讀+可寫(xiě)類型。
  • 服務(wù)器向客戶端的寫(xiě)請(qǐng)求會(huì)調(diào)用send從pending_write鏈表中取該客戶端數(shù)據(jù),將數(shù)據(jù)寫(xiě)入客戶端的內(nèi)核發(fā)送緩沖區(qū),發(fā)送后如果pending_write鏈表已經(jīng)沒(méi)有當(dāng)前客戶端的數(shù)據(jù),那么調(diào)用epoll_ctl將客戶端的fd狀態(tài)修改為可讀類型。

理一下寫(xiě)處理的過(guò)程:首先線程接收客戶端讀請(qǐng)求并處理,處理完成如果該客戶端的內(nèi)核緩沖區(qū)有足夠的空閑那么將數(shù)據(jù)直接發(fā)送到內(nèi)核緩沖區(qū),后臺(tái)會(huì)異步發(fā)送數(shù)據(jù)。只有該客戶端的內(nèi)核緩沖區(qū)滿了才將數(shù)據(jù)放到clients_pending_write鏈表中,并設(shè)為可寫(xiě)類型,當(dāng)該客戶端的內(nèi)核緩沖區(qū)中的數(shù)據(jù)發(fā)送完后觸發(fā)中斷,中斷處理程序檢查有足夠的空閑了且該fd目前是可寫(xiě)狀態(tài),那么會(huì)將該fd的寫(xiě)請(qǐng)求放入就緒鏈表。

Linux下redis(單線程) epoll 處理網(wǎng)絡(luò)數(shù)據(jù)過(guò)程:(從用戶態(tài)調(diào)用的角度分析,不涉及內(nèi)核態(tài),多路復(fù)用的思想更直觀)

//redis網(wǎng)絡(luò)I/O入口函數(shù)
main {
	server.el = aeCreateEventLoop();// 創(chuàng)建epoll
	listenToPort(server.port,server.ip);// redis創(chuàng)建監(jiān)聽(tīng)socket,給定監(jiān)聽(tīng)的ip和port(服務(wù)器的ip)
	createSocketAcceptHandler(acceptTcpHandler);// 將監(jiān)聽(tīng)socket添加到epoll,并添加監(jiān)聽(tīng)讀處理函數(shù)(監(jiān)聽(tīng)讀處理函數(shù)就是step11) 
	
	// 死循環(huán),該線程一直網(wǎng)絡(luò)I/O
	while(true){
		// 為clients_pending_write鏈表中(有寫(xiě)需求)的客戶端socket依次添加寫(xiě)處理函數(shù)(寫(xiě)處理函數(shù)就是step21)
		foreach(clients_pending_write){
			connSetWriteHandlerWithBarrier(sendReplyToClient);
		}
		
		/* epoll_wait等待中斷信號(hào),返回就緒鏈表
		客戶端發(fā)送的連接請(qǐng)求引發(fā)的中斷會(huì)將監(jiān)聽(tīng)fd添加到就緒鏈表,設(shè)為讀請(qǐng)求
		客戶端發(fā)送的讀請(qǐng)求引發(fā)的中斷會(huì)將客戶端fd添加到就緒鏈表,設(shè)為讀請(qǐng)求
		服務(wù)器向客戶端發(fā)送返回結(jié)果后引發(fā)的中斷會(huì)將客戶端fd添加到就緒鏈表,設(shè)為寫(xiě)請(qǐng)求
		*/
		numevents = aeApiPoll();
		// 依次處理就緒鏈表中的socket
		for(j = 0; j < numevents; j++){
			if(j is 監(jiān)聽(tīng)socket){
				fd = accept(newClient);// 接收新客戶端的連接請(qǐng)求,得到客戶端socket的fd
				connSetReadHandler(fd, readQueryFromClient);// 將客戶端socket添加到epoll,并添加讀處理函數(shù)(讀處理函數(shù)就是step17+18)
			}
			
			if(j is 客戶端讀socket){
				connRead(c);// 將數(shù)據(jù)從內(nèi)核緩沖區(qū)讀到內(nèi)存中該客戶端的輸入緩沖區(qū)
				processInputBuffer(c);// 解析數(shù)據(jù)中的命令為字符串?dāng)?shù)組[set, name, jack]
				cmd = lookupCommand(c->argv[0]);// redis中命令是以鍵值對(duì)方式存儲(chǔ)的,通過(guò)key=set就能找到對(duì)應(yīng)的函數(shù)體
				proc(cmd,c);// 執(zhí)行cmd命令,傳入數(shù)據(jù)c
				addReply();// 將命令執(zhí)行結(jié)果寫(xiě)入該客戶端的等待隊(duì)列
			}	

			if(j is 客戶端寫(xiě)socket){
			    if (!clientHasPendingReplies(c)) {// 檢查是否有數(shù)據(jù)要寫(xiě)
			        aeDeleteFileEvent(server.el, fd, AE_WRITABLE);// 沒(méi)有數(shù)據(jù),立即取消寫(xiě)事件監(jiān)聽(tīng)
			        return;
			    }
			    int nwritten = write(c->fd, c->buf + c->sentlen, c->bufpos - c->sentlen);// 有數(shù)據(jù),直接寫(xiě)入socket
			}	
		}
	}
}

2.命令處理單線程+網(wǎng)絡(luò)I/O多線程模型

2.1 Redis單線程網(wǎng)絡(luò)模型的瓶頸

Redis命令執(zhí)行部分必須是單線程。

為什么redis要做成單線程:

  • 除了持久化操作外,redis是純內(nèi)存操作,因此執(zhí)行速度非???/strong>,限制redis性能的是網(wǎng)絡(luò)I/O延遲而不是指令執(zhí)行速度,短板效應(yīng)下多線程也不會(huì)帶來(lái)性能提升。
  • 多線程會(huì)導(dǎo)致上下文切換,會(huì)帶來(lái)額外開(kāi)銷。
  • 單線程是為了保證命令的隔離性,而多線程會(huì)有線程安全問(wèn)題,使用鎖機(jī)制可以解決線程安全問(wèn)題但是也會(huì)帶來(lái)額外的開(kāi)銷。

經(jīng)過(guò)上面的分析,redis的執(zhí)行過(guò)程為:接收網(wǎng)絡(luò)請(qǐng)求(step12~17)->執(zhí)行命令(step18)->返回響應(yīng)結(jié)果(step19~22)

  • 接收請(qǐng)求:redis單線程阻塞等待,并行的網(wǎng)絡(luò)請(qǐng)求由內(nèi)核的中斷處理程序并行處理,性能很強(qiáng),但是redis單線程返回用戶態(tài)后需要串行執(zhí)行read系統(tǒng)調(diào)用將數(shù)據(jù)從內(nèi)核態(tài)讀到用戶態(tài),且需要頻繁切換用戶態(tài)和內(nèi)核態(tài),這是主要瓶頸。
  • 執(zhí)行命令:redis單線程串行執(zhí)行命令,因?yàn)槊畋仨毚胁拍鼙WC原子操作,必須保持現(xiàn)狀。
  • 返回響應(yīng):由redis單線程串行調(diào)用send()函數(shù)發(fā)送響應(yīng),且需要頻繁切換用戶態(tài)和內(nèi)核態(tài)(雖然內(nèi)核態(tài)發(fā)送響應(yīng)可以異步并行執(zhí)行,但串行send效率太低),這是主要瓶頸。

由于redis性能收到網(wǎng)絡(luò)I/O的限制,經(jīng)過(guò)上述分析,網(wǎng)絡(luò)I/O的接受請(qǐng)求、返回響應(yīng)部分可以設(shè)計(jì)成多線程,且這兩部分不涉及命令的執(zhí)行,所以不會(huì)出現(xiàn)并發(fā)問(wèn)題。

2.2 Redis多線程網(wǎng)絡(luò)模型

對(duì)于接收請(qǐng)求,由redis單線程分發(fā)“read系統(tǒng)調(diào)用將數(shù)據(jù)從內(nèi)核態(tài)讀到用戶態(tài)(step17)”這一操作給多個(gè)子線程執(zhí)行,大大提高了讀取速度,且不會(huì)出現(xiàn)線程安全問(wèn)題(不涉及請(qǐng)求數(shù)據(jù)中的命令執(zhí)行)。

數(shù)據(jù)讀取并解析完成后,由redis單(主)線程來(lái)順序執(zhí)行命令,內(nèi)存執(zhí)行速度快,切能保證原子操作。雖然接受請(qǐng)求使用多線程,但速度上仍然無(wú)法保證指令執(zhí)行的主線程有100%的利用率。

對(duì)于返回響應(yīng),由redis單線程分發(fā)“send系統(tǒng)調(diào)用將數(shù)據(jù)從用戶態(tài)讀到內(nèi)核態(tài)(step21)”這一操作給多個(gè)子線程執(zhí)行,大大提高了讀取速度,且不會(huì)出現(xiàn)線程安全問(wèn)題。

雖然如此,但網(wǎng)絡(luò)I/O依舊是短板。

七、RESP協(xié)議

Redis是CS架構(gòu)。

  • 服務(wù)端:在Linux操作系統(tǒng)中安裝Redis并啟動(dòng),指定IP地址和端口號(hào)。
  • 客戶端:redis-cli命令行、jedis。

通信過(guò)程分為兩步:

  • 客戶端向服務(wù)端的ip+port發(fā)送一條請(qǐng)求,攜帶數(shù)據(jù)(命令)。
  • 服務(wù)端在端口上持續(xù)監(jiān)聽(tīng)請(qǐng)求,使用網(wǎng)絡(luò)I/O模型接收到請(qǐng)求,解析并執(zhí)行命令,返回響應(yīng)結(jié)果給客戶端。

Redis中采用RESP協(xié)議來(lái)規(guī)定客戶端和服務(wù)器發(fā)送請(qǐng)求的規(guī)范。

1.數(shù)據(jù)類型

RESP通過(guò)首字節(jié)的字符來(lái)區(qū)分不同數(shù)據(jù)類型,常用的數(shù)據(jù)類型包括5種:

  • 單行字符串:首字節(jié)是‘+’,后面跟單行字符串,以CRLF(“\r\n”)結(jié)尾,無(wú)法使用特殊字符,二進(jìn)制不安全,一般用于服務(wù)端返回響應(yīng)。
  • Errors:首字節(jié)是’-',后面跟異常信息,以CRLF(“\r\n”)結(jié)尾。
  • 數(shù)值:首字節(jié)是’:',后面跟數(shù)字格式的字符串,以CRLF(“\r\n”)結(jié)尾。
  • 多行字符串:首字節(jié)是’$',后根字符串占用字節(jié)數(shù)量,后根字符串,二進(jìn)制安全,最大支持512MB。

例如:$3\r\nabc\r\n

如果大小為0,代表空字符串:“$0\r\n\r\n”

如果大小為-1,則代表不存在: “$-1\r\n”

  • 數(shù)組:首字節(jié)是’*',后根元素個(gè)數(shù),后根元素,元素?cái)?shù)據(jù)類型不限,可以嵌套數(shù)組。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Redis中管道操作的項(xiàng)目實(shí)踐

    Redis中管道操作的項(xiàng)目實(shí)踐

    Redis管道操作通過(guò)將多個(gè)命令一次性發(fā)送到服務(wù)器,減少了網(wǎng)絡(luò)往返次數(shù),本文就來(lái)介紹一下Redis的管道操作,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-02-02
  • Python Redis如何執(zhí)行Lua腳本

    Python Redis如何執(zhí)行Lua腳本

    這篇文章主要介紹了Python Redis如何執(zhí)行Lua腳本問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • Redis實(shí)現(xiàn)短信登錄的示例代碼

    Redis實(shí)現(xiàn)短信登錄的示例代碼

    本文主要介紹了Redis實(shí)現(xiàn)短信登錄的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Redis?命令詳解與實(shí)戰(zhàn)案例

    Redis?命令詳解與實(shí)戰(zhàn)案例

    本文詳細(xì)介紹了Redis的基礎(chǔ)知識(shí)、核心數(shù)據(jù)結(jié)構(gòu)與命令、高級(jí)功能與命令、最佳實(shí)踐與性能優(yōu)化,以及實(shí)戰(zhàn)應(yīng)用場(chǎng)景,通過(guò)實(shí)戰(zhàn)案例,展示了如何使用Redis構(gòu)建高性能應(yīng)用系統(tǒng),感興趣的朋友跟隨小編一起看看吧
    2025-11-11
  • 硬核!15張圖解Redis為什么這么快(推薦)

    硬核!15張圖解Redis為什么這么快(推薦)

    作為一名服務(wù)端工程師,工作中你肯定和 Redis 打過(guò)交道。Redis為什么快,這點(diǎn)想必你也知道,至少為了面試也做過(guò)準(zhǔn)備,今天通過(guò)本文給大家介紹下,感興趣的朋友一起看看吧
    2020-10-10
  • Redis跳躍表的基本原理和實(shí)現(xiàn)

    Redis跳躍表的基本原理和實(shí)現(xiàn)

    本文主要介紹了Redis跳躍表的基本原理和實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Redis 常用命令之基礎(chǔ)、進(jìn)階與場(chǎng)景化實(shí)戰(zhàn)案例

    Redis 常用命令之基礎(chǔ)、進(jìn)階與場(chǎng)景化實(shí)戰(zhàn)案例

    Redis常用命令全解析,涵蓋基礎(chǔ)、進(jìn)階和場(chǎng)景化實(shí)戰(zhàn),包括字符串、哈希、列表、集合、有序集合等數(shù)據(jù)類型,以及發(fā)布訂閱、分布式鎖、事務(wù)等高級(jí)功能,本文給大家介紹Redis常用命令之基礎(chǔ)、進(jìn)階與場(chǎng)景化實(shí)戰(zhàn)案例,感興趣的朋友一起看看吧
    2026-01-01
  • redis的啟動(dòng)方式操作詳解

    redis的啟動(dòng)方式操作詳解

    文章介紹了Redis的三種啟動(dòng)方式,直接啟動(dòng)、通過(guò)配置文件啟動(dòng)和使用啟動(dòng)腳本,還詳細(xì)說(shuō)明了如何將啟動(dòng)腳本復(fù)制到/etc/init.d目錄并設(shè)置開(kāi)機(jī)自啟動(dòng),解決了使用chkconfig命令時(shí)可能出現(xiàn)的錯(cuò)誤,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • Redis數(shù)據(jù)庫(kù)安全詳解

    Redis數(shù)據(jù)庫(kù)安全詳解

    這篇文章主要為大家介紹了Redis數(shù)據(jù)庫(kù)安全詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 淺談Redis 緩存的三大問(wèn)題及其解決方案

    淺談Redis 緩存的三大問(wèn)題及其解決方案

    Redis 經(jīng)常用于系統(tǒng)中的緩存,這樣可以解決目前 IO 設(shè)備無(wú)法滿足互聯(lián)網(wǎng)應(yīng)用海量的讀寫(xiě)請(qǐng)求的問(wèn)題。本文主要介紹了淺談Redis 緩存的三大問(wèn)題及其解決方案,感興趣的可以了解一下
    2021-07-07

最新評(píng)論

东台市| 富蕴县| 南涧| 襄城县| 剑川县| 韶关市| 喀喇沁旗| 仁化县| 资兴市| 百色市| 遂宁市| 郑州市| 太和县| 禹州市| 进贤县| 余干县| 基隆市| 江川县| 泰兴市| 从化市| 淳化县| 迭部县| 麟游县| 乌什县| 印江| 阜城县| 武宁县| 方山县| 浙江省| 会宁县| 沙田区| 澄城县| 虹口区| 奉贤区| 蓝山县| 财经| 黎城县| 澜沧| 梓潼县| 巴塘县| 溧阳市|