目录
一 简介
二 数据结构
Redis key 的数据类型都是字符串,value 的类型有以下五种:
序号 | 数据结构 |
---|---|
1 | String(字符串) |
2 | List(列表) |
3 | Set(集合) |
4 | Hash(哈希表) |
5 | ZSet(有序集合) |
2.1 String
- 【简介】
Redis String 存储字节序列,包括文本、序列化对象和二进制数组。String 是 Redis 最基本的数据类型。String 通常用于缓存,但除此之外,它也可以用于实现计数器和执行位运算。 - 【限制】
默认情况下,单个 Redis String 最大为 512MB。 - 【性能】
大部分 Redis String 操作的时间复杂度都是 O(1),SUBSTR、GETRANGE、SETRANGE 的时间复杂度可能是 O(n)。在处理大字符串时,这些随机访问字符串命令可能会导致性能问题。
2.2 List
-
【简介】
Redis list 是字符串值的链表。Redis list 经常用于:(1)实现栈和队列(2)为后台工作系统构建队列管理。Redis list 是通过 linked list 实现的。这意味着即使在一个列表中有数百万个元素,在列表的head 或 tail 添加一个新元素的操作也是在一个固定时间内执行的。使用 LPUSH 命令向包含10个元素的列表的头部添加新元素的速度与向包含1000万个元素的列表的头部添加元素的速度相同。
坏处是什么呢?通过 linked list 实现的列表查询速度不如像通过 array 实现的列表那样快。
-
【常见用例】
List 对于许多任务都很有用,下面是两个非常有代表性的用例:- 记住用户在社交网站上发布的最新更新。
- 进程间通信,使用生产者-消费者模式,其中生产者 push 进列表,消费者消费这些 item 并执行操作。Redis 有特殊的列表命令,可以使该用例更加可靠和高效。
-
【限制】
Redis 列表的最大长度为 2^32 - 1 (4,294,967,295) 个元素。 -
【性能】
访问其头部或尾部的列表操作的复杂度为 O(1),这意味着它们非常高效。然而,操作列表中元素的命令通常是 O(n)。这些示例包括LINDEX、、LINSERT和LSET。运行这些命令时请务必小心,尤其是在大型列表上操作时。 -
【数据结构】
List 的数据结构为 quickList。
在 List 元素较少的时候会使用 ziplist,在元素比较多的时候会使用 quikList。
Redis 将链表和 ziplist 结合起来组成了 quicklist,即将多个 ziplist 使用双向指针串起来使用,这样既满足了快速的插入和删除性能,又不会出现太大的空间冗余。
2.3 Set
- 【简介】
Redis Set 是无序的、去重的。我们可以使用 Redis Set:- Track 唯一的 item(例如:跟踪访问给定博客文章的所有的唯一 IP)。
- 表示关系(例如:具有给定角色的所有用户的集合)。
- 执行常见的集合运算(例如:交集、并集、差集)。
- 【限制】
Redis Set 的最大大小为 2^32 - 1 (4,294,967,295) 个成员。 - 【性能】
大多数集合操作(包括添加、删除以及检查某项是否是集合成员)的复杂度都是 O(1)。这意味着他们的效率很高。但是,对于具有数十万或更多成员的大型集,运行命令时应小心谨慎SMEMBERS。该命令的复杂度为 O(n),并在单个响应中返回整个集合。作为替代方案,请考虑SSCAN,它允许您迭代地检索集合的所有成员。 - 【数据结构】
Set 数据结构是字典,字典是用 hash 表实现的。
Java 中的 HashSet 的内部实现使用 HashMap,只不过所有的 value 都指向同一个对象。 Redis 的 set 结构也是一样的,它的内部也使用 hash 结构,所有的 value 都指向同一个内部值。
2.4 Hash
-
【简介】
Redis Hash 是一个键值对集合。可以使用 Hash 表示基本对象并存储计数器分组等。Redis Hash 是一个 String 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。 类似于 java 里面的 Map<String,Object>。
-
【性能】
大多数 Redis 哈希命令的复杂度都是 O(1)。
一些命令 - 例如HKEYS、HVALS和HGETALL- 的复杂度为 O(n),其中n是字段值对的数量。
- 【限制】
每个哈希最多可以存储 4,294,967,295 (2^32 - 1) 个字段值对。实际上,您的哈希值仅受托管 Redis 部署的虚拟机上的总内存的限制。 - 【数据结构】
Hash 类型对应的数据结构是 2 种:ziplist(压缩列表),hashtable(哈希表)。
当 field-value 长度较短个数较少时,使用 ziplist,否则使用 hashtable。
2.5 ZSet
-
【简介】
Redis 排序集是按关联分数排序的 Set 集合。当多个字符串具有相同分数时,字符串按字典序排序。排序集的一些用例包括:- 排行榜。例如,你可以使用 sorted set 来轻松地维护大型在线游戏中最高分的有序列表。
- 速率限制器。尤其是,您可以使用 sorted set 来构建滑动窗口速率限制器,以防止过多的 API 请求。
你可以将排序集看作是 Set 和 Hash 的混合。与集合类似,排序集合由唯一的、不重复的字符串元素组成,因此在某种意义上,排序集合也是一个集合。
然而,虽然集合内的元素没有排序,但排序集合中的每个元素都与一个称为分数的浮点值相关联 (这就是为什么该类型也类似于散列,因为每个元素都映射到一个值)。
此外,排序集中的元素是按顺序获取的(因此它们不是根据请求排序的,顺序是用于表示排序集的数据结构的特性)。它们根据以下规则排序:
- 如果 B 和 A 是具有不同分数的两个元素,则如果 A.score > B.score,则 A > B。
- 如果 B 和 A 的分数完全相同,则如果 A 字符串按字典顺序大于 B 字符串,则 A > B。B 和 A 字符串不能相等,因为排序集仅具有唯一元素。
-
【性能】
大多数排序集操作的复杂度为 O(log(n)),其中n是成员数。ZRANGE运行具有较大返回值(例如,数万或更多)的命令时请务必小心。该命令的时间复杂度为 O(log(n) + m),其中m是返回结果的数量。
-
【数据结构】
SortedSet(zset) 是 redis 提供的一个非常特别的数据结构,内部使用到了 2 种数据结构,hash 表与跳表。- hash 表。类似于 java 中的 Map<String,score>,key 为集合中的元素,value 为元素对应的 score,可以用来快速定位元素定义的 score,时间复杂度为 O(1)。
- 跳表(skiplist)。跳表是一个非常优秀的数据结构,实现简单,插入、删除、查找的复杂度均为 O(logN)。类似 java 中的 ConcurrentSkipListSet,根据 score 的值排序后生成的一个跳表,可以快速按照位置的顺序或者 score 的顺序查询元素。
2.6 BitMaps
BitMaps 不是实际的数据类型,而是在 String 类型上定义的一组面向位的操作,这些操作被视为位向量。由于字符串是二进制安全的 blobs,其最大长度为512MB,因此它们适合设置为2 ^ 32个不同的位。
您可以对一个或多个字符串执行按位操作:
- 对于集合成员对应于整数 0-N 的情况,有效的集合表示。
- 对象权限,其中每个位表示特定权限,类似于文件系统存储权限的方式。
位图的最大优点之一是,它们在存储信息时通常可以极大地节省空间。例如,在一个系统中,不同的用户由增量的用户 ID 表示,记住 40 亿用户的一个位信息(例如,知道一个用户是否想要接收新闻通讯)可能只需要 512MB 的内存。
2.7 概率数据结构
2.7.1 HyperLoglog
HyperLogLog 是一种概率数据结构,用于估计集合的基数。作为一种概率数据结构,HyperLogLog 以完美的准确性换取高效的空间利用。
HyperLoglog 的应用场景,例如:统计网站的 UV 等(这一天该页面的独立访问次数有多少?有多少独立用户播放过这首歌?有多少独立用户观看过该视频?)。求集合中不重复元素的个数的问题称为基数问题。
- 每个时间段的每页(视频/歌曲)都会创建一个 HyperLogLog,并且每次访问时都会将每个 IP/标识符添加到其中。
HyperLoglog 的优点:每个 HyperLoglog 键只需要 12KB 内存就可以计算最多 2^64 个不同元素的基数。
2.7.2 布隆过滤器
三 Redis 的发布与订阅(Pub/Sub)
发布与订阅是一种消息通信模式,发布者发布消息,订阅者接收消息。
四 Q&A
1. 为什么redis cluster slot数量是16384(2^14)?
https://github.com/redis/redis/issues/2576
The reason is:
Normal heartbeat packets carry the full configuration of a node, that can be replaced in an idempotent way with the old in order to update an old config. This means they contain the slots configuration for a node, in raw form, that uses 2k of space with16k slots, but would use a prohibitive 8k of space using 65k slots.
At the same time it is unlikely that Redis Cluster would scale to more than 1000 mater nodes because of other design tradeoffs.
So 16k was in the right range to ensure enough slots per master with a max of 1000 maters, but a small enough number to propagate the slot configuration as a raw bitmap easily. Note that in small clusters the bitmap would be hard to compress because when N is small the bitmap would have slots/N bits set that is a large percentage of bits set.