3. 高性能IO模型,探究Redis單線程為何很快

1. Redis單線程

  • 通常說(shuō)Redis單線程是指Redis的網(wǎng)絡(luò)IO和鍵值對(duì)讀寫(xiě)是由一個(gè)線程完成的
但Redis的其他功能,比如持久化,異步刪除,集群數(shù)據(jù)同步等都是由額外的線程完成的

2. 為什么Redis使用單線程


2.1 多線程的開(kāi)銷(xiāo)

  • 問(wèn)題:
    • 通常情況,假如沒(méi)有良好的設(shè)計(jì),在剛開(kāi)始增加線程數(shù)時(shí),吞吐率會(huì)有所增加,但進(jìn)一步增加線程時(shí),吞吐率就會(huì)增長(zhǎng)遲緩甚至?xí)霈F(xiàn)下降情況
  • 原因:
    • 系統(tǒng)中常會(huì)存在被多線程同時(shí)訪問(wèn)的共享資源,比如一個(gè)共享的數(shù)據(jù)結(jié)構(gòu),當(dāng)多個(gè)線程要修改共享資源時(shí),就要有額外的機(jī)制來(lái)保證共享資源準(zhǔn)確性,就會(huì)帶來(lái)額外的開(kāi)銷(xiāo)
    • 比如,redis中的lpush和lpop操作,假如兩個(gè)線程同時(shí)對(duì)list分別做lpush和lpop操作,就會(huì)遇到這個(gè)問(wèn)題
  • 問(wèn)題就是多線程編程面臨的共享資源的并發(fā)訪問(wèn)控制問(wèn)題

2.2 其他原因

  • 多線程開(kāi)發(fā)一般會(huì)引入同步原語(yǔ)來(lái)保護(hù)共享資源并發(fā)訪問(wèn),會(huì)降低系統(tǒng)的調(diào)試和維護(hù)性

3. 為什么那么快

  • 大部分操作內(nèi)存中完成
  • 高效的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)(如哈希表和跳表)
  • 采用多路復(fù)用機(jī)制

4. 基本IO模型和阻塞點(diǎn)

4.1 SimpleKV處理GET請(qǐng)求,IO示例如下圖,依次執(zhí)行如下操作:

Redis基本IO模型
  • 潛在阻塞點(diǎn)如下:
    • accept()
      • 當(dāng) Redis 監(jiān)聽(tīng)到一個(gè)客戶(hù)端有連接請(qǐng)求,但一直未能成功建立起連接時(shí),會(huì)阻塞在 accept() 函數(shù)這里,導(dǎo)致其他客戶(hù)端無(wú)法和 Redis 建立連接
    • recv()
      • 當(dāng) Redis 通過(guò) recv() 從一個(gè)客戶(hù)端讀取數(shù)據(jù)時(shí),如果數(shù)據(jù)一直沒(méi)有到達(dá),Redis 也會(huì)一直阻塞在 recv()。

4.2 socket的非阻塞模式

socket 模型,不同操作調(diào)用后會(huì)返回不同的套接字類(lèi)型。
image.png
    1. socket() 方法會(huì)返回主動(dòng)套接字,
    1. 調(diào)用 listen() 方法,將主動(dòng)套接字轉(zhuǎn)化為監(jiān)聽(tīng)套接字,此時(shí),可以監(jiān)聽(tīng)來(lái)自客戶(hù)端的連接請(qǐng)求。
    1. 調(diào)用 accept() 方法接收到達(dá)的客戶(hù)端連接,并返回已連接套接字。
1. 針對(duì)監(jiān)聽(tīng)套接字,可以設(shè)置非阻塞模式:當(dāng)Redis調(diào)用accept()但一直未有連接請(qǐng)求到達(dá)時(shí),Redis線程可以返回處理其他操作,不用一直等待。但調(diào)用accept()時(shí),已經(jīng)存在監(jiān)聽(tīng)套接字了
2. 同理,也可以對(duì)已連接套接字設(shè)置非阻塞模式,如數(shù)據(jù)沒(méi)有到達(dá),可以返回處理其他操作
3. 問(wèn)題:需要有機(jī)制,在監(jiān)聽(tīng)套接字等待后續(xù)連接(有請(qǐng)求時(shí)通知Redis),繼續(xù)監(jiān)聽(tīng)已連接套接字,在有數(shù)據(jù)到達(dá)時(shí)通知Redis

Redis機(jī)制:基于多路復(fù)用的高性能I/O模型

引言:

  • linux中IO多路復(fù)用:指一個(gè)線程處理多個(gè)IO流,也就是select/epoll機(jī)制
  • Redis中:在一個(gè)內(nèi)核中,同時(shí)存在多個(gè)監(jiān)聽(tīng)套接字和已連接套接字。內(nèi)核會(huì)一直監(jiān)聽(tīng)這些套接字上的連接請(qǐng)求或數(shù)據(jù)請(qǐng)求。有請(qǐng)求到達(dá),就交給redis線程處理

基于多路復(fù)用的Redis高性能IO模型

Redis高性能IO模型
    1. 圖中的多個(gè)FD指多個(gè)套接字。Redis網(wǎng)絡(luò)框架調(diào)用epoll機(jī)制,讓內(nèi)核監(jiān)聽(tīng)這些套接字。這樣,Redis線程不會(huì)阻塞在某個(gè)特定的監(jiān)聽(tīng)或已連接套接字(不會(huì)阻塞在某一個(gè)特定的客戶(hù)端請(qǐng)求處理)。所以,Redis可以同時(shí)和多個(gè)客戶(hù)端連接并處理請(qǐng)求,提升并發(fā)性
    1. 為了在請(qǐng)求到達(dá)時(shí)通知到Redis,select/epoll提供了基于事件的回調(diào)機(jī)制(針對(duì)不同事件發(fā)生,調(diào)用相應(yīng)處理函數(shù))

回調(diào)機(jī)制

  • select/epoll一旦監(jiān)測(cè)到FD上有請(qǐng)求,出發(fā)相應(yīng)事件將事件放進(jìn)一個(gè)隊(duì)列,Redis單線程對(duì)該事件隊(duì)列不斷進(jìn)行處理
  • 以連接請(qǐng)求和讀數(shù)據(jù)請(qǐng)求為例:
    • 兩個(gè)請(qǐng)求分別對(duì)應(yīng)Accept事件和Read事件,Redis分別對(duì)這兩個(gè)事件注冊(cè)accept和get回調(diào)函數(shù)。當(dāng)linux內(nèi)核監(jiān)聽(tīng)到有連接請(qǐng)求或讀數(shù)據(jù)請(qǐng)求時(shí),就會(huì)觸發(fā)Accept事件和Read事件,此時(shí),內(nèi)核就會(huì)回調(diào)Redis相應(yīng)的accept和get函數(shù)進(jìn)行處理

總結(jié):

  • Redis單線程是指它對(duì)網(wǎng)絡(luò)IO和數(shù)據(jù)讀寫(xiě)的操作采用了一個(gè)單線程,核心原因是避免多線程開(kāi)發(fā)的并發(fā)控制問(wèn)題
  • Redis單線程獲得高性能的原因在:
    • 多路復(fù)用的IO模型:避免了accept()和send()/recv()潛在的網(wǎng)絡(luò)IO操作點(diǎn)
最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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