您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“Redis單線程為什么這么快”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Redis單線程為什么這么快”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
性能測試報告
查看了下阿里 Redis 的性能測試報告如下,能夠達到數十萬、百萬級別的 QPS(暫時忽略阿里對 Redis 所做的優化),我們從 Redis 的設計和實現來分析一下 Redis 是怎么做的。
Redis的設計與實現
其實 Redis 主要是通過三個方面來滿足這樣高效吞吐量的性能需求
高效的數據結構
多路復用 IO 模型
事件機制
1、高效的數據結構
Redis 支持的幾種高效的數據結構 string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)。
以上幾種對外暴露的數據結構它們的底層編碼方式都是做了不同的優化的,不細說了,不是本文重點。
2、多路復用 IO 模型
假設某一時刻與 Redis 服務器建立了 1 萬個長連接,對于阻塞式 IO 的做法就是,對每一條連接都建立一個線程來處理,那么就需要 1萬個線程,同時根據我們的經驗對于 IO 密集型的操作我們一般設置,線程數 = 2 * CPU 數量 + 1,對于 CPU 密集型的操作一般設置線程 = CPU 數量 + 1。
當然各種書籍或者網上也有一個詳細的計算公式可以算出更加合適準確的線程數量,但是得到的結果往往是一個比較小的值,像阻塞式 IO 這也動則創建成千上萬的線程,系統是無法承載這樣的負荷的更加彈不上高效的吞吐量和服務了。
而多路復用 IO 模型的做法是,用一個線程將這一萬個建立成功的鏈接陸續的放入 event_poll,event_poll 會為這一萬個長連接注冊回調函數,當某一個長連接準備就緒后(建立建立成功、數據讀取完成等),就會通過回調函數寫入到 event_poll 的就緒隊列 rdlist 中,這樣這個單線程就可以通過讀取 rdlist 獲取到需要的數據。
需要注意的是,除了異步 IO 外,其它的 I/O 模型其實都可以歸類為阻塞式 I/O 模型,不同的是像阻塞式 I/O 模型在第一階段讀取數據的時候,如果此時數據未準備就緒需要阻塞,在第二階段數據準備就緒后需要將數據從內核態復制到用戶態這一步也是阻塞的。而多路復用 IO 模型在第一階段是不阻塞的,只會在第二階段阻塞。
通過這種方式,就可以用 1 個或者幾個線程來處理大量的連接了,極大的提升了吐吞量
3、事件機制
Redis 客戶端與 Redis 服務端建立連接,發送命令,Redis 服務器響應命令都是需要通過事件機制來做的,如下圖
首先 redis 服務器運行,監聽套接字的 AE_READABLE 事件處于監聽的狀態下,此時連接應答處理器工作
客戶端與 Redis 服務器發起建立連接,監聽套接字產生 AE_READABLE 事件,當 IO 多路復用程序監聽到其準備就緒后,將該事件壓入隊列中,由文件事件分派器獲取隊列中的事件交于連接應答處理器工作處理,應答客戶端建立連接成功,同時將客戶端 socket 的 AE_READABLE 事件壓入隊列由文件事件分派器獲取隊列中的事件交命令請求處理器關聯
客戶端發送 set key value 請求,客戶端 socket 的 AE_READABLE 事件,當 IO 多路復用程序監聽到其準備就緒后,將該事件壓入隊列中,由文件事件分派器獲取隊列中的事件交于命令請求處理器關聯處理
命令請求處理器關聯處理完成后,需要響應客戶端操作完成,此時將產生 socket 的 AE_WRITEABLE 事件壓入隊列,由文件事件分派器獲取隊列中的事件交于命令恢復處理器處理,返回操作結果,完成后將解除 AE_WRITEABLE 事件與命令恢復處理器的關聯
reactor模式
大體上可以說 Redis 的工作模式是,reactor 模式配合一個隊列,用一個 serverAccept 線程來處理建立請求的鏈接,并且通過 IO 多路復用模型,讓內核來監聽這些 socket,一旦某些 socket 的讀寫事件準備就緒后就對應的事件壓入隊列中,然后 worker 工作,由文件事件分派器從中獲取事件交于對應的處理器去執行,當某個事件執行完成后文件事件分派器才會從隊列中獲取下一個事件進行處理。
可以類比在 netty 中,我們一般會設置 bossGroup 和 workerGroup 默認情況下 bossGroup 為 1,workerGroup = 2 * cpu 數量,這樣可以由多個線程來處理讀寫就緒的事件,但是其中不能有比較耗時的操作如果有的話需要將其放入線程池中,不然會降低其吐吞量。在 Redis 中我們可以看做這二者的值都是 1。
為什么說存儲的值不宜過大
比如一個 string key = a,存儲了 500MB,首先讀取事件壓入隊列中,文件事件分派器從中獲取到后,交于命令請求處理器處理,此處就涉及到從磁盤中加載 500MB。
比如是普通的 SSD 硬盤,讀取速度 200MB/S,那么需要 2.5S 的讀取時間,在內存中讀取數據比較快比如 DDR4 中 50G/秒,讀取 500MB 需要 100 毫秒左右。
線程的庫一般默認 10 毫秒就算慢查詢了,大部分的指令執行時間都是微秒級別,此時其它 socket 所有的請求都將處于等待過程中,就會導致阻塞了 100 毫秒,同時又會占用較大的帶寬導致吞吐量進一步下降。
讀到這里,這篇“Redis單線程為什么這么快”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。