redis爲什麼這麼快?

一說到微服務緩存大家都想到使用redis來做,因爲它快。支持10W+的qps。那它到底爲什麼這麼快呢?今天來簡單回顧一下。

 

內存操作

第一個肯定要說的是redis的內存操作,相對於關係型數據庫的將數據存入磁盤。內存的操作速度就相當快。沒有磁盤IO操作的耗時自然就快了。看過一個資料,CPU讀取一次內存數據,大約需要100個時鐘週期。這對於磁盤IO真的非常快了。

 

redisObject

redis的對象及數據結構設計也是它快的很重要的原因。
redis中並不是直接使用數據結構存儲,而是通過redisObject對象操作數據。
redisObject包含屬性:type-數據類型encoding-數據編碼,ptr-底層數據結構等。

type

指redis提供的幾種數據類型,例如:REDIS_STRING,REDIS_LIST這類的數據結構。

encoding

指當前存儲的具體數據結構,例如:使用指令:set key 1時,value的encoding就是REDIS_ENCODING_INT(long型),再次set key hello時,value的encoding就會變成REDIS_ENCODING_RAW(簡單動態字符串)或者REDIS_ENCODING_EMBSTR(embstr編碼的簡單動態字符串)。

RAW和EMBSTR簡單的區別:REDIS_ENCODING_RAW簡單動態字符串,創建redisObject 時分配一次內存,創建存放字符串的sdshdr時分配一次,共調用兩次內存分配函數,適用於保存長字符串;REDIS_ENCODING_EMBSTR簡單動態字符串,專門保存短字符串。創建redisObject和sdshdr共調用一次內存函數,所以可以獲取一塊連續的內存空間。更好的利用CPU緩存優勢。

 

數據結構

數據結構也是一個redis設計的一個很重要的點,我就撿我瞭解的說一些。

SDS-simple dynamic string

簡單動態字符串數據結構,底層是buf[]數組保存字符串,只不過他在數組種固定保存了幾個值。

len

已使用字符串長度,等於buf長度,有人會問爲什麼保存len字段?因爲這樣獲取長度時時間複雜度是O(1)。加快訪問速度。

free

記錄buf中未使用的字節數量,那爲什麼有free字段?爲了防止緩衝區溢出,字符串擴展前先判斷是否足夠空間可分配,如果足夠則直接擴展;如果不足則先重新進行內存分配,再擴展字符串。

redis內存重新分配的優化策略

1. 空間預分配,當SDS發生空間擴展時,程序不僅會給擴展出本次足夠的內存空間,還會再預先分配出一塊內存空間,並把大小存入free字段,這樣,下次再擴展可以直接使用上次分配的未空間,而不必觸發內存分配操作。

2. 惰性空間釋放,前面提到的預分配是字符串擴展的情況。而當遇到縮容時,被施放的字符串空間不會立刻收回,而是放到free裏,等待下次使用。這就是惰性釋放,減少了內存釋放的操作。

 

字符串變量的共享和適配

redis會創建一定範圍內的整數對應的字符串對象用來共享使用。避免重複創建。使用redis.h/REDIS_SHARED_INTEGERS可以修改創建的共享字符串對象的數量,目前默認應該是0-9999,如果服務器要使用到這些對象時,就會直接使用共享字符串變量。

 

當前時間存儲

redis會緩存秒級和毫秒級的系統unix時間戳,減少獲取當前時間的系統調用。這個功能用於一些對時間精度要求不高的地方,例如日誌。設置超時這類精度要求高的還是會獲取系統時間的。

 

redis主從複製算法的優化

redis開始時主從複製是使用sync指令,每次複製master會生成一份RDB快照,然後傳輸給slave進行數據恢復。這樣每次複製都是一個全量複製。性能消耗很大。後來2.8版本以後使用psync指令將複製分爲完整重同步和部分重同步。只有從服務器第一次或者斷開很長時間才使用完整重同步,而其他情況採用偏移量增量複製,將自身的偏移量告訴主服務,主服務會根據收到的offset和自身的offset將複製積壓緩衝區中從服務丟失的寫命令發送給從服務這就是部分重,同步避免全複製的性能消耗。

 

IO的多路複用

 

redis使用一個線程監聽多個套接字,誰先有讀寫事件就處理誰,減少線程切換開銷和IO阻塞,以提高CPU利用率。

 

單進程單線程?

其實redis並不是一個單進程單線程服務,例如它在bgsave時會fork出一個子線程去執行。它只不過是在接受請求網絡模塊使用的是單線程模式。其他的一些緩慢操作還是多線程去執行的。

而且最新的6.0+版本,redis已經支持了多線程模式,性能至少是單線程模式的一倍。如果單線程小數據包能達到10w+qps;那麼多線程大概是20w+。

redis啓動多線程原因:

1. 充分利用cpu資源。單線程只能利用到CPU的單一核心。

2. 利用多線程分擔同步IO的讀寫負荷。

 

那麼如何啓用多線程?

io-threads-do-reads yes

io-threads 4(不要超過CPU核心數)

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