标题:【Redis15】Redis基础:通用命令(二)
Redis基础学习:通用命令(二)
今天我们继续学习 Redis 剩余的一些通用命令。这些命令也都是非常简单的命令,而且更重要的是,今天的内容是我们基础部分的最后一篇了哦,大家可要卯足精神坚持学完啦。
数据类型查看
要查看一个 KEY 的数据类型直接使用 TYPE 命令就可以,我们就以五大基础数据类型为例。
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 之类的类型或功能吗?现在你可以试试用 TYPE 命令看看它们本质上都是属于哪种类型。
真正的内部类型对象及调试
上面使用 TYPE 看到的只是基本的数据类型,但其实在 Redis 的内部,不同的数据类型还有更深层次的优化。我们把每一个 KEY 都看成是一个类型对象,就和在编程语言中的对象一样。不同类型的 KEY 对象在使用的时候会根据条件再次选取不同类型的数据结构。这些内容,我们可以通过 OBJECT 相关的命令看到,它是一个复合命令,包含下面这些子命令。
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
首先来看到的就是如何查看某个数据类型的真实数据存储结构,使用的就是 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"
这都是个啥?别怕,官网文档中有解释。
字符串可以被编码为 raw (常规字符串) 或者int (用字符串表示64位无符号整数这种编码方式是为了节省空间)。
列表类型可以被编码为ziplist 或者 linkedlist。ziplist 是为了节省较小的列表空间而设计的一种特殊编码方式。
集合被编码为 intset 或者 hashtable。 intset 是为了存储数据的较小集合而设计的一种特殊编码方式。
哈希表可以被编码为 zipmap 或者hashtable。zipmap 是专为了较小的哈希表而设计的一种特殊编码方式。
有序集合被编码为ziplist 或者 skiplist 格式。ziplist可以表示较小的有序集合, skiplist 表示任意大小的有序集合。
其实呀,就是如果某个 Key 在数据量较小的情况下,会使用某一种数据结构,而内部的数量量大的时候会使用另一种数据结构。比如说普通字符串是 raw ,但如果你这个 KEY 中只是数字的话,那么它会使用 int 类型来保存。同样的,较小的 LIST 会使用 ziplist ,而当数据项超过一定数量或者某个数据项的内容长度非常大时,就会变成 linkedlist 。这个 linkedlist 大家应该不陌生吧,就是链表的意思。所以 LIST 在头尾插入和删除的时候能达到 O(1) 的速度。
不过我们这里显示出来的 LIST 的数据结构是 quicklist ,它是 Redis3.2 引入的数据结构,是结合了 ziplist 和 linkedlist 的一种快速链表。现在默认情况下 LIST 都是使用 quicklist 这种结构了。
光说不练假把式,注意看上面的 c 这个 Hash 的类型是 ziplist ,我们直接给它增加一个内容比较长的 field ,看看它的数据结构会不会发生改变。
127.0.0.1:6379> hmset c test abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxy
OK
127.0.0.1:6379> OBJECT ENCODING c
"hashtable"
怎么样,是不是发生变化了,一般来说,集合中超过多少个元素,或者某一个元素的内容长度大于多少个字节,就无法使用 ziplist 这种数据结构了。大家可以自己再拿 Set 或者 Sorted Set 试下哦。具体的内容我们将在进阶系列的文章中再进行深入的学习。
OBJECT REFCOUNT
Redis 为了节省内存会在初始化服务器时,创建一万个字符串对象,这些对象包含了1到9999的所有整数值,当服务器需要用到值为0到9999的字符串对象时,服务器就会使用这些共享对象,而不是创建新的对象
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 就是用来查看对象的线上服务情况的,它只对value >= 0 && value < OBJ_SHARED_INTEGERS 的数值类对象生效,除此之外的其他redis对象,都不会相互引用。
OBJECT IDLETIME
IDLE 一般指的是闲置、懒散的意思,很明显,这个命令就是获取指定的 KEY 从被存储之后空闲的时间,以秒为单位的。
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 的空闲时间发生了改变。
OBJECT FREQ
最后这个 FREQ 子命令返回的是在 LFU 淘汰算法下,对于某个 KEY 的使用计数。
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
当 a 被使用了一次之后,这个 KEY 下面的相关计数器就会加一。关于具体的淘汰算法的问题我们在之后的进阶系列中再学习,现在你只要知道,必须要使用 LFU 相关的缓存淘汰算法,这个命令才可以使用。
排序
对于 List、Set、Sorted Set 来说,我们可以通过 SORT 这个命令来对它们的值进行排序。这个命令的参数比较多,它的命令签名是这样的。
SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]
先来进行一个简单的不加任何参数的排序,默认情况是正序排列。
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 就可以进行倒序的排列,同时还可以使用 LIMIT 控制偏移量和数量,和 MySQL 中非常类似。
127.0.0.1:6379> SORT b1 LIMIT 2 5 DESC
1) "8"
2) "8"
3) "7"
4) "7"
5) "5"
对于数字来说,排序非常方便,那么能不能对字符串进行排序呢?
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 参数,这样就可以对字符串进行排序了。另外我们可以通过 STORE 参数将排序后的内容放到另一个 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"
注意看这个例子中,排序的结果会是一个 List 类型。
除了上面这些简单的排序操作外,我们还可以通过 BY 和 GET 命令实现非常复杂的排序操作。BY 是通过外部元素的 KEY 作为权重来进行排序。还是拿上面那个 e 中的 Set 元素为例,这回我们定义三个外部 KEY ,它们的键名中包含 Set 集合中的元素信息,它们的值是用于排序的数字类型。
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"
神奇吗?现在的顺序是按照新定义的那三个外部 KEY 的值来排序的,不信?你可以再通过 GET 参数来验证。
127.0.0.1:6379> SORT e BY es:* get es:*
1) "2"
2) "4"
3) "5"
GET 参数是将排序的结果信息显示为外部键的值信息。如果想跳过排序的元素,可以直接使用一个不存在的 KEY 。
127.0.0.1:6379> SORT e BY es:noone
1) "a"
2) "c"
3) "b"
说了半天 BY 和 GET ,感觉很强大,但是有什么实际用处呢?咱们来看一个例子,通过 BY 和 GET 来对多个 Hash 数据进行排序。
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 中调用对象的属性或方法一样的符号,在这里也是类似的概念。BY xxx->score 就是通过 Hash 对象中 score 字段进行排序 ,然后再使用 GET 来获得 ->title 标题,是不是酷毙了。
删除
最后的最后,我们再来看两个非常简单的命令,它们都是用于删除数据的。
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 是在主线程中运行的,大量删除时会阻塞线程影响效率,而 UNLINK 是将键的引用标记断开,然后通过其它的线程回收内存,真正的删除会在异步完成。
DEL 删除一个普通的 String 类型的 Key 是 O(1) ,但是,如果是 List、Hash、Set、Sorted Set 就不会这么轻松了,会达到 O(n) 的级别。另外,假如一个 String 类型的值有几百兆,要删它的话,也会产生阻塞。
因此,估计大家也都猜到了,线上繁忙的生产环境,或许使用 UNLINK 会更合适一些。这也是 bigkey 删除的一个面试点。
总结
完结撒花!!
对于 Redis 的基础命令的学习就告一段落了,其实明眼人应该一眼就能看出,我这又是开始在刷 Redis 的文档了。接下来我们就要进入更深层次的学习,也就是进阶部分的学习。这部分,少不了各种面试的八股文。不过我们也并不是以面试为主,而是通过面试时的这些常见问题,来更加深入的理解 Redis ,从而能更进一步地用好它。
继续跟着,别掉队哦!
视频链接
微信文章地址:https://mp.weixin.qq.com/s/eiMRo_iJv0EjgGCBFTrfLg