Redis基礎(chǔ)學(xué)習(xí):通用命令(二)
今天我們繼續(xù)學(xué)習(xí) Redis 剩余的一些通用命令。這些命令也都是非常簡單的命令,而且更重要的是,今天的內(nèi)容是我們基礎(chǔ)部分的最后一篇了哦,大家可要卯足精神堅(jiān)持學(xué)完啦。
數(shù)據(jù)類型查看
要查看一個 KEY 的數(shù)據(jù)類型直接使用 TYPE 命令就可以,我們就以五大基礎(chǔ)數(shù)據(jù)類型為例。
127.0.0.1:6379> SET a 123
OK
127.0.0.1:6379> LPUSH b a b c d
(integer) 4
127.0.0.1:6379> hmset c name zy age 18
OK
127.0.0.1:6379> ZADD d 1 a 2 b 3 c
(integer) 3
127.0.0.1:6379> SADD e a b c
(integer) 3
127.0.0.1:6379> TYPE a
string
127.0.0.1:6379> TYPE b
list
127.0.0.1:6379> TYPE c
hash
127.0.0.1:6379> TYPE d
zset
127.0.0.1:6379> TYPE e
set
還記得之前講過的 Bitmap、HyperLogLog、GEO 之類的類型或功能嗎?現(xiàn)在你可以試試用 TYPE 命令看看它們本質(zhì)上都是屬于哪種類型。
真正的內(nèi)部類型對象及調(diào)試
上面使用 TYPE 看到的只是基本的數(shù)據(jù)類型,但其實(shí)在 Redis 的內(nèi)部,不同的數(shù)據(jù)類型還有更深層次的優(yōu)化。我們把每一個 KEY 都看成是一個類型對象,就和在編程語言中的對象一樣。不同類型的 KEY 對象在使用的時(shí)候會根據(jù)條件再次選取不同類型的數(shù)據(jù)結(jié)構(gòu)。這些內(nèi)容,我們可以通過 OBJECT 相關(guān)的命令看到,它是一個復(fù)合命令,包含下面這些子命令。
127.0.0.1:6379> OBJECT HELP
1) OBJECT <subcommand> [<arg> [value] [opt] ...]. Subcommands are:
2) ENCODING <key>
3) Return the kind of internal representation used in order to store the value
4) associated with a <key>.
5) FREQ <key>
6) Return the access frequency index of the <key>. The returned integer is
7) proportional to the logarithm of the recent access frequency of the key.
8) IDLETIME <key>
9) Return the idle time of the <key>, that is the approximated number of
10) seconds elapsed since the last access to the key.
11) REFCOUNT <key>
12) Return the number of references of the value associated with the specified
13) <key>.
14) HELP
15) Prints this help.
OBJECT ENCODING
首先來看到的就是如何查看某個數(shù)據(jù)類型的真實(shí)數(shù)據(jù)存儲結(jié)構(gòu),使用的就是 OBJECT ENCODING 這個命令。從字面意思也可以看出,它是獲得對象的編碼類型。
127.0.0.1:6379> OBJECT ENCODING a
"int"
127.0.0.1:6379> OBJECT ENCODING b
"quicklist"
127.0.0.1:6379> OBJECT ENCODING c
"ziplist"
127.0.0.1:6379> OBJECT ENCODING d
"ziplist"
127.0.0.1:6379> OBJECT ENCODING e
"hashtable"
這都是個啥?別怕,官網(wǎng)文檔中有解釋。
- 字符串可以被編碼為 raw (常規(guī)字符串) 或者int (用字符串表示64位無符號整數(shù)這種編碼方式是為了節(jié)省空間)。
- 列表類型可以被編碼為ziplist 或者 linkedlist。ziplist 是為了節(jié)省較小的列表空間而設(shè)計(jì)的一種特殊編碼方式。
- 集合被編碼為 intset 或者 hashtable。 intset 是為了存儲數(shù)據(jù)的較小集合而設(shè)計(jì)的一種特殊編碼方式。
- 哈希表可以被編碼為 zipmap 或者h(yuǎn)ashtable。zipmap 是專為了較小的哈希表而設(shè)計(jì)的一種特殊編碼方式。
- 有序集合被編碼為ziplist 或者 skiplist 格式。ziplist可以表示較小的有序集合, skiplist 表示任意大小的有序集合。
其實(shí)呀,就是如果某個 Key 在數(shù)據(jù)量較小的情況下,會使用某一種數(shù)據(jù)結(jié)構(gòu),而內(nèi)部的數(shù)量量大的時(shí)候會使用另一種數(shù)據(jù)結(jié)構(gòu)。比如說普通字符串是 raw ,但如果你這個 KEY 中只是數(shù)字的話,那么它會使用 int 類型來保存。同樣的,較小的 LIST 會使用 ziplist ,而當(dāng)數(shù)據(jù)項(xiàng)超過一定數(shù)量或者某個數(shù)據(jù)項(xiàng)的內(nèi)容長度非常大時(shí),就會變成 linkedlist 。這個 linkedlist 大家應(yīng)該不陌生吧,就是鏈表的意思。所以 LIST 在頭尾插入和刪除的時(shí)候能達(dá)到 O(1) 的速度。
不過我們這里顯示出來的 LIST 的數(shù)據(jù)結(jié)構(gòu)是 quicklist ,它是 Redis3.2 引入的數(shù)據(jù)結(jié)構(gòu),是結(jié)合了 ziplist 和 linkedlist 的一種快速鏈表。現(xiàn)在默認(rèn)情況下 LIST 都是使用 quicklist 這種結(jié)構(gòu)了。
光說不練假把式,注意看上面的 c 這個 Hash 的類型是 ziplist ,我們直接給它增加一個內(nèi)容比較長的 field ,看看它的數(shù)據(jù)結(jié)構(gòu)會不會發(fā)生改變。
127.0.0.1:6379> hmset c test abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxy
OK
127.0.0.1:6379> OBJECT ENCODING c
"hashtable"
怎么樣,是不是發(fā)生變化了,一般來說,集合中超過多少個元素,或者某一個元素的內(nèi)容長度大于多少個字節(jié),就無法使用 ziplist 這種數(shù)據(jù)結(jié)構(gòu)了。大家可以自己再拿 Set 或者 Sorted Set 試下哦。具體的內(nèi)容我們將在進(jìn)階系列的文章中再進(jìn)行深入的學(xué)習(xí)。
OBJECT REFCOUNT
Redis 為了節(jié)省內(nèi)存會在初始化服務(wù)器時(shí),創(chuàng)建一萬個字符串對象,這些對象包含了1到9999的所有整數(shù)值,當(dāng)服務(wù)器需要用到值為0到9999的字符串對象時(shí),服務(wù)器就會使用這些共享對象,而不是創(chuàng)建新的對象
127.0.0.1:6379> set a 9999
OK
127.0.0.1:6379> set b1 100000
OK
127.0.0.1:6379> OBJECT REFCOUNT a
(integer) 2147483647
127.0.0.1:6379> OBJECT REFCOUNT b1
(integer) 1
OBJECT REFCOUNT 就是用來查看對象的線上服務(wù)情況的,它只對value >= 0 && value < OBJ_SHARED_INTEGERS 的數(shù)值類對象生效,除此之外的其他redis對象,都不會相互引用。
OBJECT IDLETIME
IDLE 一般指的是閑置、懶散的意思,很明顯,這個命令就是獲取指定的 KEY 從被存儲之后空閑的時(shí)間,以秒為單位的。
127.0.0.1:6379> OBJECT IDLETIME a
(integer) 32
127.0.0.1:6379> OBJECT IDLETIME b
(integer) 156
127.0.0.1:6379> OBJECT IDLETIME c
(integer) 161
127.0.0.1:6379> OBJECT IDLETIME d
(integer) 167
127.0.0.1:6379> OBJECT IDLETIME e
(integer) 169
在上個例子中我們修改過了 a 的值,所以 a 的空閑時(shí)間發(fā)生了改變。
OBJECT FREQ
最后這個 FREQ 子命令返回的是在 LFU 淘汰算法下,對于某個 KEY 的使用計(jì)數(shù)。
127.0.0.1:6379> CONFIG SET maxmemory-policy allkeys-lfu
OK
127.0.0.1:6379> OBJECT FREQ a
(integer) 0
127.0.0.1:6379> get a
"10000000"
127.0.0.1:6379> OBJECT FREQ a
(integer) 1
127.0.0.1:6379> OBJECT FREQ b
(integer) 0
當(dāng) a 被使用了一次之后,這個 KEY 下面的相關(guān)計(jì)數(shù)器就會加一。關(guān)于具體的淘汰算法的問題我們在之后的進(jìn)階系列中再學(xué)習(xí),現(xiàn)在你只要知道,必須要使用 LFU 相關(guān)的緩存淘汰算法,這個命令才可以使用。
排序
對于 List、Set、Sorted Set 來說,我們可以通過 SORT 這個命令來對它們的值進(jìn)行排序。這個命令的參數(shù)比較多,它的命令簽名是這樣的。
SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]
先來進(jìn)行一個簡單的不加任何參數(shù)的排序,默認(rèn)情況是正序排列。
127.0.0.1:6379> LPUSH b1 5 2 4 5 7 3 1 7 8 10 2 8 9
(integer) 13
127.0.0.1:6379> SORT b1
1) "1"
2) "2"
3) "2"
4) "3"
5) "4"
6) "5"
7) "5"
8) "7"
9) "7"
10) "8"
11) "8"
12) "9"
13) "10"
直接使用 DESC 就可以進(jìn)行倒序的排列,同時(shí)還可以使用 LIMIT 控制偏移量和數(shù)量,和 MySQL 中非常類似。
127.0.0.1:6379> SORT b1 LIMIT 2 5 DESC
1) "8"
2) "8"
3) "7"
4) "7"
5) "5"
對于數(shù)字來說,排序非常方便,那么能不能對字符串進(jìn)行排序呢?
127.0.0.1:6379> SMEMBERS e
1) "a"
2) "c"
3) "b"
127.0.0.1:6379> SORT e
(error) ERR One or more scores can't be converted into double
127.0.0.1:6379> SORT e ALPHA
1) "a"
2) "b"
3) "c"
通過上面的例子可以看出,直接對字符類型的元素排序是不行的,但是我們可以加一個 ALPAH 參數(shù),這樣就可以對字符串進(jìn)行排序了。另外我們可以通過 STORE 參數(shù)將排序后的內(nèi)容放到另一個 KEY 中。
127.0.0.1:6379> SORT e ALPHA STORE e1
(integer) 3
127.0.0.1:6379> SMEMBERS e1
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> type e1
list
127.0.0.1:6379> LRANGE e1 0 -1
1) "a"
2) "b"
3) "c"
注意看這個例子中,排序的結(jié)果會是一個 List 類型。
除了上面這些簡單的排序操作外,我們還可以通過 BY 和 GET 命令實(shí)現(xiàn)非常復(fù)雜的排序操作。BY 是通過外部元素的 KEY 作為權(quán)重來進(jìn)行排序。還是拿上面那個 e 中的 Set 元素為例,這回我們定義三個外部 KEY ,它們的鍵名中包含 Set 集合中的元素信息,它們的值是用于排序的數(shù)字類型。
127.0.0.1:6379> mset es:b 2 es:c 5 es:a 4
OK
127.0.0.1:6379> SORT e BY es:*
1) "b"
2) "a"
3) "c"
神奇嗎?現(xiàn)在的順序是按照新定義的那三個外部 KEY 的值來排序的,不信?你可以再通過 GET 參數(shù)來驗(yàn)證。
127.0.0.1:6379> SORT e BY es:* get es:*
1) "2"
2) "4"
3) "5"
GET 參數(shù)是將排序的結(jié)果信息顯示為外部鍵的值信息。如果想跳過排序的元素,可以直接使用一個不存在的 KEY 。
127.0.0.1:6379> SORT e BY es:noone
1) "a"
2) "c"
3) "b"
說了半天 BY 和 GET ,感覺很強(qiáng)大,但是有什么實(shí)際用處呢?咱們來看一個例子,通過 BY 和 GET 來對多個 Hash 數(shù)據(jù)進(jìn)行排序。
127.0.0.1:6379> hmset es2:a title Bob score 30
OK
127.0.0.1:6379> hmset es2:b title Alice score 70
OK
127.0.0.1:6379> hmset es2:c title Mary score 60
OK
127.0.0.1:6379> SORT e BY es2:*->socre get es2:*->title
1) "Bob"
2) "Alice"
3) "Mary"
127.0.0.1:6379> SORT e BY es2:*->socre get es2:*->title DESC
1) "Mary"
2) "Alice"
3) "Bob"
看明白啥意思了沒?-> 這個符號,就像我們在 PHP 中調(diào)用對象的屬性或方法一樣的符號,在這里也是類似的概念。BY xxx->score 就是通過 Hash 對象中 score 字段進(jìn)行排序 ,然后再使用 GET 來獲得 ->title 標(biāo)題,是不是酷斃了。
刪除
最后的最后,我們再來看兩個非常簡單的命令,它們都是用于刪除數(shù)據(jù)的。
127.0.0.1:6379> del es:a es:b
(integer) 2
127.0.0.1:6379> UNLINK es:c es:d
(integer) 1
127.0.0.1:6379> keys es*
1) "es2"
2) "es2:b"
3) "es2:a"
4) "es3"
5) "es1"
6) "es2:c"
DEL 和 UNLINK 命令都可以刪除一個或多個鍵,但是,DEL 是在主線程中運(yùn)行的,大量刪除時(shí)會阻塞線程影響效率,而 UNLINK 是將鍵的引用標(biāo)記斷開,然后通過其它的線程回收內(nèi)存,真正的刪除會在異步完成。
DEL 刪除一個普通的 String 類型的 Key 是 O(1) ,但是,如果是 List、Hash、Set、Sorted Set 就不會這么輕松了,會達(dá)到 O(n) 的級別。另外,假如一個 String 類型的值有幾百兆,要刪它的話,也會產(chǎn)生阻塞。
因此,估計(jì)大家也都猜到了,線上繁忙的生產(chǎn)環(huán)境,或許使用 UNLINK 會更合適一些。這也是 bigkey 刪除的一個面試點(diǎn)。
總結(jié)
完結(jié)撒花!!
對于 Redis 的基礎(chǔ)命令的學(xué)習(xí)就告一段落了,其實(shí)明眼人應(yīng)該一眼就能看出,我這又是開始在刷 Redis 的文檔了。接下來我們就要進(jìn)入更深層次的學(xué)習(xí),也就是進(jìn)階部分的學(xué)習(xí)。這部分,少不了各種面試的八股文。不過我們也并不是以面試為主,而是通過面試時(shí)的這些常見問題,來更加深入的理解 Redis ,從而能更進(jìn)一步地用好它。
繼續(xù)跟著,別掉隊(duì)哦!