redis数据结构重点总结

  1. 简单动态字符串
  • Redis 只会使用 C 字符串作为字面量, 在大多数情况下, Redis 使用 SDS (Simple Dynamic String,简单动态字符串)作为字符串表示。
  • 比起 C 字符串, SDS 具有以下优点:
    1. 常数复杂度获取字符串长度。
    2. 杜绝缓冲区溢出。
    3. 减少修改字符串长度时所需的内存重分配次数。
    4. 二进制安全。
    5. 兼容部分 C 字符串函数。 

 2链表 

  • 链表被广泛用于实现 Redis 的各种功能, 比如列表键, 发布与订阅, 慢查询, 监视器, 等等。
  • 每个链表节点由一个 listNode 结构来表示, 每个节点都有一个指向前置节点和后置节点的指针, 所以 Redis 的链表实现是双端链表。
  • 每个链表使用一个 list 结构来表示, 这个结构带有表头节点指针、表尾节点指针、以及链表长度等信息。
  • 因为链表表头节点的前置节点和表尾节点的后置节点都指向 NULL , 所以 Redis 的链表实现是无环链表。
  • 通过为链表设置不同的类型特定函数, Redis 的链表可以用于保存各种不同类型的值。

3.字典

  • 字典被广泛用于实现 Redis 的各种功能, 其中包括数据库和哈希键。
  • Redis 中的字典使用哈希表作为底层实现, 每个字典带有两个哈希表, 一个用于平时使用, 另一个仅在进行 rehash 时使用。
  • 当字典被用作数据库的底层实现, 或者哈希键的底层实现时, Redis 使用 MurmurHash2 算法来计算键的哈希值。
  • 哈希表使用链地址法来解决键冲突, 被分配到同一个索引上的多个键值对会连接成一个单向链表。
  • 在对哈希表进行扩展或者收缩操作时, 程序需要将现有哈希表包含的所有键值对 rehash 到新哈希表里面, 并且这个 rehash 过程并不是一次性地完成的, 而是渐进式地完成的。

为什么有序集合需要同时使用跳跃表和字典来实现?

在理论上来说, 有序集合可以单独使用字典或者跳跃表的其中一种数据结构来实现, 但无论单独使用字典还是跳跃表, 在性能上对比起同时使用字典和跳跃表都会有所降低。

举个例子, 如果我们只使用字典来实现有序集合, 那么虽然以 O(1) 复杂度查找成员的分值这一特性会被保留, 但是, 因为字典以无序的方式来保存集合元素, 所以每次在执行范围型操作 —— 比如 ZRANK 、 ZRANGE 等命令时, 程序都需要对字典保存的所有元素进行排序, 完成这种排序需要至少 O(N \log N) 时间复杂度, 以及额外的 O(N) 内存空间 (因为要创建一个数组来保存排序后的元素)。

另一方面, 如果我们只使用跳跃表来实现有序集合, 那么跳跃表执行范围型操作的所有优点都会被保留, 但因为没有了字典, 所以根据成员查找分值这一操作的复杂度将从 O(1) 上升为 O(\log N) 。

因为以上原因, 为了让有序集合的查找和范围型操作都尽可能快地执行, Redis 选择了同时使用字典和跳跃表两种数据结构来实现有序集合。

举个例子, 如果前面 price 键创建的不是 ziplist 编码的有序集合对象, 而是 skiplist 编码的有序集合对象, 那么这个有序集合对象将会是图 8-16 所示的样子, 而对象所使用的 zset 结构将会是图 8-17 所示的样子。

digraph {      label = "\n 图 8-16    skiplist 编码的有序集合对象";      rankdir = LR;      node [shape = record];      redisObject [label = " redisObject | type \n REDIS_ZSET | encoding \n REDIS_ENCODING_SKIPLIST | <ptr> ptr | ... "];      zset [label = " <head> zset | <dict> dict | <zsl> zsl "];      node [shape = plaintext];      dict [label = "..."];      zsl [label = "..."];      redisObject:ptr -> zset:head;     zset:dict -> dict;     zset:zsl -> zsl;  }

digraph {      rankdir = LR;      //      node [shape = record];      zset [label = " <head> zset | <dict> dict | <zsl> zsl "];      dict [label = " <head> dict | ... | <ht0> ht[0] | ... "];      ht0 [label = " <head> dictht | ... | <table> table | ... "];      table [label = " <banana> StringObject \n \"banana\" | <apple> StringObject \n \"apple\" | <cherry> StringObject \n \"cherry\" "];      node [shape = plaintext];      apple_price [label = "8.5"];     banana_price [label = "5.0"];     cherry_price [label = "6.0"];      //      zset:dict -> dict:head;     dict:ht0 -> ht0:head;     ht0:table -> table:head;      table:apple -> apple_price;     table:banana -> banana_price;     table:cherry -> cherry_price;      //      node [shape = record, width = "0.5"];      //      l [label = " <header> header | <tail> tail | level \n 5 | length \n 3 "];      subgraph cluster_nodes {          style = invisible;          header [label = " <l32> L32 | ... | <l5> L5 | <l4> L4 | <l3> L3 | <l2> L2 | <l1> L1 "];          bw_null [label = "NULL", shape = plaintext];          level_null [label = "NULL", shape = plaintext];          A [label = " <l4> L4 | <l3> L3 | <l2> L2 | <l1> L1 | <backward> BW | 5.0 | StringObject \n \"banana\" "];          B [label = " <l2> L2 | <l1> L1 | <backward> BW | 6.0 | StringObject \n \"cherry\" "];          C [label = " <l5> L5 | <l4> L4 | <l3> L3 | <l2> L2 | <l1> L1 | <backward> BW | 8.5 | StringObject \n \"apple\" "];      }      subgraph cluster_nulls {          style = invisible;          n1 [label = "NULL", shape = plaintext];         n2 [label = "NULL", shape = plaintext];         n3 [label = "NULL", shape = plaintext];         n4 [label = "NULL", shape = plaintext];         n5 [label = "NULL", shape = plaintext];      }      //      l:header -> header;     l:tail -> C;      header:l32 -> level_null;     header:l5 -> C:l5;     header:l4 -> A:l4;     header:l3 -> A:l3;     header:l2 -> A:l2;     header:l1 -> A:l1;      A:l4 -> C:l4;     A:l3 -> C:l3;     A:l2 -> B:l2;     A:l1 -> B:l1;      B:l2 -> C:l2;     B:l1 -> C:l1;      C:l5 -> n5;     C:l4 -> n4;     C:l3 -> n3;     C:l2 -> n2;     C:l1 -> n1;      bw_null -> A:backward -> B:backward -> C:backward [dir = back];      zset:zsl -> l:header;      // HACK: 放在开头的话 NULL 指针的长度会有异样     label = "\n 图 8-17    有序集合元素同时被保存在字典和跳跃表中";  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值