大多数时候,你不需要理解Redis使用的底层数据结构。但是一些知识可以帮助你做出CPU v/s内存的权衡。它还可以帮助您以有效的方式对数据建模。
在内部,Redis使用以下数据结构:
字符串
字典
双链表
跳跃表
邮政编码列表
Int集
Zip地图(自Redis 2.6起已弃用,支持Zip列表)
要查找特定键使用的编码,请使用命令object encoding <key>。
1. 字符串
在Redis中,字符串被称为简单动态字符串,或SDS。它是一个较小的char *包装器,允许您将字符串的长度和空闲字节数作为前缀存储。
因为字符串的长度是存储的,所以strlen是一个O(1)操作。此外,因为长度是已知的,Redis字符串是二进制安全的。字符串中包含空字符是完全合法的。
字符串是Redis中最通用的数据结构。String是以下所有类型:
A string of characters that can store text. See SET and GET commands.
A byte array that can store binary data.
A long that can store numbers. See INCR, DECR, INCRBY and DECRBY commands.
An Array (of chars, ints, longs or any other data type) that can allow efficient random access. See SETRANGE and GETRANGE commands.
A bit array that allows you to set or get individual bits. See SETBIT and GETBIT commands.
A block of memory that you can use to build other data structures. This is used internally to build ziplists and intsets, which are compact, memory-efficient data structures for small number of elements. More on this below.
2. 字典
Redis使用字典来实现以下功能:
将键映射到其关联值,其中值可以是字符串、散列、集合、排序集合或列表。
将密钥映射到其到期时间戳。
实现Hash、Set和Sorted Set数据类型。
将Redis命令映射到处理这些命令的函数。
将Redis键映射到被该键阻塞的客户端列表。看到BLPOP。
Redis字典是使用哈希表实现的。我将不解释实现,只解释Redis的具体内容:
Dictionaries use a structure called dictType to extend the behaviour of a hash table. This structure has function pointers, and so the following operations are extendable: a) hash function, b) key comparison, c) key destructor, and d) value destructor.
Dictionaries use the murmurhash2. (Previously they used the djb2 hash function, with seed=5381, but then the hash function was switched to murmur2. See this question for an explanation of the djb2 hash algorithm.)
Redis uses Incremental Hashing, also known as Incremental Resizing. The dictionary has two hash tables. Every time the dictionary is touched, one bucket is migrated from the first (smaller) hash table to the second. This way, Redis prevents an expensive resize operation.
Set数据结构使用Dictionary来保证没有重复。排序集使用字典将一个元素映射到它的分数,这就是为什么ZSCORE是一个O(1)操作。
3.双链表
列表数据类型是使用双链表实现的。Redis的实现直接来自于算法教科书。唯一的变化是Redis将长度存储在列表数据结构中。这确保了LLEN具有O(1)复杂度。
4. 跳跃表
Redis使用跳过列表作为排序集的底层数据结构。维基百科有一个很好的介绍。William Pugh的论文跳跃列表:平衡树的概率替代方案有更多细节。
排序集同时使用跳过表和字典。字典存储每个元素的分数。
Redis的跳跃列表实现在以下方面与标准实现不同:
Redis允许重复得分。如果两个节点有相同的分数,它们将按字典顺序排序。
每个节点在0级有一个后向指针。这允许您以分数的相反顺序遍历元素。
5. 邮政编码列表
Zip List类似于双链表,只是它不使用指针并内联存储数据。
双链表中的每个节点都有3个指针——一个前向指针、一个后向指针和一个用于引用存储在该节点上的数据的指针。指针需要内存(64位系统需要8个字节),因此对于小列表,双链表的效率非常低。
Zip列表在Redis字符串中按顺序存储元素。每个元素都有一个小的头部,存储元素的长度和数据类型,下一个元素的偏移量和上一个元素的偏移量。这些偏移量替换向前指针和向后指针。由于数据是内联存储的,因此不需要数据指针。
Zip列表用于存储小列表、排序集和散列。排序集被平铺成一个列表,如[element1, score1, element2, score2, element3, score3],并存储在Zip list中。哈希被平铺成一个列表,如[key1, value1, key2, value2]等。
With Zip Lists you have the power to make a tradeoff between CPU and Memory. Zip Lists are memory-efficient, but they use more CPU than a linked list (or Hash table/Skip List). Finding an element in the zip list is O(n). Inserting a new element requires reallocating memory. Because of this, Redis uses this encoding only for small lists, hashes and sorted sets. You can tweak this behaviour by altering the values of <datatype>-max-ziplist-entries and <datatype>-max-ziplist-value> in redis.conf. See Redis Memory Optimization, section "Special encoding of small aggregate data types" for more information.
c上的注释非常棒,无需阅读代码就可以完全理解这个数据结构。
6. Int集
整型集是“排序整型数组”的花哨名称。
在Redis中,集合通常使用哈希表实现。对于小的集合,哈希表在内存方面是低效的。当集合仅由整数组成时,数组通常更有效。
Int Set是一个已排序的整数数组。为了找到一个元素,使用了二分搜索算法。这有一个O(log N)的复杂度。向这个数组中添加新的整数可能需要重新分配内存,这对于大的整数数组来说是非常昂贵的。
作为进一步的内存优化,Int集有3种不同整数大小的变体:16位、32位和64位。Redis非常聪明,可以根据元素的大小使用正确的变体。当添加一个新元素并且超出当前大小时,Redis会自动将其迁移到下一个大小。如果添加了一个字符串,Redis会自动将Int集转换为基于常规哈希表的集合。
Int Sets are a tradeoff between CPU and Memory. Int Sets are extremely memory efficient, and for small sets they are faster than a hash table. But after a certain number of elements, the O(log N) retrieval time and the cost of reallocating memory become too much. Based on experiments, the optimal threshold to switch over to a regular hash table was found to be 512. However, you can increase this threshold (decreasing it doesn't make sense) based on your application's needs. See set-max-intset-entries in redis.conf.
7. Zip地图
Zip map是扁平的字典,存储在列表中。它们非常类似于Zip列表。
自Redis 2.6以来,Zip map已弃用,小散列存储在Zip列表中。要了解关于这种编码的更多信息,请参阅zipmap.c中的注释。