我试图在明确的清单中回答两个问题:
所以,我读过Redis列表实际上是用链表实现的。但对于其他类型,我无法挖掘任何信息。此外,如果有人偶然发现了这个问题而没有对修改或访问不同数据结构的利弊进行高级别总结,那么他们就会有一个完整的列表,以便最好地使用特定类型< / strong>也可以参考。
具体来说,我希望概述所有类型:字符串,列表,集合,zset和哈希。
哦,到目前为止,我已经看过这些文章,其中包括:
答案 0 :(得分:76)
大多数情况下,您不需要了解Redis使用的基础数据结构。但是一些知识可以帮助你进行CPU v / s内存折衷。它还可以帮助您以有效的方式建模数据。
在内部,Redis使用以下数据结构:
要查找特定键使用的编码,请使用命令object encoding <key>
。
在Redis中,字符串被称为Simple Dynamic Strings, or SDS。它是char *
上的一个小包装器,允许您将字符串的长度和空闲字节数存储为前缀。
由于存储了字符串的长度,strlen是O(1)操作。此外,因为长度是已知的,Redis字符串是二进制安全的。包含null character的字符串是完全合法的。
字符串是Redis中最通用的数据结构。 String是 all 以下内容:
long
。请参阅INCR,DECR,INCRBY和DECRBY命令。chars
,ints
,longs
或任何其他数据类型)。请参阅SETRANGE和GETRANGE命令。Redis使用Dictionary进行以下操作:
Redis词典使用Hash Tables实现。我将解释Redis的具体内容,而不是解释实现:
dictType
的结构来扩展哈希表的行为。这个结构有函数指针,因此以下操作是可扩展的:a)散列函数,b)密钥比较,c)密钥析构函数,和d)值析构函数。 Set
数据结构使用Dictionary来保证没有重复项。 Sorted Set
使用字典将元素映射到其分数,这就是ZSCORE是O(1)操作的原因。
使用Doubly Linked Lists实施list
数据类型。 Redis的&#39;实现是直接来自算法的教科书。唯一的变化是Redis将长度存储在列表数据结构中。这可确保LLEN具有O(1)复杂度。
Redis使用Skip Lists作为排序集的基础数据结构。维基百科有一个很好的介绍。 William Pugh的论文Skip Lists: A Probabilistic Alternative to Balanced Trees有更多细节。
排序集使用“跳过列表”和“词典”。字典存储每个元素的分数。
的Redis&#39;跳过列表实现与以下方式的标准实现不同:
Zip列表就像一个双向链表,除了它不使用指针并将数据内联存储。
双向链表中的每个节点都有3个指针 - 一个前向指针,一个后向指针和一个引用存储在该节点的数据的指针。指针需要内存(64位系统上为8个字节),因此对于小型列表,双向链表效率非常低。
Zip List按顺序在Redis String中存储元素。每个元素都有一个小标题,用于存储元素的长度和数据类型,到下一个元素的偏移量以及到前一个元素的偏移量。这些偏移取代了前向和后向指针。由于数据是内联存储的,因此我们不需要数据指针。
Zip列表用于存储小列表,有序集和散列。排序集被展平为[element1, score1, element2, score2, element3, score3]
之类的列表并存储在Zip列表中。哈希被展平为[key1, value1, key2, value2]
等列表。
使用Zip Lists,您可以在CPU和内存之间进行权衡。 Zip列表具有内存效率,但它们使用的CPU比链表(或哈希表/跳过列表)多。在zip列表中查找元素是O(n)。插入新元素需要重新分配内存。因此,Redis仅将此编码用于小型列表,哈希和有序集。您可以通过在redis.conf中更改<datatype>-max-ziplist-entries
和<datatype>-max-ziplist-value>
的值来调整此行为。有关详细信息,请参阅Redis Memory Optimization, section "Special encoding of small aggregate data types"。
comments on ziplist.c非常出色,您无需阅读代码即可完全理解此数据结构。
Int Sets是&#34; Sorted Integer Arrays&#34;的一个奇特名称。
在Redis中,集合通常使用哈希表来实现。对于小集合,散列表是低效的内存。当集合仅由整数组成时,数组通常更有效。
Int Set是一个整数的排序数组。要查找元素,请使用binary search algorithm。这具有O(log N)的复杂性。向此数组添加新整数可能需要重新分配内存,这对于大型整数数组而言可能会变得昂贵。
作为进一步的内存优化,Int Sets有3种不同整数的变体:16位,32位和64位。 Redis足够聪明,可以根据元素的大小使用正确的变体。添加新元素并且它超过当前大小时,Redis会自动将其迁移到下一个大小。如果添加了字符串,Redis会自动将Int Set转换为基于常规哈希表的集合。
Int Sets是CPU和内存之间的权衡。 Int集合具有极高的内存效率,对于小集合,它们比散列表更快。但是经过一定数量的元素后,O(log N)检索时间和重新分配内存的成本变得过高。根据实验,切换到常规哈希表的最佳阈值为512.但是,您可以根据应用程序的需要增加此阈值(降低它没有意义)。请参阅redis.conf中的set-max-intset-entries
。
Zip地图是平面化的词典并存储在列表中。它们与Zip Lists非常相似。
自从Redis 2.6以来,Zip地图已被弃用,小的哈希值存储在Zip列表中。要了解有关此编码的详细信息,请参阅comments in zipmap.c。
答案 1 :(得分:2)
Redis存储指向值的键。密钥可以是任何二进制值,直到合理的大小(出于可读性和调试目的,建议使用短ASCII字符串)。值是五种本机Redis数据类型之一。
1.strings - 一系列二进制安全字节,最大512 MB
2.hashes - 键值对的集合
3.lists - 字符串的插入顺序集合
4.sets - 没有排序的唯一字符串的集合
5.sorted sets - 由用户定义的评分排序的唯一字符串的集合
<强>字符串强>
Redis字符串是一个字节序列。
Redis中的字符串是二进制安全的(意味着它们的已知长度不是由任何特殊的终止字符决定的),因此您可以在一个字符串中存储最多512 MB的内容。
字符串是规范的键值存储&#34;概念。您有一个指向值的键,其中键和值都是文本或二进制字符串。
对于字符串的所有可能操作,请参阅 http://redis.io/commands/#string
<强>散列强>
Redis哈希是键值对的集合。
Redis哈希包含许多键值对,其中每个键和值都是一个字符串。 Redis哈希不直接支持复杂值(意思是,您不能使哈希字段具有列表或集合或其他哈希值),但您可以使用哈希字段指向其他顶级复杂值。您可以对哈希字段值执行的唯一特殊操作是数字内容的原子递增/递减。
您可以通过两种方式考虑Redis哈希:作为直接对象表示以及紧凑地存储许多小值的方法。
直接对象表示很容易理解。对象具有名称(哈希的键)和具有值的内部键的集合。请参阅下面的示例,以及示例。
使用哈希存储许多小值是一种聪明的Redis海量数据存储技术。当散列具有少量字段(~100)时,Redis优化整个散列的存储和访问效率。 Redis的小型哈希存储优化提出了一个有趣的行为:拥有100个哈希,每个哈希具有100个内部密钥和值,而不是让10,000个顶级密钥指向字符串值,效率更高。使用Redis哈希来优化数据存储这种方式需要额外的编程开销来跟踪数据最终的位置,但如果您的数据存储基本上是基于字符串的,那么使用这一个奇怪的技巧可以节省大量的内存开销。
有关哈希的所有可能操作,请参阅hash docs
<强>解释强>
Redis列表就像链接列表一样。
您可以从列表的头部或尾部插入,删除和遍历列表。
当您需要按插入顺序维护值时使用列表。 (如果需要,Redis会为您提供插入任意列表位置的选项,但如果插入远离起始位置,插入性能会降低。)
Redis列表通常用作生产者/消费者队列。将项目插入列表,然后从列表中弹出项目。如果您的消费者尝试从没有元素的列表中弹出会发生什么?您可以要求Redis等待元素出现,并在添加元素后立即将其返回给您。这会将Redis转变为实时消息队列/事件/作业/任务/通知系统。
您可以自动删除列表任一端的元素,将任何列表视为堆栈或队列。
您还可以通过在每次插入后将列表修剪为特定大小来维护固定长度列表(上限集合)。
对于列表上的所有可能操作,请参阅lists docs
<强>集强>
Redis集合就是设置。
Redis集包含唯一的无序Redis字符串,其中每个字符串每集仅存在一次。如果将相同的元素添加十次到一个集合,它将只显示一次。套装非常适合懒洋洋地确保某些东西存在至少一次,而不必担心重复元素的累积和浪费空间。您可以根据需要多次添加相同的字符串,而无需检查它是否已存在。
对于集合中成员的成员资格检查,插入和删除,集合很快。
设置具有高效的设置操作,正如您所期望的那样。您可以同时获取多个集合的并集,交集和差异。结果可以返回给调用者,也可以将结果存储在新的集合中供以后使用。
集合具有常量时间访问以进行成员资格检查(与列表不同),并且Redis甚至可以方便地随机删除成员并返回(&#34;弹出集合中的随机元素&#34;)或随机成员返回而无需替换(& #34;给我30个随机的独特用户&#34;)或替换(&#34;给我7张卡片,但在每次选择后,将卡片放回去,以便可能再次采样&#34;)。 / p>
对于集合上的所有可能操作,请参阅sets docs。
排序集
Redis排序集是具有用户定义排序的集合。
为简单起见,您可以将有序集视为具有唯一元素的二叉树。 (Redis有序集实际上是skip lists。)元素的排序顺序由每个元素的得分定义。
排序集仍然是设置。元素只能在一组中出现一次。出于唯一性目的,元素由其字符串内容定义。插入元素&#34; apple&#34;排序得分为3,然后插入元素&#34; apple&#34;排序得分500导致一个元素&#34; apple&#34;在排序集中排序得分为500。集合仅基于数据是唯一的,而不是基于(分数,数据)对。
确保您的数据模型依赖于字符串内容,而不是元素的唯一性得分。允许分数重复(甚至为零),但最后一次,每个有序集合只能存在一次。例如,如果您尝试将每个用户登录的历史记录存储为有序集,方法是将分数设置为登录的时期和用户ID的值,则最终将仅为所有用户存储上次登录时期。您的设置将增加到用户群的大小,而不是您想要的用户群*登录大小。
使用分数将元素添加到您的集合中。您可以随时更新任何元素的分数,只需使用新分数再次添加元素即可。分数由浮点双精度表示,因此您可以根据需要指定高精度时间戳的粒度。多个元素可能具有相同的分数。
您可以通过几种不同的方式检索元素。由于所有内容都已排序,因此您可以要求从最低分数开始的元素。您可以要求从最高分开始的元素(&#34;反向&#34;)。您可以按自然顺序或反向顺序询问元素。
有关已排序集的所有可能操作,请参阅sorted sets docs.