架构
Quickwit 分布式搜索引擎依赖于四个主要服务和一个维护服务:
- Searchers (搜索器):通过 REST API 执行搜索查询。
- Indexers (索引器):从数据源对数据进行索引。
- Metastore (元存储):在类似 PostgreSQL 的数据库或云端存储文件中存储索引元数据。
- Control plane(控制平面):调度索引任务给索引器。
- Janitor(清理程序):执行周期性的维护任务。
此外,Quickwit 利用现有的基础设施,依靠经过验证的技术来实现索引存储、元数据存储和数据摄取:
- 云端存储:如 AWS S3、Google Cloud Storage、Azure Blob Storage 或其他兼容 S3 的存储用于索引存储。
- PostgreSQL:用于元数据存储。
- 分布式队列:如 Kafka 和 Pulsar 用于数据摄取。
Architecture diagram(架构图)
下图展示了 Quickwit 集群及其四大组件和清理程序,清理程序的作用是执行周期性的维护任务,更多详情请参见清理程序部分。
Index & splits(索引与分片)
Quickwit 索引存储文档并使其能够被高效查询。索引将文档组织成一系列较小的独立索引,称为splits(分片)。
文档是一组字段的集合。字段可以存储在不同的数据结构中:
- 倒排索引:使全文搜索变得快速。
- 列式存储,称为
fast field
。这相当于 Lucene 中的 doc values。Fast fields 用于计算匹配查询的文档上的聚合。它们还可以允许一些高级类型的过滤。 - 行存储,称为文档存储。它使得获取匹配文档的内容成为可能。
您可以配置索引来控制如何将 JSON 对象映射到 Quickwit 文档,并为每个字段定义是否应存储、索引或作为 fast field。了解如何配置您的索引
Splits(分片)
分片是索引的一个小部分,通过 UUID 来标识。对于每个分片,Quickwit 会添加一个 hotcache
文件与索引文件一起。这个 hotcache 使得搜索器能够在不到 60 毫秒的时间内打开分片,即使是在高延迟存储上也是如此。
Quickwit 索引通过维护分片元数据来识别其分片,特别是:
- 分片的状态,指示分片是否已准备好进行搜索。
- 如果存在时间戳字段,则计算出的最小/最大时间范围。
这些时间戳元数据在查询时非常有用。如果用户在其查询中指定了时间范围过滤器,Quickwit 将使用它来剔除无关的分片。
索引元数据需要对集群中的每个实例都可访问。这是通过 元存储
实现的。
Index storage(索引存储)
Quickwit 将索引数据(分片文件)存储在云端存储(AWS S3、Google Cloud Storage、Azure Blob Storage 或其他兼容 S3 的存储)上,并且在单服务器部署中也会存储在本地磁盘上。
Metastore(元存储)
Quickwit 将索引元数据汇总到元存储中,以便在整个集群中都能访问这些元数据。
在写入路径上,索引器将索引数据推送到索引存储,并将元数据发布到元存储。
在读取路径上,对于特定索引上的特定查询,搜索节点会向元存储请求索引元数据,然后使用这些元数据来进行查询规划,并最终执行该计划。
在集群部署中,元存储通常是传统的 RDBMS,比如 PostgreSQL,这是我们当前唯一支持的选项。在单服务器部署中,也可以依赖本地文件或 Amazon S3。
Quickwit 集群和服务
集群形成
Quickwit 使用 chitchat,这是一种由 Quickwit 实现的带有故障检测功能的集群成员协议。该协议受到了 Scuttlebutt 协调和 phi-accrual 检测的启发,这些想法借鉴自 Cassandra 和 DynamoDB。
Indexers(索引器)
请参阅 专门的索引文档页面。
Searchers(搜索器)
Quickwit 的搜索集群具有以下特点:
- 它由无 状态节点组成:任何节点都可以回答关于任何分片的任何查询。
- 节点可以将搜索工作负载分配给其他节点。
- 负载均衡通过 rendezvous 哈希实现,以允许高效的缓存。
这种设计提供了高可用性,同时保持架构简单。
工作负载分配:根节点和叶节点
任何搜索节点都可以处理任何搜索请求。接收查询的节点将在请求期间充当根节点。然后,它将按照以下三个步骤处理查询:
- 从元存储获取索引元数据,并确定与查询相关的分片。
- 在集群的节点之间分配分片工作负载。这些节点承担叶节点的角色。
- 等待来自叶节点的结果,合并它们,并返回聚合结果。
Stateless nodes(无状态节点)
Quickwit 集群在保持节点无状态的同时分配搜索工作负载。
得益于 hotcache,即使是在 Amazon S3 上打开分片也只需要 60 毫秒。这使得完全无状态成为可能:节点不需要了解任何索引信息。添加或移除节点只需几秒钟,而且不需要移动数据。
Rendezvous 哈希
根节点使用 rendezvous 哈希在叶节点之间分配工作负载。rendezvous 哈希使得可以在节点加入或离开集群时定义具有出色稳定性特性的节点/分片亲和力函数。这一技巧解锁了高效的缓存。
在 查询文档页面 上了解更多关于查询内部细节的信息。
Control plane(控制平面)
控制平面服务负责调度索引任务给索引器。调度会在调度器接收到外部或内部事件以及满足某些条件时执行:
- 调度器监听元存储事件:创建源、删除源、切换源或删除索引。在这些事件发生时,它会调度一个新的计划,称为“期望计划”,并将索引任务发送给索引器。
- 每个
HEARTBEAT
(3 秒)时,调度器检查“期望计划”与索引器上运行的索引任务是否同步。如果不一致,它将重新应用期望计划给索引器。 - 每分钟,调度器根据最新的元存储状态重建一个计划,如果与上次应用的计划不同,它将应用新的计划。这是必要的,因为调度器可能由于网络问题而未接收到所有元存储事件。
Janitor(清理程序)
清理程序服务在索引上运行维护任务:垃圾收集、删除查询任务和保留策略任务。
Data sources(数据源)
Quickwit 支持 多种数据源 来摄取数据。
文件非常适合一次性摄取,例如初始加载;摄取 API 或消息队列则非常适合持续地向系统喂送数据。
Quickwit 索引器直接连接到外部消息队列,如 Kafka、Pulsar 或 Kinesis,并保证恰好一次(exactly-once)的语义。如果您需要支持其他分布式队列,请在此 链接 中投票选择您需要的支持。