【Redis深度歷險】那些年Redis的數據結構

  • 2019 年 11 月 5 日
  • 筆記

Redis端口號6379的來源

Redis的端口號是6379,但這個端口號並不是隨機選擇的,源於"MERZ",這個單詞在手機當中的對應數字就是6379。"MERZ"在Redis作者Antirez的好友圈當中代表愚蠢的意思。

數據結構

Redis的key只能是字符串,value可以是String,Hash,List,Sorted Set(Zset)。

String

Redis的字符串是動態字符串(SDS Simple Dynamic String ),內部結構有點兒類似於java的ArrayList,都是採取預分配來減少內存的頻繁擴容。如圖len是實際字符串的長度,capacity是預分配的空間(數組容量)。創建字符串時,len和capacity一樣長,使用位元組數組存放內容。

struct SDS<T> {      T capacity; // 數組容量      T len; // 數組長度      byte flags; // 特殊標識位      byte[] content; // 數組內容  }

  • 如果在1M以內,都是加倍擴充容量
  • 如果超過1M則,每次擴容1M
  • 字符串的最大容量是512M

String的一些基礎操作

  • 普通get set
127.0.0.1:6379> set name amber  OK  127.0.0.1:6379> get name  "amber"  127.0.0.1:6379> exists name  (integer) 1  127.0.0.1:6379> del name  (integer) 1  127.0.0.1:6379> get name  (nil)  127.0.0.1:6379>  
  • 批量mset,mget
127.0.0.1:6379> set name amber  OK  127.0.0.1:6379> set name2 nick  OK  127.0.0.1:6379> mget name name2  1) "amber"  2) "nick"  127.0.0.1:6379> mset name3 wade name4 hellen  OK  127.0.0.1:6379> mget name name2 name3 name4  1) "amber"  2) "nick"  3) "wade"  4) "hellen"  127.0.0.1:6379>  
  • 設置過期時間
  1. 第一種 expire
127.0.0.1:6379> set name amber  OK  127.0.0.1:6379> expire name 5  (integer) 1  127.0.0.1:6379> get name  "amber"  //等待5s  127.0.0.1:6379> get name  (nil)  127.0.0.1:6379> 
  1. 利用setex
    setex name 時間 value
127.0.0.1:6379> setex name 5 amber  OK  127.0.0.1:6379> get name  "amber"  127.0.0.1:6379> get name  (nil)  127.0.0.1:6379>  
  • 自增自減
127.0.0.1:6379> set age 18  OK  127.0.0.1:6379> incr age  (integer) 19  127.0.0.1:6379> incrby age 5  (integer) 24  127.0.0.1:6379> incrby age -5  (integer) 19  127.0.0.1:6379> decr age  (integer) 18  127.0.0.1:6379>  

List

Redis的list結構有點像Java中的LinkedList,但實際上地產不僅僅是簡單的linkedlist,底層是quicklist(太深入了等待作者以後學習…)

特點

list的插入刪除效率很高,時間複雜度為O(1),但是索引的定位就很慢,即O(n)

操作

  • 左進右出(隊列)
127.0.0.1:6379> lpush names amber nick wade  (integer) 3  127.0.0.1:6379> rpop names  "amber"  127.0.0.1:6379> rpop names  "nick"  127.0.0.1:6379> rpop names  "wade"  127.0.0.1:6379> rpop names  (nil)  127.0.0.1:6379>  

當然你也可以左近左出(棧),可以自己實驗一下。

  • 索引操作
  1. lindex相當於java的get(int index)根據索引取值,但是因為要遍歷鏈表,如果數據很大,導致開銷增大
  2. ltrim key index1 index2 保留index1和index2之間的數據
127.0.0.1:6379> lpush names amber nick wade  (integer) 3  127.0.0.1:6379> lindex names 0  "wade"  127.0.0.1:6379> lindex names 1  "nick"  127.0.0.1:6379> lindex names 2  "amber"  127.0.0.1:6379> ltrim names 0 1  127.0.0.1:6379> lindex names 0  "wade"  127.0.0.1:6379> lindex names 1  "nick"  127.0.0.1:6379> lindex names 2  (nil)  127.0.0.1:6379>  

hash(散列)

Redis的hash類似java中的HashMap

特點

Redis中的Hash進行rehash時區別於java中的HashMap。
在redis進行rehash時會同時保留新舊兩個結構,並在後續的定時任務當中慢慢把舊的數據移動到新數據。

操作

127.0.0.1:6379> hmset person name amber age 18  OK  127.0.0.1:6379> hgetall person  1) "name"  2) "amber"  3) "age"  4) "18"  127.0.0.1:6379> hget person name  "amber"  127.0.0.1:6379> hset person gender 1  (integer) 1  127.0.0.1:6379> hgetall person  1) "name"  2) "amber"  3) "age"  4) "18"  5) "gender"  6) "1"  

set

Redis中的set相當於java中的HashSet,內部相當於實現了一個字典

特點

value唯一

操作

127.0.0.1:6379> sadd names amber  (integer) 1  127.0.0.1:6379> sadd names amber  (integer) 0  127.0.0.1:6379> sadd names nick wade  (integer) 2  127.0.0.1:6379> smembers names  1) "amber"  2) "wade"  3) "nick"  

zset(sorted set)

Redis中的zset相當於java中sorted set和HashMap的結合。在set的基礎上還可以給value賦予score(排序的權重)

特點

zset因為有score需要排序,但是採用普通的鏈表查找銷量過低。因此zst採用層級制度。有點類似於國家->省級->市->xxx。最底層的鄉鎮肯帝就是我們的L0層級了,所有的元素都串聯在一起,每個幾個元素就選出市位於L2,同樣的道理每隔幾個L2層級的元素就選出省位於L3層級。當我們插入新的節點的時候,只需要從最頂層開始進行查找定位到相應位置就行了。是不是有點兒像數組的二分查找。

操作

其實還有一些操作,不過這裡就不展示了

127.0.0.1:6379> zadd names 2 amber  (integer) 1  127.0.0.1:6379> zadd names 3 wade  (integer) 1  127.0.0.1:6379> zadd names 1 nick  (integer) 1  127.0.0.1:6379> zrange names 0 2  1) "nick"  2) "amber"  3) "wade"  127.0.0.1:6379>  

數據結構知識點拓展

  • redis的所有數據結構都可以設置時間
1. 設置時間  expire key 時間  2. 查看時間  ttl key