Redis是一個非常高效的基於記憶體的NOSQL資料庫,它提供非常高效的資料讀寫效能.在實際應用中往往是頻寬和CLIENT庫讀寫損耗過高導致無法更好地發揮出Redis更出色的能力.下面結合一些redis本身的特性和一些client操作上的改變來提高整個redis操作的交通.
上圖是反映平常操作redis的情況,每個執行緒都獨立的發起相應連線對redis的網路讀寫.雖然我們可以通過批操作的方式來把當前多個操作合併成一個,但這種方式只能針對當單執行緒,而多執行緒相互合併則設計上很少關注.從redis的協議來說其實並沒有限制,只是在client庫的設計一般沒有考慮進去.
如果在多執行緒操作REDIS的同時如果能夠合併網路操作,那意味著可以降低操作網路讀寫的情況把處理能力提升到最大化.這樣Client總體的效能都會有所提升,而REDIS也因表層的網路讀取減少而達到更好的利用率.
以上是設計圖,原理並不複雜,其實就是把每個請求的操作放到一個佇列中,後面開啟一個執行緒來把前面的指令進行一個合併操操作.一個執行緒在高併發下可以無法更快速地合併起來,可以根據需要進行合理的操作執行緒應用.
這種設計的效果是否真的比較理想呢,以一上是一個簡單的測試
public void AnycSet() { CodeTimer.Time("beetle.redis asynset", () => { Parallel.For(0, Count, x => { ProtobufKey key = x.ToString(); key.AsynSet(new User() { UserId = x, NickName = "sdffffffffffffffffffffffffffffffffffffffffsdffffffffffffffffffffffffffffffffffffffffbeetlesdffffffffffffffffffffffffffffffffffffffffsdffffffffffffffffffffffffffffffffffffffff" + x }); }); }); } public void Set() { CodeTimer.Time("beetle.redis set", () => { Parallel.For(0, Count, x => { ProtobufKey key = x.ToString(); key.Set(new User() { UserId = x, NickName = "sdffffffffffffffffffffffffffffffffffffffffsdffffffffffffffffffffffffffffffffffffffffbeetlesdffffffffffffffffffffffffffffffffffffffffsdffffffffffffffffffffffffffffffffffffffff" + x }); }); }); }
測試結果如下
以上是10W次的操作測試結果,由於redis在本機所以互動非常可觀.
雖然在多執行緒高併發下這樣的設計可以把吞吐能力和效能有一個非常不錯的效果,但其也存在缺陷因為每次操作都經過不同執行緒的調處理,如果併發執行緒不多操作密集度不高.那效果並不理想;因為網路操作密集度不高,可得到併合的數量不多,這方面的損耗有可能低於操作跨執行緒排程所帶來的損耗.