1. 程式人生 > >Redis實戰 | 5種Redis資料型別詳解

Redis實戰 | 5種Redis資料型別詳解

我們知道Redis是目前非常主流的KV資料庫,它因高效能的讀寫能力而著稱,其實還有另外一個優勢,就是Redis提供了更加豐富的資料型別,這使得Redis有著更加廣泛的使用場景。那Redis提供給使用者的有哪些資料型別呢?主要有:string(字串)、List(列表)、Set(集合)、Hash(雜湊)、Zset(有序集合)、HyperLogLogs(計算基數用的一種資料結構)、Streams(Redis 5.0提供一種建模日誌用的全新資料結構)。

需要注意的是這裡說的資料型別是指Redis值的資料型別,而Redis鍵的型別總是string。

本文主要詳解一下前5種,也就是最常用的5種資料型別。剩下兩種可上Redis官網(redis.io)自行了解下。另外,Redis已經是目前Java程式設計師面試必問內容,而 “Redis有哪些資料型別?”

更是面試官張口就來的基礎問題。如果連這第一問都過不了,那基本上Redis這塊已經涼涼了。

string | 字串型別

redis的字串型別,可以儲存字串、整數或者浮點數。如果儲存的是整數或者浮點數,還能執行自增或者自減操作。

並且redis的string型別是二進位制安全的,它可以包含任何資料,比如一個序列化的物件、一個圖片位元組流等。不過儲存大小是由上限的-512M

這裡解釋下二進位制安全的含義:簡單的來說,就是字串不是根據某種特殊的標誌位來(C語言的\0)解析的,無論輸入的是什麼,總能保證輸出是處理的原始輸入而不是根據某種特殊格式來處理。

redis是怎麼實現string型別的二進位制安全的呢?

答案是Sds (Simple Dynamic String,簡單動態字串),Redis底層定義了自己的一種資料結構。(簡單瞭解下)

typedef char *sds;

struct sdshdr {

    // buf 已佔用長度
    int len;

    // buf 剩餘可用長度
    int free;

    // 實際儲存字串資料的地方
    char buf[];
};

操作字串的一些命令

基礎set、get、del命令及示例

get keyname 獲取儲存在給定鍵中的值
set keyname value 設定儲存唉給定鍵中的值
del keyname 刪除儲存在給定鍵中的值(通用命令,適用於所有型別)

127.0.0.1:6379> set happy today
OK
127.0.0.1:6379> get happy
"today"
127.0.0.1:6379> del happy
(integer) 1
127.0.0.1:6379> get happy
(nil)
127.0.0.1:6379> 

自增和自減命令

incr keyname 將鍵儲存的值加1
decr kename 將鍵儲存的是減1
incrby keyname amount 將鍵儲存的值加上整數amount
decrby keyname amount 將鍵儲存的值減去整數amount
incrbyfloat keyname amount 將鍵儲存的值加上浮點數amount

127.0.0.1:6379> set number 1
OK
127.0.0.1:6379> get number
"1"
127.0.0.1:6379> incr number
(integer) 2
127.0.0.1:6379> get number
"2"
127.0.0.1:6379> decr number
(integer) 1
127.0.0.1:6379> get number
"1"
127.0.0.1:6379> incrby number 3
(integer) 4
127.0.0.1:6379> get number
"4"
127.0.0.1:6379> decrby number 2
(integer) 2
127.0.0.1:6379> get number
"2"
127.0.0.1:6379> incrbyfloat number 1.23
"3.23"
127.0.0.1:6379> get number
"3.23"

子串和二進位制位命令

append keyname value 追加value值到指定字串末尾
getrange keyname start end 獲取start到end範圍的所有字元組成的子串,包括start和end在內
setrange keyname offset value 從偏移量 offset 開始, 用 value 引數覆寫(overwrite)鍵 keyname 儲存的字串值。
getbit keyname offset 對 keyname 所儲存的字串值,獲取指定偏移量上的位(bit)。
setbit keyname offset value 對 keyname 所儲存的字串值,設定或清除指定偏移量上的位(bit)。

注意redis的索引以0為開始

