Redis高效性探索--线程IO模型,通信协议
生活随笔
收集整理的這篇文章主要介紹了
Redis高效性探索--线程IO模型,通信协议
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Redis線程IO模型
- Redis是單線程,這個毋庸置疑
- Redis單線程能做到這么高的效率?不用懷疑,還有很多其他的服務都是單線程但是也有超高的效率,比如Node.js,Nginx也是單線程。
- Redis單線程高效原因:
- Redis所有數據都存儲在內存中,所有運算都是內存級別的運算,正是因為Redis是單線程,所有要小心使用,我們業(yè)務中可能會有一些耗時的操作keys(死亡命令)時間復雜度O(n),這種一定要謹慎使用,會造成Redis卡頓
- Redis單線程如何處理這么多客戶端的并發(fā)連接?
- 多路復用解決此問題,利用操作系統(tǒng)底層的select系列事件輪詢API,非阻塞IO的形式,就是java中NIO的理念同樣可以在Redis中復用
非阻塞IO
- 當我們通過套接字(socket)利用網絡協議進行數據交換(讀寫)的時候,默認是阻塞的狀態(tài),比如read方法要傳遞進去一個參數n,標識最多讀取n個字節(jié)后在返回,如果一個字節(jié)都沒有,線程就會卡主,直到新的數據到來或者連接關閉,read方法才可以返回,線程才能繼續(xù)處理其他的事務。Write方法一般不阻塞,除非內核為套接字分配的緩沖區(qū)已經滿,write方法會阻塞,直到緩沖區(qū)空閑,在將剩下的寫入,如下是redis中socket讀寫的一個流程。
- 非阻塞IO在套接字對象上提供了一個選項Non_blocking,當這個選項打開時候,讀寫方法不會阻塞,而是能讀多少讀多少,能寫多少寫多少。能讀多少取決于內核為套接字分配的讀緩沖區(qū)內部的數據字節(jié)數。寫多少取決于內核為套接字分配的寫緩沖區(qū)空閑空間字節(jié)數。讀方法和寫方法都會通過返回值來告知程序實際讀寫了多少字節(jié)
- 有了非阻塞IO意味著線程無在IO操作時無需阻塞,可以瞬間完成,然后繼續(xù)別的事件
事件輪詢(多路復用)
- 上文中提到的線程讀取數據時候,并不是一次性讀完,而是取決于緩沖區(qū)大小,讀取了一部分就返回,那么線程如何知道什么時候才繼續(xù)讀,也就是當數據來了,線程如何得到通知。寫也是一樣,如果緩沖區(qū)滿,寫不完,剩下的數據什么時候才繼續(xù)寫,線程也需要得到通知。
- 事件輪詢API就是用來解決這個問題。最簡單的時間輪詢API是select函數。操作系統(tǒng)級別的API。
- 輸入是讀寫描述符列表read_fds&write_fds,輸出是與之對應的可讀寫事件,還提供了timeout參數,
- 沒有事件需要執(zhí)行的時候最多等待timeout時間線程處于阻塞狀態(tài)
- 一旦期間有任何事件到來就可以返回。時間過了之后沒有任何事件到來也立刻返回
- 例如RedisClient執(zhí)行一個 查詢命令,client通過socket在select上注冊一個read_fds,其他客戶端的任何操作也是想select中注冊事件,這個時間按定時任務執(zhí)行,當輪到你執(zhí)行的時候才執(zhí)行你的socket事件。如下圖
- 每個套接字socket都有對應的讀寫文件描述符,偽代碼描述如下
指令隊列
- Redis為每個客戶端套接字關聯一個指令隊列。客戶端指令通過隊列排隊進行順序處理,先到先服務。
響應隊列
- 同樣Redis也為每個客戶端套接字關聯一個響應隊列。Redis服務器通過響應隊列來將指令結果回復給客戶端。如果隊列空則空閑,此時可以將當前客戶端描述符從write_fds里面移除。等隊列有數據在將描述符放入,避免select系統(tǒng)調用立刻返回寫時間,結果發(fā)現沒有數據可以寫,空耗CPU資源
定時任務
- 上文中socket注冊時間流程中,定時任務是關鍵的一環(huán),服務器除了處理IO操作以外,還有定時任務。
- Redis的定時任務用來記錄需要執(zhí)行的任務,這些數據被記錄在一個被稱為“最小堆”的數據結構中。這個堆中最快執(zhí)行的在最上方。每個循環(huán)周期中,Redis服務器端都會對最小堆中已經到實際點的任務進行處理,并將下一個要執(zhí)行的任務還需要的時間記錄下來(此處的時間就是我們注冊時間到select中的socket的timeout)。因為Redis知道未來timeout時間內是沒有任務,所有可以休眠timeout時間。
通信協議
- Redis序列化方法RESP(Redis Serialization Protocol)。一種直觀的文本協議,優(yōu)勢在于簡單直觀,易于實現,解析性能極好,Redis的高效性的原因之一。
- Redis協議將傳輸結構分為五種單元類型,每種類型結束都加上回車換行(\r\n)。
- 單行字符串以“+”符合開頭
- 多行字符串以“$”符號開頭,后跟字符串長度
- 整數以“:”開頭,后面跟整數字符串的形式
- 錯誤信息以“-”開頭。
- 數組以“*”開頭,后面跟數組長度
- 一下示例:
客戶端到服務器通信
- 客戶端向服務器發(fā)送指令只有一種格式,多行字符串數組。比如一個簡單的set指令set myList addfirst,會被序列化如下格式
- 控制臺輸出這個字符串如下,可以看出來是一個很容易理解的格式:
服務器到客戶端
- 服務器向客戶端回復響應信息要支持多種數據結構,所以消息響應在結構上要復雜一點,不過還是基于上文中的規(guī)則。
- 小結
-
- Redis協議中大量冗余的回車換行,但是不影響性能,Redis的序列化協議還是互聯網技術領域非常受歡迎的文本協議。技術上,性能的確重要,但是同時兼具簡單易用,易實現,這些都是需要權衡。
上一篇:Redis高效性探索–管道
總結
以上是生活随笔為你收集整理的Redis高效性探索--线程IO模型,通信协议的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 男人死精能治好吗
- 下一篇: Redis持久化-深入理解AOF,RDB