既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
+ **head**:指向 Quicklist 头部节点的指针。
+ **tail**:指向 Quicklist 尾部节点的指针。
+ **count**:表示 Quicklist 中总元素的数量。
+ **len**:表示 Quicklist 中节点的数量。
+ **fill**:表示每个压缩列表节点的最大元素数量,由配置参数 `list-max-ziplist-size` 控制。
总之,Redis 的 Quicklist 是一种高效的列表实现,它综合了压缩列表和双向链表的优点,通过多个压缩列表节点和双向链表结构实现了内存紧凑且访问高效的列表操作。在处理各种规模的列表数据时,Quicklist 都能提供较好的性能和可扩展性。
应用场景
Redis List 是一种有序的集合数据类型,提供了多种元素插入、删除、获取和遍历的操作。由于其高效且灵活的特性,Redis List 可以应用于多种场景,以下是一些常见的应用场景:
- 消息队列:Redis List 可用作简单的消息队列,支持生产者-消费者模型。生产者可以使用 LPUSH 或 RPUSH 将消息添加到队列的头部或尾部,消费者可以使用 LPOP 或 RPOP 从队列的头部或尾部获取并删除消息。如果需要阻塞等待消息,可以使用 BLPOP 或 BRPOP 命令。Redis提供了 BRPOP 命令。BRPOP命令也称为阻塞式读取,客户端在没有读到队列数据时,自动阻塞,直到有新的数据写入队列,再开始读取新数据
- 任务堆栈:Redis List 可用作任务堆栈,实现后进先出(LIFO)的任务处理模式。可以使用 LPUSH 将任务压入堆栈,使用 LPOP 从堆栈顶部弹出任务进行处理。
- 任务队列:Redis List 可用作任务队列,实现先进先出(FIFO)的任务处理模式。可以使用 RPUSH 将任务添加到队列尾部,使用 LPOP 从队列头部获取任务进行处理。
- 时间线和动态消息:Redis List 可用于存储时间线和动态消息,如用户的微博、动态等。可以使用 LPUSH 将新消息添加到列表头部,保证列表中的消息按时间顺序排列。使用 LRANGE 命令可以快速获取最新的消息或指定时间段内的消息。
- 数据缓存:Redis List 可用作数据缓存,存储最近访问的数据。当需要缓存新数据时,可以使用 LPUSH 将数据添加到列表头部,如果列表长度超过缓存容量,可以使用 RPOP 删除最早访问的数据。这样可以实现简单的 LRU(Least Recently Used)缓存策略。
- 排行榜和计分板:Redis List 可用于实现排行榜和计分板,按照分数或其他指标对元素进行排序。可以使用 LPUSH 或 RPUSH 添加元素,使用 SORT 命令对列表元素进行排序,使用 LRANGE 获取指定范围内的排名。
这些只是 Redis List 的一部分应用场景,由于其高性能和灵活性,Redis List 可以应用于许多其他场景。实际使用中,可以根据需求选择合适的 List 命令来实现所需的功能。
Hash
Hash 是一个键值对(key - value)集合,其中 value 的形式如: value=[{field1,value1},...{fieldN,valueN}]
。Hash 特别适合用于存储对象。
底层实现
Hash 类型的底层数据结构是由压缩列表或哈希表实现的:
- 如果哈希类型元素个数小于
512
个(默认值,可由hash-max-ziplist-entries
配置),所有值小于64
字节(默认值,可由hash-max-ziplist-value
配置)的话,Redis 会使用压缩列表作为 Hash 类型的底层数据结构; - 如果哈希类型元素不满足上面条件,Redis 会使用哈希表作为 Hash 类型的 底层数据结构。
在 Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。
压缩列表底层实现
哈希表底层实现
哈希表是一种高效的动态数组数据结构,它通过哈希函数将键值对映射到一个数组中的索引位置。哈希表的特点是插入、删除和查找操作的时间复杂度都为 O(1),具有良好的性能和可扩展性。
哈希表的底层实现采用开放寻址法(open addressing)和渐进式哈希(incremental hashing)策略。当哈希表的负载因子(即已存储的键值对数量与数组容量的比值)超过一定阈值时,Redis 会自动扩容并重新哈希所有的键值对。这样可以确保哈希表在处理不同大小的数据时都能保持高效的性能。
底层实现关键点:
- 哈希函数:Redis 使用 MurmurHash2 算法作为哈希函数,将键值对的 field 映射到哈希表中的索引位置。MurmurHash2 算法非常适合在哈希表中使用,因为它可以快速计算哈希值且具有较低的冲突率。
- 开放寻址法:Redis 哈希表采用开放寻址法(open addressing)进行冲突解决。当两个或多个键值对的哈希值发生冲突时,它们会被放置在哈希表中的其他位置。Redis 使用线性探测法(linear probing)进行开放寻址,即在发生冲突时,沿着数组顺序查找下一个空闲位置来存放新的键值对。
- 渐进式哈希:为了保持哈希表的高效性能,Redis 使用渐进式哈希(incremental hashing)策略进行扩容和收缩。当哈希表的负载因子(即已存储的键值对数量与数组容量的比值)超过一定阈值(通常为 1.0)时,Redis 会自动扩容并重新哈希所有的键值对。同样,当负载因子低于一定阈值时,Redis 会自动收缩哈希表。渐进式哈希意味着,在扩容或收缩过程中,Redis 会逐步将键值对重新哈希到新的哈希表中,而不是一次性完成。这样可以减少单次操作的计算量和延迟,保持 Redis 的高性能和低延迟特性。
- 扩容和收缩策略:Redis 哈希表采用 2 倍扩容策略。当哈希表的负载因子超过阈值时,Redis 会将哈希表的容量扩大为当前容量的 2 倍。当负载因子低于阈值时,Redis 会将哈希表的容量减小为当前容量的一半。这种扩容和收缩策略可以保证哈希表在不同大小的数据集下都能保持高效的性能。
listpack底层实现
在 Redis 6.2 版本以后,Hash 类型数据结构的底层实现从 Ziplist 被替换成了 Listpack。Listpack 是一种紧凑的连续内存数据结构,它将多个键值对存储在一个连续的内存区域中。这样可以减少内存碎片并降低内存占用。Listpack 适用于存储较小的哈希表,具有紧凑的内存占用和高效的访问性能。
以下是 Redis Hash 类型数据结构中 Listpack 底层实现的关键点:
- 变长编码:Listpack 使用变长编码(variable length encoding)来存储每个键值对的长度。这意味着每个键值对的长度信息占用的字节数量取决于其实际长度。较短的键值对占用较少的字节,从而节省内存空间。
- 紧凑内存分布:Listpack 将键值对紧凑地存储在连续的内存区域中,以减少内存碎片和降低内存占用。每个键值对在 Listpack 中以连续的字节序列形式存储,包括长度信息、实际数据以及一些特殊标记。
- 顺序访问:Listpack 的访问模式是顺序访问。在查找、插入和删除操作中,Listpack 会按照存储顺序遍历所有的键值对,直到找到目标键值对。对于较小的哈希表,这种顺序访问模式具有较高的性能。但是,在处理较大的哈希表时,顺序访问的效率会降低。
- 自动切换:Redis 通过配置参数
hash-max-listpack-entries
和hash-max-listpack-value
控制何时使用 Listpack 作为底层实现。当哈希表中的键值对数量超过hash-max-listpack-entries
或键值对的值长度超过hash-max-listpack-value
时,Redis 会自动将底层实现切换为哈希表。
总之,在 Redis 6.2 版本以后,Hash 类型数据结构中的 Listpack 底层实现是一种紧凑的连续内存数据结构,适用于存储较小的哈希表。Listpack 通过变长编码、紧凑内存分布和顺序访问等技术实现较低的内存占用和高效的访问性能。与之前的 Ziplist 实现相比,Listpack 提供了更好的性能和可维护性。
应用场景
- 缓存对象
Hash 类型的 (key,field, value) 的结构与对象的(对象id, 属性, 值)的结构相似,也可以用来存储对象。
- 购物车
用户 id 为 key,商品 id 为 field,商品数量为 value,恰好构成了购物车的3个要素
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q6X9kLDQ-1690854545413)(C:\Users\hp\AppData\Roaming\Typora\typora-user-images\image-20230801091309300.png)]
Set
Set 类型是一个无序并唯一的键值集合,它的存储顺序不会按照插入的先后顺序进行存储
底层实现
Set 类型的底层数据结构是由哈希表或整数集合实现的:
- 如果集合中的元素都是整数且元素个数小于
512
(默认值,set-maxintset-entries
配置)个,Redis 会使用整数集合作为 Set 类型的底层数据结构; - 如果集合中的元素不满足上面条件,则 Redis 使用哈希表作为 Set 类型的底层数据结构。
整数集合底层实现
哈希表底层实现
应用场景
- 点赞
Set 类型可以保证一个用户只能点一个赞,这里举例子一个场景,key 是文章id,value 是用户id。
- 共同关注
Set 类型支持交集运算,所以可以用来计算共同关注的好友、公众号等。
- 抽奖活动
存储某活动中中奖的用户名 ,Set 类型因为有去重功能,可以保证同一个用户不会中奖两次。
Zset
底层实现
Zset 类型(有序集合类型)相比于 Set 类型多了一个排序属性 score(分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序集合的元素值,一个是排序值。
Zset 类型的底层数据结构是由压缩列表或跳表实现的:
- 如果有序集合的元素个数小于
128
个,并且每个元素的值小于64
字节时,Redis 会使用压缩列表作为 Zset 类型的底层数据结构; - 如果有序集合的元素不满足上面的条件,Redis 会使用跳表作为 Zset 类型的底层数据结构;
调表底层实现
Redis 的底层实现主要包括内存分配、数据结构、事件处理、持久化等多个部分。在这里,我们主要关注 Redis 的数据结构实现,特别是调表(跳跃表)的底层实现。
跳跃表(Skip List)是一种数据结构,它允许快速地搜索、插入和删除有序数据。跳跃表的主要思想是在有序链表的基础上增加多级索引,从而减少查找时需要遍历的节点数量。跳跃表的搜索、插入和删除操作的时间复杂度为 O(log N),这使得它成为一种高效的数据结构。
Redis 使用跳跃表实现有序集合(Sorted Set)的底层数据结构。有序集合是一种同时支持按分数排序和按元素查找的数据结构。为了满足这两种需求,Redis 的有序集合同时使用跳跃表和散列表(Hash Table)存储数据。在实际使用中,跳跃表主要用于排序和范围查询操作,而散列表用于快速查找元素的分数。
以下是 Redis 跳跃表的一些底层实现细节:
- 节点结构:Redis 跳跃表的节点包含以下几个主要成分:元素、分数、后退指针(backward pointer)和层级信息。其中,元素存储有序集合的成员,分数用于排序,后退指针指向前一个节点,层级信息包含多个前进指针(forward pointer)和跨度(span)。
// Redis 跳跃表节点结构定义(源码 src/server.h)
typedef struct zskiplistNode {
robj \*obj;
double score;
struct zskiplistNode \*backward;
struct zskiplistLevel {
struct zskiplistNode \*forward;
unsigned int span;
} level[];
} zskiplistNode;
- 跳跃表结构:Redis 跳跃表包含头节点、尾节点和最大层数。头节点用于存储多级索引,尾节点指向最后一个元素,最大层数表示当前跳跃表的最大索引层数。
// Redis 跳跃表结构定义(源码 src/server.h)
typedef struct zskiplist {
struct zskiplistNode \*header, \*tail;
unsigned long length;
int level;
} zskiplist;
- 随机层数:为了保持跳跃表的平衡性,Redis 在插入节点时使用随机函数生成节点的层数。这样可以确保每一层的节点数量大约为上一层的一半,从而实现良好的查找性能。
// Redis 随机层数生成函数(源码 src/t\_zset.c)
int zslRandomLevel(void) {
int level = 1;
while ((random() & 0xFFFF) < (ZSKIPLIST_P \* 0xFFFF)) {
level += 1;
}
return (level < ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}
- 插入、删除和查找操作:Redis 跳跃表的插入、删除和查找操作都遵循类似的查找路径。首先从头节点的最高层开始查找,然后逐层向下查找,直到找到目标节点或者确定目标节点的插入位置。在查找过程中,跳跃表使用分数和元素进行比较,以确保有序集合的正确排序。
总之,Redis 使用跳跃表作为有序集合的底层数据结构,实现了高效的搜索、插入和删除操作。跳跃表的底层实现包括节点结构、跳跃表结构、随机层数和各种操作函数。通过这些底层实现,Redis 能够支持有序集合的各种功能和需求。
listpack底层实现
Listpack 是 Redis 中的一种紧凑型列表数据结构,它是为了替代原有的 ziplist 实现而引入的。Listpack 是一种紧凑的、可变长度的、连续内存数据结构,用于存储有序的值列表。它设计用于存储少量的数据,如哈希表的小数目元素。Listpack 的主要目标是实现存储和访问的高效率,同时保持较低的内存消耗。
以下是 Redis Listpack 的一些底层实现细节:
- 内存布局:Listpack 采用连续内存存储数据,包含一个 header 和多个 entry。每个 entry 包含一个特定长度的值。header 部分包含两个字段:一个 16-bit 的总长度字段(包含 header 本身的长度)和一个 16-bit 的元素数量字段。header 的大小为 6 字节。
- 数据编码:Listpack 支持多种数据编码格式,包括整数和字节串。为了节省空间,Listpack 使用标志位来表示不同类型的数据编码。对于整数类型,Listpack 使用一种可变长度的编码格式,称为 IntEnc。对于字节串类型,Listpack 使用原始字节数组存储数据。
- 操作函数:Redis Listpack 提供了一系列操作函数,用于实现列表的基本操作,如插入、删除、查找和迭代等。这些函数根据 Listpack 的内存布局和数据编码进行高效的实现。
以下是 Redis Listpack 的一些核心操作函数(源码:src/listpack.c):
lpNew
:创建一个新的 Listpack。lpFree
:释放一个 Listpack。lpLength
:获取 Listpack 中元素的数量。lpSeek
:查找指定索引的元素。lpInsert
:在指定位置插入一个元素。lpDelete
:删除指定位置的元素。lpGet
:获取指定位置的元素值。lpNext
和lpPrev
:实现 Listpack 的迭代。
总之,Redis Listpack 是一种紧凑的列表数据结构,用于存储少量的有序数据。Listpack 的底层实现包括内存布局、数据编码和各种操作函数。通过这些底层实现,Redis Listpack 能够实现高效的存储和访问操作,同时保持较低的内存消耗。
应用场景
Redis 的有序集合(Sorted Set,简称 zset)是一种将成员与分数关联的数据结构,成员之间以分数进行排序,每个成员的分数都是唯一的。zset 支持多种操作,如插入、删除、按分数范围查询、按成员范围查询和按排名查询等。zset 的底层实现使用跳跃表和散列表,以实现高效的排序和查找操作。
有序集合在许多应用场景中都非常有用,以下是一些典型的应用场景:
- 排行榜:zset 可以用于实现各种排行榜,如游戏中的积分排行榜、网站的热门文章排行榜等。通过将用户或项目的得分作为分数,将用户或项目的 ID 作为成员,可以轻松地将数据添加到有序集合中。之后,可以使用 zset 的范围查询操作来获取排名前 N 的用户或项目,或者使用排名查询操作获取特定用户或项目的排名。
- 延时任务队列:zset 可以用于实现延时任务队列。将任务的执行时间作为分数,将任务的 ID 作为成员,可以将待执行的任务添加到有序集合中。之后,可以使用 zset 的范围查询操作来获取当前需要执行的任务,并执行相应的操作。当任务完成后,可以将其从有序集合中删除。
- 时间序列数据:zset 可以用于存储时间序列数据,如用户的登录记录、传感器采集的数据等。将时间戳作为分数,将事件或数据的 ID 作为成员,可以将数据添加到有序集合中。之后,可以使用 zset 的范围查询操作来获取特定时间范围内的事件或数据。
- 自动补全:zset 可以用于实现搜索框的自动补全功能。将关键词的权重作为分数,将关键词本身作为成员,可以将热门搜索词添加到有序集合中。之后,可以使用 zset 的成员范围查询操作来根据用户输入的前缀获取匹配的关键词,并按权重排序。
- 去重统计:zset 可以用于实现去重统计,如统计用户访问次数、统计网站的不同 IP 访问次数等。将用户或 IP 的访问次数作为分数,将用户或 IP 的 ID 作为成员,可以将数据添加到有序集合中。之后,可以使用 zset 的分数查询操作来获取特定用户或 IP 的访问次数。
上述应用场景仅为有序集合的一部分,实际上 zset 可以应用于许多其他场景。总之,通过 Redis 的有序集合,我们可以实现各种有序、去重和统计功能,以满足不同的业务需求。
Bitmap
Bitmap,即位图,是一串连续的二进制数组(0和1),可以通过偏移量(offset)定位元素。BitMap通过最小的单位bit来进行0|1
的设置,表示某个元素的值或者状态,时间复杂度为O(1)。
底层实现
Bitmap(位图)并非一种单独的数据结构,而是基于字符串(String)数据结构实现的一种高效的二进制位操作方法。Bitmap 通常用于存储大量的布尔值(真/假),每个布尔值只占用一个位。由于位图是基于字符串实现的,它的底层实现实际上就是使用连续的字节(byte)存储数据。
以下是 Redis Bitmap 的一些底层实现细节:
- 内存布局:Bitmap 是基于字符串实现的,因此它的内存布局与字符串相同。字符串是一个连续的字节数组,每个字节包含 8 个位。在位图中,每个位(bit)表示一个布尔值,0 表示 false,1 表示 true。
- 位操作:Redis 提供了一系列位操作命令(如
SETBIT
、GETBIT
和BITCOUNT
等),用于实现 Bitmap 的基本操作。这些命令可以读取或修改位图中的单个位,或者统计位图中特定值的数量。位操作命令的实现主要依赖于位操作和字节操作,如位掩码和位移等。
以下是 Redis Bitmap 的一些核心位操作命令及其功能:
SETBIT
:设置位图中指定位置的值。例如,SETBIT key 10 1
将位图中第 10 位的值设置为 1。GETBIT
:获取位图中指定位置的值。例如,GETBIT key 10
将返回位图中第 10 位的值。BITCOUNT
:统计位图中特定值的数量。例如,BITCOUNT key
将返回位图中值为 1 的位的数量。BITOP
:对多个位图执行逻辑位操作(如 AND、OR、XOR 和 NOT 等)。例如,BITOP AND destkey key1 key2
将对 key1 和 key2 对应的位图执行 AND 操作,并将结果保存到 destkey 对应的位图中。
总之,Redis Bitmap 是一种基于字符串实现的高效位操作方法,用于存储大量的布尔值。Bitmap 的底层实现包括内存布局和位操作命令。通过这些底层实现,Redis Bitmap 能够实现高效的布尔值存储和访问操作,同时保持较低的内存消耗。
基于字符串实现
应用场景
Redis Bitmap(位图)是一种高效的二进制位操作方法,适用于存储大量的布尔值。由于每个布尔值仅占用一个位,Bitmap 在大规模数据处理时具有较低的内存消耗。以下是一些典型的 Redis Bitmap 应用场景:
- 用户签到功能:Bitmap 可以用于实现用户的签到功能。将每个用户的 ID 作为键,将每天的日期作为位的索引,可以记录用户的签到信息。通过
SETBIT
和GETBIT
命令可以方便地设置和获取用户的签到状态。 - 用户行为追踪:Bitmap 可以用于追踪用户的行为,如用户是否浏览过某个文章、是否点击过某个广告等。将每个用户的 ID 作为键,将行为标识(如文章 ID 或广告 ID)作为位的索引,可以记录用户的行为信息。通过
SETBIT
和GETBIT
命令可以方便地设置和获取用户的行为状态。 - 统计分析:Bitmap 可以用于统计分析,如统计活跃用户数量、统计用户某个行为的次数等。通过
BITCOUNT
命令可以统计位图中值为 1 的位的数量,从而得到相应的统计结果。此外,通过BITOP
命令可以实现多个位图之间的逻辑操作,如求用户行为的交集、并集等。 - 布隆过滤器:虽然 Redis 中的布隆过滤器是基于特殊的数据结构实现的,但我们也可以使用 Bitmap 来实现简化版的布隆过滤器。布隆过滤器是一种概率型数据结构,用于判断一个元素是否在一个集合中。将哈希函数的输出结果作为位的索引,可以使用 Bitmap 记录集合中的元素。通过多次哈希和位操作,可以实现高效的集合查询操作。
- IP 黑名单/白名单:Bitmap 可以用于实现 IP 黑名单或白名单。将 IP 地址转换为整数,然后将整数作为位的索引,可以记录 IP 的状态。通过
SETBIT
和GETBIT
命令可以方便地设置和获取 IP 的状态。
上述应用场景仅为 Redis Bitmap 的一部分,实际上 Bitmap 可以应用于许多其他场景。总之,通过 Redis Bitmap,我们可以实现大量布尔值的高效存储和访问操作,满足不同的业务需求。
HyperLogLog(统计基数)
HyperLogLog 是 Redis 2.8.9 版本新增的数据类型,是一种用于「统计基数」的数据集合类型,基数统计就是指统计一个集合中不重复的元素个数。但要注意,HyperLogLog 是统计规则是基于概率完成的,不是非常准确,标准误算率是 0.81%。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64
个不同元素的基数,和元素越多就越耗费内存的 Set 和 Hash 类型相比,HyperLogLog 就非常节省空间。
底层实现
Redis HyperLogLog(HLL)是一种概率型数据结构,用于解决基数计数问题。基数计数是统计一个集合中不重复元素的数量。传统的基数计数方法需要存储所有元素,因此会消耗大量内存。然而,HyperLogLog 通过牺牲一定的精度来实现低内存消耗。Redis 中的 HyperLogLog 实现基于 HLL 算法,并进行了一些优化。
以下是 Redis HyperLogLog 的底层实现细节:
- 数据结构:Redis HyperLogLog 使用一个固定大小的数组来存储数据。数组的每个元素称为 Register(寄存器),用于记录集合中元素的某些信息。每个寄存器占用 5 位(可以表示 0-31 的整数),因此一个 HLL 结构可以存储 2^14 个寄存器,总共占用 12 KB 内存。
- 哈希函数:为了统计集合中不重复元素的数量,需要对每个元素应用哈希函数得到一个固定长度的二进制串。Redis HyperLogLog 使用 MurmurHash64A 哈希函数,可以得到一个 64 位的哈希值。
- 寄存器索引和最大连续零数:对于每个哈希值,将其分成两部分:一部分用于寄存器索引(register index),另一部分用于计算最大连续零数(maximum consecutive zeros)。在 Redis HyperLogLog 中,哈希值的前 14 位用于寄存器索引,后 50 位用于计算最大连续零数。将哈希值映射到寄存器后,更新寄存器的值为最大连续零数。
- Harmonic Mean:Redis HyperLogLog 使用调和平均数(Harmonic Mean)来估算基数。计算调和平均数时,需要使用寄存器的值和 HLL 算法中的一些常数。最终得到的调和平均数是基数的一个近似值。
- 稀疏和密集表示:为了减少内存消耗,Redis HyperLogLog 对稀疏表示进行了优化。在数据稀疏时,可以使用更紧凑的格式来存储寄存器。当数据变得密集时,会自动切换到标准的 HLL 表示。这种优化方法在内存消耗和性能之间取得了平衡。
以下是 Redis HyperLogLog 的一些核心命令及其功能:
PFADD
:向 HyperLogLog 数据结构中添加元素。例如,PFADD key element
将元素添加到 key 对应的 HyperLogLog 结构中。PFCOUNT
:统计 HyperLogLog 数据结构中的不重复元素数量。例如,PFCOUNT key
将返回 key 对应的 HyperLogLog 结构中的基数估计值。PFMERGE
:合并多个 HyperLogLog 数据结构。例如,PFMERGE destkey key1 key2
将 key1 和 key2 对应的 HyperLogLog 结构合并,并将结果保存到 destkey 对应的 HyperLogLog 结构中。
总之,Redis HyperLogLog 是一种基于 HLL 算法实现的概率型数据结构,用于解决基数计数问题。通过牺牲一定的精度,HyperLogLog 可以实现低内存消耗的基数计数。其底层实现包括数据结构、哈希函数、寄存器索引和最大连续零数计算、Harmonic Mean 估算和稀疏表示优化等。
基于数组实现
应用场景
Redis HyperLogLog(HLL)是一种概率型数据结构,用于解决基数计数问题。它可以用来估算一个集合中不重复元素的数量,牺牲一定的精度以实现低内存消耗。以下是一些典型的 Redis HyperLogLog 应用场景:
- 统计用户活跃度:HyperLogLog 可以用于统计网站或应用中的活跃用户数量。例如,在一段时间内,记录每个访问用户的 ID。使用 HLL 结构可以快速估算出访问用户的数量,从而衡量用户活跃度。
- 网站访问量统计:HyperLogLog 可以用于统计网站的访问量,例如统计每天或每小时访问网站的独立 IP 数量。将每个访问 IP 地址添加到 HLL 结构中,可以快速估算出访问量。
- 搜索引擎查询统计:搜索引擎可以使用 HyperLogLog 来估算每个关键词的搜索结果数量。将搜索结果的 URL 添加到 HLL 结构中,可以获得一个近似的搜索结果数量。
- 在线广告曝光统计:HyperLogLog 可以用于统计在线广告的曝光次数。例如,可以记录每个广告被展示给不同用户的次数,从而了解广告的曝光情况。
- 社交网络中的关注者统计:在社交网络中,可以使用 HyperLogLog 来估算每个用户的关注者数量。将关注者的用户 ID 添加到 HLL 结构中,可以快速获得关注者数量的近似值。
- 数据流分析:在大数据处理中,可以使用 HyperLogLog 来估算数据流中的不重复元素数量。这对于数据去重、异常检测等场景非常有用。
- 数据仓库中的基数计数问题:在数据仓库中,可以使用 HyperLogLog 来估算数据集中的不重复元素数量,从而对数据进行分析和处理。
这些应用场景仅为 Redis HyperLogLog 的一部分。实际上,HLL 可以应用于许多其他需要基数计数的场景。总之,通过 Redis HyperLogLog,我们可以实现大量不重复元素数量的高效估算,满足不同的业务需求。
GEO
Redis GEO 是 Redis 3.2 版本新增的数据类型,主要用于存储地理位置信息,并对存储的信息进行操作
底层实现
Redis GEO 是 Redis 提供的一组地理空间相关的功能,用于存储地理位置信息并执行地理空间查询。Redis GEO 使用经纬度坐标表示地理位置,并提供了一系列命令来添加、更新和查询这些地理位置。以下是 Redis GEO 的底层实现细节:
- 数据结构:Redis GEO 使用 Sorted Set(有序集合)数据结构来存储地理位置信息。Sorted Set 是一个以分数排序的不重复元素集合。在 Redis GEO 中,地理位置的经纬度坐标被转换成一个一维的 GeoHash 值,然后作为分数存储在 Sorted Set 中。这样,我们可以使用 Sorted Set 的功能来执行地理空间查询。
- GeoHash:GeoHash 是一种将二维地理空间坐标(经纬度)编码为字符串的方法。GeoHash 将地球表面划分为网格,并将每个网格分配一个唯一的字符串。地理位置的经纬度坐标被映射到这些网格上。在 Redis GEO 中,GeoHash 值被编码为 52 位整数(有时也会用 53 位表示更高的精度),并用作 Sorted Set 的分数。GeoHash 的一个重要特性是,相邻的网格具有相似的 GeoHash 值,这使得 Redis GEO 能够执行地理空间查询。
- 查询算法:Redis GEO 提供了一系列地理空间查询命令,例如
GEORADIUS
和GEORADIUSBYMEMBER
。这些命令使用 Sorted Set 数据结构的功能,结合 GeoHash 算法执行查询。在查询过程中,会计算目标位置的 GeoHash 值,并查询附近的网格以找到符合条件的地理位置。通过改变查询参数,可以调整查询的精度和范围。
以下是 Redis GEO 的一些核心命令及其功能:
GEOADD
:向 GEO 数据结构中添加地理位置。例如,GEOADD key longitude latitude member
将具有给定经纬度坐标的地理位置添加到 key 对应的 GEO 结构中。GEOPOS
:获取地理位置的经纬度坐标。例如,GEOPOS key member
将返回 key 对应的 GEO 结构中 member 的经纬度坐标。GEODIST
:计算两个地理位置之间的距离。例如,GEODIST key member1 member2 [unit]
将返回 key 对应的 GEO 结构中 member1 和 member2 之间的距离,可以选择距离单位(如米、千米等)。GEORADIUS
:查询给定半径内的地理位置。例如,GEORADIUS key longitude latitude radius unit [options]
将返回 key 对应的 GEO 结构中位于给定经纬度坐标和半径范围内的地理位置。GEORADIUSBYMEMBER
:查询给定半径内的地理位置,以某个成员为中心。例如,GEORADIUSBYMEMBER key member radius unit [options]
将返回 key 对应的 GEO 结构中位于给定成员为中心的半径范围内的地理位置。
总之,Redis GEO 是一种基于 Sorted Set 数据结构和 GeoHash 算法实现的地理空间功能。通过 Redis GEO,可以存储地理位置信息并执行地理空间查询,满足不同的业务需求。
基于Sorted Set实现
应用场景
Redis GEO 是 Redis 提供的一组地理空间相关的功能,可用于存储地理位置信息并执行地理空间查询。以下是一些典型的 Redis GEO 应用场景:
- 附近的地点查询:在地图应用或者本地服务应用中,用户可能需要查找附近的餐厅、商店、酒店等。通过将这些地理位置信息存储在 Redis GEO 中,可以快速查询给定位置附近的相关地点。
- 距离计算:在物流、交通等领域,计算两个地点之间的距离是一个重要功能。通过 Redis GEO 中的
GEODIST
命令,可以快速计算两个地点之间的直线距离。 - 路径规划:在路径规划中,需要考虑地点之间的距离和方向。通过 Redis GEO,可以对地理位置进行查询和距离计算,从而实现路径规划功能。
- 位置追踪:在共享经济、车辆监控等场景中,需要对车辆、设备等进行位置追踪。通过将这些地点信息存储在 Redis GEO 中,可以实时查询和更新地理位置,从而实现位置追踪功能。
- 地理数据分析:在大数据分析中,地理位置信息可以用于分析用户行为、市场趋势等。通过 Redis GEO,可以方便地存储和查询地理位置数据,从而实现地理数据分析。
- 社交网络:在社交网络中,用户可能需要查找附近的朋友、活动等。通过将用户地理位置信息存储在 Redis GEO 中,可以快速查询附近的用户和活动。
- 游戏开发:在基于地理位置的游戏中,需要对玩家、道具等进行地理位置管理。通过 Redis GEO,可以实现地理位置的存储和查询,从而满足游戏开发的需求。
这些应用场景仅为 Redis GEO 的一部分。实际上,Redis GEO 可以应用于许多其他需要地理空间查询的场景。总之,通过 Redis GEO,我们可以实现高效的地理位置信息管理和查询,满足不同的业务需求。
stream
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
这些应用场景仅为 Redis GEO 的一部分。实际上,Redis GEO 可以应用于许多其他需要地理空间查询的场景。总之,通过 Redis GEO,我们可以实现高效的地理位置信息管理和查询,满足不同的业务需求。
stream
[外链图片转存中…(img-l4SjpcD2-1715886315775)]
[外链图片转存中…(img-PjV3xG2X-1715886315776)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!