Redis 支持多种数据类型,每种类型都具有不同的特性和用途。以下是 Redis 中常见的数据类型:
字符串是最基本的数据类型,可以存储任意类型的数据,如文本、数字或序列化对象。可以使用字符串相关的命令对其进行操作,如设置值(SET)、获取值(GET)、增加数值(INCR)、追加字符串(APPEND)等。
在 Redis 中,字符串类型的底层数据结构和原理是简单动态字符串(SDS,Simple Dynamic String)。
简单动态字符串是 Redis 自己实现的一种字符串结构,相比于传统的 C 字符串(以空字符 ‘\0’ 结尾的字符数组),简单动态字符串具有以下优势:
O(1) 时间复杂度的修改操作:简单动态字符串通过维护字符串长度和可用空间的变量,可以在 O(1) 时间复杂度内进行字符串的修改操作,而不需要像传统 C 字符串那样进行内存拷贝和分配。
动态扩容和缩容:简单动态字符串在需要扩容时,可以按需动态地增加内存空间,而不是事先分配固定大小的缓冲区。这种动态扩容的机制使得字符串的长度可以根据需要进行自由调整,节省了内存空间。
二进制安全:简单动态字符串可以存储任意二进制数据,不仅仅限于文本字符串。这使得 Redis 可以在字符串类型中存储和处理二进制数据,而不会受到 C 字符串的限制。
内存预分配和惰性空间释放:简单动态字符串在进行扩容时,会预先分配一定的额外空间,以减少频繁的内存分配操作。同时,在字符串缩短时,不会立即释放多余的空间,而是保留一部分空间,以备后续使用,从而提高了性能。
总之,简单动态字符串是 Redis 中字符串类型的底层数据结构,通过动态扩容和缩容、O(1) 时间复杂度的修改操作等特性,提供了高效的字符串操作。这种数据结构使得 Redis 的字符串类型非常适用于存储和处理各种类型的数据,包括文本和二进制数据。
这个其实和Java中的ArrayList的实现比较像,ArrayList使用的是动态数组。如果读者比较感兴趣的话,后续我可以单独写一篇文章来着重讲述两者的区别。
哈希是一种键值对集合,类似于对象或字典。每个哈希可以存储多个字段和对应的值。可以使用哈希相关的命令对其进行操作,如设置**字段值(HSET)、获取字段值(HGET)、获取全部字段和值(HGETALL)**等。
在 Redis 中,Hash(哈希)的底层数据结构是哈希表(Hash Table)。
哈希表是一种基于键值对的数据结构,通过哈希函数将键映射到存储位置,实现了高效的键值查找和插入操作。在 Redis 的哈希中,哈希表被用来存储哈希的键值对。
Hash大家应该都比较熟悉了,这里简单讲一下Redis 中哈希底层数据结构和原理的一些关键点:
哈希表优势:使用哈希表作为哈希的底层数据结构有几个优势。首先,通过哈希表,可以以常数时间复杂度 O(1) 来执行插入、删除和查找操作。其次,哈希表可以高效地处理大量的键值对数据,具有良好的扩展性。
哈希表存储键值对:在 Redis 中,每对哈希的键值对被存储为哈希表的键值对。哈希表的键被称为字段(field),而哈希表的值被称为值(value)。通过哈希表的键,可以快速检索和操作哈希中的键值对。
哈希表的键冲突处理:由于哈希函数的输出空间有限,不同的键可能映射到相同的哈希表位置,这就是所谓的键冲突。为了解决键冲突,Redis 使用链地址法(Separate Chaining)来处理相同哈希值的键。具体来说,Redis 将具有相同哈希值的键值对放在同一个链表中,通过链表来存储冲突的键值对。
哈希表的扩容和缩容:为了保持哈希表的高效性能,Redis 在哈希表达到一定负载因子时会自动进行扩容操作。扩容操作涉及重新计算哈希函数和重新分配空间,以便保持较低的键冲突率。同样,当哈希表的使用率较低时,Redis 会自动进行缩容操作,以节省内存空间。
哈希的常用操作:Redis 提供了一系列命令来操作哈希,如添加字段和值(HSET)、获取值(HGET)、删除字段(HDEL)等。这些命令通过操作底层的哈希表来实现哈希的操作。
Redis 中的哈希数据类型通过使用哈希表作为底层数据结构,实现了高效的键值查找和插入操作。这种数据结构使得 Redis 的哈希非常适用于存储和操作具有键值对结构的数据,如用户信息、配置信息等。
列表是一个有序的字符串元素集合,可以在列表的两端进行插入或删除操作。列表可以用作队列或栈。您可以使用列表相关的命令对其进行操作,如插入元素到列表头(LPUSH)、弹出列表尾部元素(RPOP)、获取指定范围的元素(LRANGE)等。
在 Redis 中,List(列表)的底层数据结构是双向链表(doubly linked list)。(注:Java中的LinkedList底层也是双向链表,可以对比学习。)
双向链表是一种由多个节点组成的数据结构,每个节点包含一个值和两个指针,分别指向前一个节点和后一个节点。在 Redis 中,每个列表都由一个双向链表来实现,列表的每个元素都存储在一个节点中。
Redis 通过使用双向链表作为列表的底层数据结构,实现了对列表的高效操作。以下是一些关于 Redis 列表底层数据结构和原理的重要细节:
双向链表优势:使用双向链表作为列表的底层数据结构有几个优势。首先,它允许在列表的两端进行高效的插入和删除操作,时间复杂度为 O(1)。其次,双向链表可以按照顺序遍历和反向遍历列表,使得在有序列表中执行范围操作非常高效。
列表的头节点和尾节点:Redis 中的列表有一个特殊的头节点和尾节点,分别表示列表的开头和结尾。这些节点不包含实际的值,只是作为指示列表边界的标记。它们的存在使得在列表两端执行插入和删除操作更加高效。
列表的节点结构:每个列表节点包含三个主要部分。第一个部分是前置节点指针,指向前一个节点;第二个部分是后置节点指针,指向后一个节点;第三个部分是值,存储实际的列表元素。
列表的头指针和尾指针:Redis 中的列表有两个指针,分别指向列表的头节点和尾节点。这些指针使得在列表两端执行插入、删除和访问操作更加方便和高效。
列表的常用操作:Redis 提供了一系列命令来操作列表,如插入元素到列表头部(LPUSH)、插入元素到列表尾部(RPUSH)、从列表头部弹出元素(LPOP)、从列表尾部弹出元素(RPOP)等。这些命令通过操作双向链表的节点来实现列表的操作。
Redis 中的列表数据类型通过使用双向链表作为底层数据结构,实现了高效的插入、删除和访问操作。这种数据结构使得 Redis 列表非常适用于队列、栈、任务列表等应用场景,同时提供了丰富的命令和操作来满足不同的需求。
集合是一组唯一的、无序的字符串元素集合。集合支持添加、删除和判断元素是否存在的操作。您可以使用集合相关的命令对其进行操作,如添加元素到集合(SADD)、删除元素(SREM)、判断元素是否存在(SISMEMBER)等。
在 Redis 中,Set(集合)的底层数据结构是哈希表(Hash Table)。
哈希表是一种基于键值对的数据结构,它通过哈希函数将键映射到存储位置,从而实现高效的键值查找和插入操作。在 Redis 的集合中,哈希表被用来存储集合的元素。
以下是 Redis 中集合底层数据结构和原理的一些关键点:
哈希表优势:使用哈希表作为集合的底层数据结构有几个优势。首先,通过哈希表,可以以常数时间复杂度 O(1) 来执行插入、删除和查找操作。其次,哈希表可以高效地处理大量的键值对数据,具有良好的扩展性。
哈希表存储集合元素:在 Redis 中,每个集合元素被存储为哈希表的键,而哈希表的值则被设置为一个固定的空占位符。通过哈希表的键,可以快速检索和操作集合中的元素。
哈希表的键冲突处理:由于哈希函数的输出空间有限,不同的键可能映射到相同的哈希表位置,这就是所谓的键冲突。为了解决键冲突,Redis 使用链地址法(Separate Chaining)来处理相同哈希值的键。具体来说,Redis 将具有相同哈希值的键放在同一个链表中,通过链表来存储冲突的键值对。
哈希表的扩容和缩容:为了保持哈希表的高效性能,Redis 在哈希表达到一定负载因子时会自动进行扩容操作。扩容操作涉及重新计算哈希函数和重新分配空间,以便保持较低的键冲突率。同样,当哈希表的使用率较低时,Redis 会自动进行缩容操作,以节省内存空间。
集合的常用操作:Redis 提供了一系列命令来操作集合,如添加元素到集合(SADD)、从集合中移除元素(SREM)、判断元素是否存在于集合中(SISMEMBER)等。这些命令通过操作底层的哈希表来实现集合的操作。
Redis 中的集合数据类型通过使用哈希表作为底层数据结构,实现了高效的插入、删除和查找操作。这种数据结构使得 Redis 集合非常适用于存储和操作唯一值的场景,并提供了丰富的命令和操作来满足不同需求。
有序集合是一种在集合基础上增加了一个分数(score)的数据结构。每个元素都有一个唯一的值和一个对应的分数,通过分数可以对元素进行排序。您可以使用有序集合相关的命令对其进行操作,如添加元素到有序集合(ZADD)、根据分数范围获取元素(ZRANGEBYSCORE)、获取指定排名范围的元素(ZRANGE)等。
在 Redis 中,Sorted Set(有序集合)的底层数据结构是跳跃表(Skip List)和哈希表(Hash Table)的结合使用。
跳跃表是一种有序数据结构,类似于链表,但在每个节点中添加了多个指向其他节点的指针,从而实现高效的查找和范围操作。哈希表用于存储每个成员及其对应的分数。
以下是 Redis 中有序集合底层数据结构和原理的一些关键点:
跳跃表:有序集合使用跳跃表作为有序数据的主要存储结构。跳跃表中的每个节点都包含一个成员和一个分数,节点按照成员的字典序进行排序。通过多个层级的指针,跳跃表可以快速跳过一些节点,从而加快查找速度。
跳跃表的层级:跳跃表由多个层级组成,每个层级都是一个有序链表。最底层是包含所有成员的完整链表,而上层则是通过“跳跃”的方式连接较少的节点。这种层级结构使得在跳跃表中查找成员的时间复杂度为 O(log n)。
哈希表:除了跳跃表,Redis 还使用哈希表来存储每个成员及其对应的分数。哈希表通过将成员作为键、分数作为值来存储数据。这样,当需要根据成员进行查找或更新时,可以通过哈希表快速定位到对应的节点。
跳跃表和哈希表的结合使用:通过将跳跃表和哈希表结合使用,Redis 实现了有序集合数据类型的高效操作。跳跃表提供了快速的有序查找和范围操作,而哈希表则提供了快速定位成员的能力。
有序集合的常用操作:Redis 提供了一系列命令来操作有序集合,如添加成员和分数(ZADD)、根据分数范围获取成员(ZRANGEBYSCORE)、获取成员的排名(ZRANK)等。这些命令通过操作底层的跳跃表和哈希表来实现有序集合的操作。
Redis 中的有序集合数据类型通过使用跳跃表和哈希表的结合,实现了高效的有序集合操作。跳跃表提供了快速的查找和范围操作,而哈希表提供了快速定位成员的能力。这种数据结构使得 Redis 有序集合非常适用于实现排行榜、计数器和范围查询等应用场景。
位图是一种使用二进制位来存储和操作数据的数据结构。您可以使用位图相关的命令对其进行操作,如设置位(SETBIT)、获取位(GETBIT)、对多个位进行逻辑操作(BITOP)等。
除了以上常见的数据类型,Redis 还支持其他数据类型,如地理位置(Geospatial)、流(Stream)等。每种数据类型都有各自的用途和适用场景,您可以根据具体的需求选择合适的数据类型来存储和操作数据。
在 Redis 中,Bitmaps(位图)的底层数据结构是字符串(String)。
字符串是 Redis 中最基本的数据结构,可以存储二进制数据。在 Bitmaps 中,每个位(bit)都被存储为字符串中的一个字符,可以表示为 0 或 1。Redis 使用字符串来表示位图,并提供了一系列命令来操作位图数据。
以下是 Redis 中位图底层数据结构和原理的一些关键点:
字符串存储位:在 Redis 中,每个位都由一个字符来表示。位图使用 0 或 1 来表示位的状态,通常使用字符 “0” 和 “1” 分别表示位值为 0 或 1。
字符串的位操作:Redis 提供了一些位操作命令,如设置位(SETBIT)、获取位(GETBIT)、对多个位进行逻辑操作(BITOP)等。这些命令通过操作字符串的位来实现位图的操作。
字符串的二进制存储:Redis 使用字节序列来存储字符串,每个字节可以表示 8 个位。位图中的每个位会转换成对应字节中的某一位。
位图的大小:Redis 中的位图可以非常大,可以有 2^32 = 4294967296 个位,即可以表示一个长度为 4294967296 的位序列。
位图的应用:位图在 Redis 中广泛应用于各种场景,如统计在线用户、记录用户行为、判断某个元素是否存在等。由于位图的存储非常紧凑,可以有效地节省存储空间。
请注意,位图虽然非常高效,但它是以整个字符串作为单位进行操作的,如果只需要操作其中的某些位,可能会涉及到整个字符串的读取和写入,可能会对性能产生影响。因此,在使用位图时,需要根据实际需求和数据规模进行权衡和设计。
除了以上常见的数据类型,Redis 还支持其他数据类型,如地理位置(Geospatial)、流(Stream)等。每种数据类型都有各自的用途和适用场景,可以根据具体的需求选择合适的数据类型来存储和操作数据。