127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> append hello ,java
(integer) 10
127.0.0.1:6379> get hello
"world,java"
127.0.0.1:6379> getrange hello 2 5
"rld,"
127.0.0.1:6379> setrange hello 6 redis
(integer) 11
127.0.0.1:6379> get hello
"world,redis"
127.0.0.1:6379> 
127.0.0.1:6379> setbit bitstr 100 1
(integer) 0
127.0.0.1:6379> getbit bitstr 100
(integer) 1
127.0.0.1:6379> get bitstr
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b"
127.0.0.1:6379> 

其他幾個重要的命令

setnx key value 只在鍵 key 不存在的情況下, 將鍵 key 的值設定為 value;若鍵 key 已經存在, 則 SETNX 命令不做任何動作。
setex key seconds value 將鍵 key 的值設定為 value , 並將鍵 key 的生存時間設定為 seconds 秒鐘。如果鍵 key 已經存在, 那麼 SETEX 命令將覆蓋已有的值。

說明一下:

  • SETNX 是『SET if Not eXists』(如果不存在,則 SET)的簡寫。命令在設定成功時返回 1 , 設定失敗時返回 0 。
  • SETEX命令相當於SET key value 和 EXPIRE key seconds # 設定生存時間兩條命令的效果,但是SETEX是一個原子操作。
127.0.0.1:6379> exists mark
(integer) 0
127.0.0.1:6379> setnx mark abcd
(integer) 1
127.0.0.1:6379> setnx mark defg
(integer) 0
127.0.0.1:6379> get mark
"abcd"
127.0.0.1:6379> setex cachekey 20 ak98
OK
127.0.0.1:6379> get cachekey
"ak98"
127.0.0.1:6379> ttl cachekey
(integer) 2

List | 列表型別

Redis的列表型別和許多程式語言中的列表型別類似,可以有序地儲存多個字串

支援從列表的左端和右端推入或彈出元素。Redis列表的底層實現是壓縮列表(redis內容自己實現的資料結構)和雙端連結串列。看下圖

圖片來自《Redis 設計與實現》

列表操作命令詳解

lpush key value [value...]

將一個或者多個value值插入列表的表頭。如果 key 不存在,會建立一個空列表並執行 LPUSH 操作。當 key 存在但不是列表型別時,返回一個錯誤。

執行 LPUSH 命令後,會返回列表的長度。

127.0.0.1:6379> lpush listkey a
(integer) 1
127.0.0.1:6379> lpush listkey a b c
(integer) 4
127.0.0.1:6379> lrange listkey 0 -1
1) "c"
2) "b"
3) "a"
4) "a"
127.0.0.1:6379> 
  • list型別可以加入重複的元素,這個和後面要說的set(集合型別)不同。
  • lrange listkey 0 -1 是獲取整個列表的內容
  • 類似地rpush命令是從列表右端加入元素

LPOP key

從列表的左端彈出一個值,並返回被彈出的值

127.0.0.1:6379> lrange listkey 0 -1
1) "c"
2) "b"
3) "a"
4) "a"
127.0.0.1:6379> lpop listkey
"c"
127.0.0.1:6379> lrange listkey 0 -1
1) "b"
2) "a"
3) "a"
127.0.0.1:6379> 

lrange key start end

獲取列表key在給定start到end範圍上的所有元素值。

0表示第一個元素,-1表示最後一個元素。

127.0.0.1:6379> lrange listkey 0 -1
1) "b"
2) "a"
3) "a"
127.0.0.1:6379> lrange listkey 0 1
1) "b"
2) "a"
127.0.0.1:6379>

lindex key index

獲取列表在給定index位置上的單個元素值。

可以是-1,代表最後一個元素,-2表示倒數第二個元素,以此類推。

127.0.0.1:6379> lrange listkey 0 -1
1) "b"
2) "a"
3) "a"
127.0.0.1:6379> lindex listkey 0
"b"
127.0.0.1:6379> lindex listkey -1
"a"
127.0.0.1:6379> lindex listkey 3
(nil)
127.0.0.1:6379> 

blpop key [key …] timeout

blpop 是阻塞式的彈出命令,它是lpop key 命令的阻塞版本。當給定列表內沒有任何元素可供彈出的時候,連線將被 blpop 命令阻塞,直到等待超時或發現可彈出元素為止。

當給定多個 key 引數時,按引數 key 的先後順序依次檢查各個列表,彈出第一個非空列表的頭元素。

因此可以分兩種情況討論,一種是至少有一個key存在且是非空列表,則blpop命令不會阻塞,另外是blpop命令中的列表是空列表,此時會在超時時間內阻塞。

