Redis爲什麼這麼快,帶你瞭解線程IO模型!

1、Redis是單線程還是多線程?

Redis 是個單線程程序。

2、Redis爲什麼這麼快?

數據都在內存中,內存操作當然比磁盤操作快。

3、Redis單線程如何同時處理多個請求?

(1) 使用非阻塞IO+IO多路複用技術處理。

平常我們自己寫的socket程序都是read和write多少個字節返回,接收不夠一直阻塞,成爲阻塞IO,阻塞的時候線程幹不了別的事情。套接字提供一個選項Non_Blocking,當這個選項打開時,讀寫方法不會阻塞,而是能讀多少讀多少,能寫多少寫多少。有了非阻塞 IO 意味着線程在讀寫 IO 時可以不必再阻塞了,讀寫可以瞬間完成然後線 程可以繼續幹別的事了。

非阻塞 IO 有個問題,那就是線程要讀數據,結果讀了一部分就返回了,線程如何知道 何時才應該繼續讀。也就是當數據到來時,線程如何得到通知。寫也是一樣,如果緩衝區滿 了,寫不完,剩下的數據何時才應該繼續寫,線程也應該得到通知。

事件輪詢 API 就是用來解決這個問題的,最簡單的事件輪詢 API 是 select 函數,它是 操作系統提供給用戶程序的 API。輸入是讀寫描述符列表 read_fds & write_fds,輸出是與之 對應的可讀可寫事件。同時還提供了一個 timeout 參數,如果沒有任何事件到來,那麼就最多 等待 timeout 時間,線程處於阻塞狀態。一旦期間有任何事件到來,就可以立即返回。時間過 了之後還是沒有任何事件到來,也會立即返回。拿到事件後,線程就可以繼續挨個處理相應 的事件。處理完了繼續過來輪詢。於是線程就進入了一個死循環,我們把這個死循環稱爲事 件循環,一個循環爲一個週期。

每個客戶端套接字 socket 都有對應的讀寫文件描述符。

read_events, write_events = select(read_fds, write_fds, timeout) for event in read_events:

handle_read(event.fd) for event in write_events:

handle_write(event.fd)
handle_others() # 處理其它事情,如定時任務等

因爲我們通過 select 系統調用同時處理多個通道描述符的讀寫事件,因此我們將這類系 統調用稱爲多路複用 API。現代操作系統的多路複用 API 已經不再使用 select 系統調用,而 改用 epoll(linux)和 kqueue(freebsd & macosx)。

(2) 指令隊列和相應隊列

Redis 會將每個客戶端套接字都關聯一個指令隊列。客戶端的指令通過隊列來排隊進行順序處理,先到先服務。

Redis 會將每個客戶端套接字都關聯一個響應隊列,用來關聯寫事件。

如果隊列爲空,那麼意味着連接暫時處於空閒狀態,不需要 去獲取寫事件,也就是可以將當前的客戶端描述符從 write_fds 裏面移出來。等到隊列有數據 了,再將描述符放進去。避免 select 系統調用立即返回寫事件,結果發現沒什麼數據可以 寫。出這種情況的線程會飆高 CPU。

(3) 定時任務

服務器處理要響應 IO 事件外,還要處理其它事情。比如定時任務就是非常重要的一件 事。如果線程阻塞在 select 系統調用上,定時任務將無法得到準時調度。那 Redis 是如何解 決這個問題的呢?

Redis 的定時任務會記錄在一個稱爲最小堆的數據結構中。這個堆中,最快要執行的任 務排在堆的最上方。在每個循環週期,Redis 都會將最小堆裏面已經到點的任務立即進行處 理。處理完畢後,將最快要執行的任務還需要的時間記錄下來,這個時間就是 select 系統調 用的 timeout 參數。因爲 Redis 知道未來 timeout 時間內,沒有其它定時任務需要處理,所以 可以安心睡眠 timeout 的時間。

—————————————————

參考來源:http://ddrv.cn/a/258735

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章