我們知道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:
公眾號:二營長的筆記
閑話:用技術築起自己的風牆,當外面的槍林彈雨來臨的時候,至少可以來個“面對疾風吧!!!”