Redis基礎資料類型
redis有5中基礎資料類型結構,分别為 string(字元串)、list(清單)、hash(字典)、set(集合)、zset(有序集合).
redis所有的資料結構都用一個唯一的key字元串作為名稱,然後通過這唯一的key擷取相應的value資料,不同類型的資料結構在于value的結構不同,也就是說不管是什麼資料類型,他們的key都是字元串,代表能找到相應資料的辨別。
string(字元串)
字元串結構用于存儲一些固定的資訊,例如使用者資訊,在系統中将使用者資訊序列化成一個json字元串,随後根據用使用者id作為key,json串作為value存入redis,
後續的過程中,擷取使用者資訊時,就可以通過id來redis中擷取,避免頻繁通路資料庫。
内部實作
和java中的字元串不同,java中的字元串對象是不可變的,在redis中,字元串是可變的,内部結構的實作類似于Java中的ArrayList,采用預配置設定備援空間的方式減少記憶體的頻繁配置設定,
如下圖,内部為字元串配置設定的實際空間(capacity)一般要高于實際字元串長度(len),當字元串長度小于1MB時,擴容是按照目前的空間翻倍,如果超過1MB,擴容每次最多隻多加1MB的空間。
打個假設, value 是 hello 時,那麼字元串的長度可能是10,前面五個存hello,後面5個用于應對後續的擴容。
PS:字元串的最大長度是512MB。
要點:Redis的字元串可擴容,大小不超過1MB時,翻倍拓容,超過則每次隻加1MB。
list(清單)
Redis的清單和Java中的LinkedList相似,是連結清單結構,
既然是連結清單,則意味着插入和删除的速度非常快,時間複雜度為O(1),但是查找就很慢了,時間複雜度則是O(N),
連結清單中的每個元素都有雙向指針,可以指向上一個元素和下一個元素,友善支援向前周遊和向後周遊,當清單彈出最後一個元素後(也就是連結清單中沒資料了),該list将會被自動删除,list占用的記憶體被回收。
快速清單
Redis的底層不是一個簡單的LinkedList,而是一個quicklist(快速清單)結構,
當清單元素較少的情況下,會使用一塊連續的記憶體存儲,這個結構是ziplist,即壓縮清單,它将所有的元素緊湊的放在一起存儲,使用的是一塊連續的記憶體空間(數組),當資料量比較多的時候才會改為quicklist。
因為普通的連結清單需要兩個指針,指針也是占用記憶體空間的,還會加重記憶體的碎片化,比如一個連結清單中存的是int類型資料,還需要額外為這個資料增加兩個額外的指針(prev和next),
是以redis将連結清單和ziplist結合組成quicklist,既能滿足快速的插入删除性能,又不會出現太大的空間備援,在性能和空間之間做了平衡。
要點:Redis中的清單其實是一個連結清單和ziplist的組合(為了節省空間),稱為quicklist,而非想象中的類似java的ArrayList,該結構的好處是新增删除比較快,查找較慢。
hash(字典)
Redis字典和java的HashMap類似,都是無序字典,内部存儲着鍵值對,同時雙方的資料結構都是 數組+連結清單 的二維結構,當數組位置發生碰撞時,就會将碰撞的元素使用數組位置下的連結清單進行串聯。
不同的是,Redis字典的值隻能是字元串,并且rehash的方式和java也不一樣,java的HashMap在rehash時,需要一次性全部完成,在資料量比較大的時候,性能較差,會比較慢,
redis為了追求高性能,保證rehash的時候不阻塞服務,采用了漸進式rehash政策。
漸進式 rehash
漸進式rehash會在rehash的同時,保留新舊兩個hash結構,查詢時會同時查詢兩個hash結構,
然後在後續的定時任務以及hash操作指令中,循環漸進的将舊hash的内容一點點遷移到新的hash結構中,
當遷移完成,新的hash将替代老的hash,老的hash在最後一個元素被移除後,将會被自動删除,記憶體被回收。
set(集合)
Redis中的集合相當于Java語言中的HashSet,鍵值對是無序,但唯一的,内部實作是一個字典,字典中所有的value都是一個null值,當集合中的最後一個元素被移除時,資料結構将被自動删除,記憶體則被回收。
zset(有序集合)
zset類似Java的SortedSet和HashMap的結合體,一方面是一個set可以保證唯一性,另一方面可以給set中的每個資料設定一個score,用于給資料進行排序,
zset的内部資料結構是一個叫做跳躍清單的樹結構,同set一樣,當集合中的最後一個元素被移除時,資料結構将被自動删除,記憶體則被回收。
跳躍清單
zset需要支援随機插入和删除,使用數組自然不合适,是以存儲需要使用連結清單資料結構,同時這個連結清單還需要使用score值進行排序,
有序意味着,當有新元素要插入時,要定位到特定位置的插入點,這樣才可以保證連結清單是有序的,通常定位插入點會用二分查找來做,但是隻有數組才支援二分查找,連結清單做不到,此時就需要跳躍清單幫忙做這件事,
舉個例子,如果在一個幾百号人的公司裡,老闆配置設定任務,在沒有組織架構的情況下,得單獨為每個人配置設定,這樣必然會很累,
但是公司可以選部門經理,再選小組組長,再到組員,這樣老闆每次下達任務隻需要找部門經理,部門經理再找組長,組長再找組員,這樣就會輕松很多,
跳躍清單就類似于上面的層級制,所有的元素都在最下層用連結清單串着,每隔幾個元素選出一個組長,組長之間再選部門經理,
之是以叫做跳躍清單,是因為一個元素可能會同時存在于不同的層次中,例如下圖中間的元素,可以同時存在于L0、1、2 三層中,
定位插入點時,先在頂層進行定位,然後下潛到下一層,直到下潛到最底層,将新元素插入進去,用這種方式可以達到類似二分查找的效果。