一. 命令类型
Redis中操作键的命令可以分为两类。
- 一种命令可以对任意类型的键执行,比如说DEL,EXPIRE,RENAME,TYPE,OBJECT命令等。
举个例子:
#字符串键
127.0.0.1:6379> set msg "hello world"
OK
#列表键
127.0.0.1:6379> rpush number 1 2 3
(integer) 3
#集合键
127.0.0.1:6379> sadd fruits apple banana cherry
(integer) 3
127.0.0.1:6379> keys *
1) "number"
2) "msg"
3) "fruits"
127.0.0.1:6379> del msg
(integer) 1
127.0.0.1:6379> del fruits
(integer) 1
127.0.0.1:6379> del number
(integer) 1
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379>
- 另一种命令是对特定类型的键执行
1. SET, GET, APPEND,STRLEN等命令只能对字符串键使用。
2. HDEL, HSET, HGET,HLEN等命令只能对哈希键使用。
3. RPUSH, LPOP, LINSERT, LLEN等命令只能对列表键使用。
4. SADD, SPOP, SINTER, SCARD等命令只能对集合键使用。
5. ZADD, ZCARD, ZRANK, ZSCORE等命令只能对有序集合键使用。
例如:
对字符串键使用,只能对列表键使用的LLEN命令,redis返回了一个类型错误。
127.0.0.1:6379> set msg "hello world"
OK
127.0.0.1:6379> get msg
"hello world"
127.0.0.1:6379> llen msg
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379>
二.类型检测的实现
从上面的报错可以看出,为了确保只有指定类型的键可以执行某些特殊命令,在执行一个类型特定的命令之前,Redis会先检测输入键的类型是否正确,然后再决定是否执行给定的命令。
类型特定命令所进行的类型检查是通过redisObject结构中的type属性来实现的。
流程图如下:
三. 多态命令的实现
Redis除了会根据值对象的类型判断键是否能够执行指定命令外,还会根据值对象的编码方式,选择正确的命令实现代码来执行命令。
比如:列表对象的编码可以是ziplist或者linkedlist,其中前者使用压缩列表API实现列表命令,而后者则使用双端链表API来实现列表命令。
假如,我们对一个键是哟个LLEN命令时,服务器除了需要确保执行命令的键是列表键,还需要根据键的编码选择正确的LLEN命令的实现:
- 如果是ziplist编码,程序将使用ziplistlen函数返回列表长度。
- 如果是linkedlist编码,程序将使用listlength函数返回双端链表的长度。
借用面向对象的思想,我们可以认为LLEN命令是多态的,只需要执行LLEN命令的是列表键,不论是什么编码,都会执行成功。
下图展示了LLEN命令从类型检查 到更具编码来选择实现函数的过程,其他类型特定命令的执行过程也类似:
实际上DEL, EXPIRE等命令也称之为多态命令,无论使用的是什么类型,这些命令都可以执行成功。
DEL, EXPIRE命令和LLEN命令的去表在于,前者是基于类型多态,后者是基于编码多态。