当前位置 博文首页 > 文章内容

    Redis阅读笔记-类型检查与命令多态

    作者: 栏目:未分类 时间:2020-10-14 15:01:11

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    Redis阅读笔记-类型检查与命令多态

    ​ Redis用于操作键的命令基本可以分为两种类型。

    ​ 其中一种命令可以对任何类型的键执行,如del、expire、rename、type、object命令等。

    ​ 举个例子,以下代码就展示了del命令来删除三种不同类型的键:

    # 字符串键
    127.0.0.1:6379> set msg 'hell'
    OK
    
    # 列表键
    127.0.0.1:6379> rpush numbers 1 2 3
    (integer) 3
    
    # 集合键
    127.0.0.1:6379> sadd fruits apple banana cherry
    (integer) 3
    127.0.0.1:6379> del msg
    (integer) 1
    127.0.0.1:6379> del numbers
    (integer) 1
    127.0.0.1:6379> del fruits
    (integer) 1
    

    ​ 另一种命令只能对特定类型执行的键,比如说:

    • set、get、append、strlen等命令只能对字符串键执行;

    • hdel、hset、hget、hlen等命令只能对哈希键执行;

    • rpush、lpop、linsert、llen等命令只能对列表执行;

    • sadd、spop、sinsert、scard等命令只能对集合键执行;

    • zadd、zcard、zrank、zscore等命令只能对有序集合键执行;

      举个例子, 我们可以用set命令创建一个字符串, 然后用get命令和append命令操作这个键, 但是如果我们试图对这个字符串执行只有列表键才能执行的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> append msg " again!"
      (integer) 18
      127.0.0.1:6379> get msg
      "hello world again!"
      127.0.0.1:6379> llen msg
      (error) WRONGTYPE Operation against a key holding the wrong kind of value 
      

    类型检测的实现

    ​ 从上面发生的类型错误的代码示例可以看出, 为了确保只有指定类型的键可以执行某些特定的命令,在执行一个特定类型的命令之前,Redis会先检查输入键的类型释放正确, 然后再决定是否执行给定的命令。

    ​ 类型特定命令所进行的类型检查是通过redisObject结构的type属性来实现的:

    • 在执行一个类型特定命令之前, 服务器会先检查输入数据库键值对象是否为执行命令所有的类型,如果是的话, 服务器将会对键执行指定的命令;

    • 否则,服务将拒绝执行命令, 并向客户端返回一个类型错误。

      ​ 举个例子, 对于llen命令来说:

    • 在执行llen命令之前, 服务器会先检查输入数据库键的值对象是否为列表类型, 也即是, 检查值对象redisObject结构type属性的值是否为REDIS_LIST,如果是的话, 服务器就对键执行llen命令;

    • 否则的话, 服务器就拒绝执行命令并向客户端返回一个类型错误, 下图展示了这一类型检查的过程。

    graph TD A[客户端发送LLEN命令] B{服务器检查键key的值对象是否列表对象} C[对键key执行LLEN命令] D[返回一个类型错误] A-->B B--是-->C B--否-->D

    ​ 其他类型特定命令的类型检查过程也和这里展示的LLEN命令的类型检查过程类似。

    多态命令的实现

    ​ Redis除了会根据值对象的类型来判断键是否能执行特定命令之外, 还会根据值对象的编码方式, 选择正确的命令实现代码来执行命令。

    ​ 举个例子, 在前面介绍列表对象的编码时我们说过,列表对象有ziplist和linkedlist两种编码可用, 其中前者使用压缩列表API来实现列表命令,而后者则使用双端链表API来实现列表命令。

    ​ 现在,考虑这样一个情况, 如果我们对一个键执行LLEN命令,那么服务器除了要确保执行命令的是列表键之外,还需要根据键的值对象所使用的编码来选择正确的llen命令实现:

    • 如果列表对象的编码为ziplist, 那么说明列表对象的实现为压缩列表, 程序将使用ziplistLen函数来返回列表的长度;

    • 如果列表对象的编码为linkedlist,那么说明列表对象的实现为双端链表, 程序将使用listLength函数来返回双端链表的长度;

      ​ 借用面向对象方面的术语来说, 我们认为LLEN命令是多态(polymorphism)的,只要执行LLEN命令的是列表键, 那么无论值对象使用的是ziplist编码还是linedlist编码, 命令都可以正常执行。

      ​ 下图展示LLEN命令从类型检查到根据编码选择实现函数的整个执行过程, 其他类型特定命令的执行过程也是类似的。

      graph TD A[客户端发送LLEN命令] B{服务器检查键key的值对象是否列表对象?} C{对象的编码是ziplist还是linkedlist?} E[调用ziplisten函数返回压缩列表的长度] F[调用listLength函数返回双端链表的长度] D[返回一个类型错误] A-->B B--是-->C B--否-->D C--ziplist编码-->E C--linkedlist编码-->F

    ​ 实际上, 我们已将del、expire、type命令也称为堕胎命令,因为无论输入的键是什么类型, 这些命令都可以正确执行。

    ​ del、expire等命令和llen等命令的区别在于, 前者是基于类型的多态-----一个命令可以同时用于处理多个不同类型的键,而后者是基于编码的多态-----一个命令可以用于处理多种不同的编码。