先看下非阻塞的場景,返回值是第一個非空列表名和被彈出元素。

127.0.0.1:6379> lpush list1 hello java
(integer) 2
127.0.0.1:6379> lpush list2 hello redis
(integer) 2
127.0.0.1:6379> blpop list2 list1 list3 0
1) "list2"
2) "redis"
127.0.0.1:6379> 

阻塞的場景,在執行了blpop book1 book2 300 命令後會一直阻塞住。

127.0.0.1:6379> exists book1
(integer) 0
127.0.0.1:6379> exists book2
(integer) 0
127.0.0.1:6379> blpop book1 book2 300
阻塞在這裡了

這個時候,我們如果在開另外一個redis客戶端,執行如下lpush命令往book1列表中推入一個元素。

127.0.0.1:6379> lpush book1 springboot
(integer) 1
127.0.0.1:6379> 

此時,再回到原來阻塞的客戶端,已經彈出了元素。

127.0.0.1:6379> exists book1
(integer) 0
127.0.0.1:6379> exists book2
(integer) 0
127.0.0.1:6379> blpop book1 book2 300
1) "book1"
2) "springboot"
(237.45s)
127.0.0.1:6379> 

通過利用Redis列表型別的阻塞式命令的特性,我們最容易想到的就是可以用它實現一個簡易版的訊息佇列。

set | 集合型別

Redis的集合以無序的方式儲存多個不同的元素。這裡要注意的是無序和不同。

除了對集合能快速執行新增、刪除、檢查一個元素是否在集合中之外,還可以對多個集合執行交集、並集和差集運算。

底層實現概述

Redis的集合型別底層實現主要是通過一種叫做字典的資料結構。不過Redis為了追求極致的效能,會根據儲存的值是否是整數,選擇一種intset的資料結構。當滿足一定條件後,會切換成字典的實現。

這裡大概解釋下字典: 其實是由一集鍵值對(key-value pairs)組成, 各個鍵值對的鍵各不相同, 程式可以新增新的鍵值對到字典中, 或者基於鍵進行查詢、更新或刪除等操作。

Redis的set(集合)在使用字典資料結構儲存資料時,將元素儲存到字典的鍵裡面, 而字典的值則統一設為 NULL 。

集合型別操作命令詳解

sadd key member [member...]

將一個或者多個元素新增到集合key中,已存在於集合中的元素將被忽略。返回新新增的元素數量,不包括忽略的元素。

srem key member [member...]

移除集合中的一個或多個元素,不存在的元素將被忽略。返回被成功移除的元素數量。

sismember key meber

檢查元素member是否存在於集合key中。如果是返回1,不是或者key不存在,返回0。

scard key 返回集合包含的元素數量
spop key 隨機移除集合中的一個元素,並返回被移除元素。
smembers key 返回集合中包含的所有元素

127.0.0.1:6379> sadd set1 java spring redis
(integer) 3
127.0.0.1:6379> smembers set1
1) "redis"
2) "spring"
3) "java"
127.0.0.1:6379> scard set1
(integer) 3
127.0.0.1:6379> srem set1 spring
(integer) 1
127.0.0.1:6379> sismember set1 spring
(integer) 0
127.0.0.1:6379> smembers set1
1) "redis"
2) "java"
127.0.0.1:6379> sadd set1 mysql spring
(integer) 2
127.0.0.1:6379> spop set1
"redis"
127.0.0.1:6379> smembers set1
1) "mysql"
2) "spring"
3) "java"
127.0.0.1:6379>

下面是一些用於處理多個集合的一些命令

sdiff key [key...] 返回存在於第一個集合,但不存在於其他集合中的元素(數學上的差集運算)
sinter key [key...] 返回同時存在於所有集合中的元素(數學上的交集運算)
sunion key [key...] 返回至少存在於一個集合中的元素(數學上的並集運算)

127.0.0.1:6379> smembers set1
1) "mysql"
2) "spring"
3) "java"
127.0.0.1:6379> smembers set2
1) "mysql"
2) "springboot"
3) "redis"
127.0.0.1:6379> sdiff set1 set2
1) "java"
2) "spring"
127.0.0.1:6379> sinter set1 set2
1) "mysql"
127.0.0.1:6379> sunion set1 set2
1) "mysql"
2) "springboot"
3) "java"
4) "spring"
5) "redis"
127.0.0.1:6379>

hash | 散列表(雜湊表)

Redis的hash型別其實就是一個縮減版的redis。它儲存的是鍵值對,將多個鍵值對儲存到一個redis鍵裡面。

底層實現概述

hash型別的底層主要也是基於字典這種資料結構來實現的。


redis內部在實現hash資料型別的時候是使用了兩種資料結構。在建立一個空的hash表時,預設使用的是ziplist的資料結構,滿足一定條件後會轉成字典的形式。

散列表操作命令詳解

hmget hash-key key [key...] 從散列表裡面獲取一個或多個鍵的值
hmset hash-key key value [key value...] 為散列表裡面的一個或多個鍵設定值
hdel hash-key key [key...] 刪除散列表裡面的一個或多個鍵值對,返回刪除成功的鍵值對的數量
hlen hash-key 返回散列表包含的鍵值對的數量
hexists hash-key key 檢查給定的鍵是否存在於散列表中
hkeys hash-key 獲取雜湊包含的所有鍵
hvals hash-key 獲取雜湊包含的所有值
hgetall hash-key 獲取雜湊包含的所有鍵值對

127.0.0.1:6379> hmset hash1 username tom email 123@123 year 12
OK
127.0.0.1:6379> hmget hash1 email
1) "123@123"
127.0.0.1:6379> hlen hash1
(integer) 3
127.0.0.1:6379> hdel hash1 year
(integer) 1
127.0.0.1:6379> hexists hash1 year
(integer) 0
127.0.0.1:6379> hkeys hash1
1) "username"
2) "email"
127.0.0.1:6379> hvals hash1
1) "tom"
2) "123@123"
127.0.0.1:6379> hgetall hash1
1) "username"
2) "tom"
3) "email"
4) "123@123"
127.0.0.1:6379>

zset | 有序集合

有序集合相比較於集合,多個有序兩個字,我們知道set集合型別儲存的元素是無序的,那Redis有序集合是怎麼保證有序的?使用分值,有序集合裡儲存這成員與分值之間的對映,並提供了分值處理命令,以及根據分值的大小有序地獲取成員或分值的命令。

底層實現概述

Redis有序集合的實現使用了一種叫跳躍表的資料結構(簡稱跳錶,可自行查閱),同時也使用到了前面提到的壓縮列表。也是滿足一定條件的話,會自行轉換。

有序集合操作命令詳解

zadd z-key score memer [score member...] 將帶有給定分值的成員新增到有序集合裡面
zrem z-key member [member...] 從有序集合裡面移除給定的成員,並返回被移除成員的數量
zcard z-key 返回有序集合包含的成員數量
zincrby z-key increment member 將member成員的分值加上increment
zcount z-key min max 返回分值介於min和max之間的成員數量
zrank z-key member 返回成員member在有序集合中的排名
zscore z-key member 返回成員member的分值
zrange z-key start stop [withscores] 返回有序集合中排名介於start和stop之間的成員,如果給定了可選的withscores選項,name命令會將成員的分值也一併返回。

zrevrank z-key member 返回有序集合裡成員member的排名,成員按照分值從大到小排列。
zrevrange z-key start stop 返回有序集合給定排名範圍內的成員,成員按照分值從大到小排列。
zrangebyscore z-key min max 返回有序集合中分值介於min和max之間的所有成員

127.0.0.1:6379> zadd zset1 10 a 12 b 1 c 3 d 20 e
(integer) 5
127.0.0.1:6379> zcard zset1
(integer) 5
127.0.0.1:6379> zcount zset1 2 10
(integer) 2
127.0.0.1:6379> zrank zset1 d
(integer) 1
127.0.0.1:6379> zscore zset1 e
"20"
127.0.0.1:6379> zrange zset1 3 5
1) "b"
2) "e"
127.0.0.1:6379> zrevrank zset1 d
(integer) 3
127.0.0.1:6379> zrevrange zset1 3 5
1) "d"
2) "c"
127.0.0.1:6379> zrangebyscore zset1 5 10
1) "a"
127.0.0.1:6379> 

作者Info:
公眾號:二營長的筆記
閒話:用技術築起自己的風牆,當外面的槍林彈雨來臨的時候,至少可以來個“面對疾風吧!!!”