MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

ElasticSearch实时搜索的实现原理

2021-04-277.6k 阅读

ElasticSearch 实时搜索概述

在现代的互联网应用中,实时搜索功能至关重要。无论是电商平台上用户搜索商品,社交媒体平台上查找用户或内容,还是企业内部系统搜索文档资料,用户都期望能得到即时反馈。ElasticSearch(以下简称 ES)作为一款强大的分布式搜索引擎,具备卓越的实时搜索能力。

ES 的实时搜索并非传统意义上的瞬间响应,这里的“实时”主要体现在近实时(Near - Real - Time,NRT),意味着文档写入后,能在极短延迟内被搜索到,一般延迟在秒级。这种近实时性是通过 ES 独特的架构和设计实现的。

ElasticSearch 架构基础

要理解 ES 实时搜索原理,先需了解其基础架构。ES 是基于 Lucene 构建的分布式搜索引擎。一个 ES 集群由多个节点组成,每个节点是一个独立的 ES 实例。集群可包含不同类型的节点,如主节点(Master Node)、数据节点(Data Node)和协调节点(Coordinating Node)。

  • 主节点:负责集群层面的管理工作,如创建或删除索引,管理节点加入或离开集群等。它不参与数据的存储和搜索,通过选举产生,确保集群管理的一致性。
  • 数据节点:承担数据的存储和检索任务。数据以分片(Shard)形式分布在不同的数据节点上,每个分片是一个独立的 Lucene 索引。分片机制实现了数据的分布式存储和并行处理,提升系统的扩展性和性能。
  • 协调节点:接收客户端请求,根据请求类型将其转发到合适的数据节点,收集各数据节点的响应并汇总返回给客户端。

索引结构与实时性实现

ES 的索引结构对实时搜索影响重大。在 Lucene 中,索引由多个段(Segment)组成。段是不可变的,一旦创建,其中的数据不能修改。新文档写入时,不会直接修改已有段,而是生成新段。这一特性保证了索引的一致性和查询的高效性。

ES 采用一种名为“写入后刷新(Write - then - Refresh)”的策略实现近实时搜索。当文档写入 ES 时,首先进入内存缓冲区(In - Memory Buffer)。在缓冲区中,文档以临时格式存储,等待被刷新到磁盘形成新段。默认情况下,每隔 1 秒,缓冲区中的文档会被刷新到磁盘,生成一个新的段并打开供搜索使用。这就是 ES 实现近实时搜索的关键,1 秒的刷新间隔保证了文档写入后能在短时间内被搜索到。

文档写入流程

  1. 客户端请求:客户端向 ES 集群发送写入文档的请求,该请求首先到达协调节点。
  2. 路由计算:协调节点根据文档的 ID 计算出应写入的分片位置,然后将请求转发到对应的主分片所在的数据节点。
  3. 主分片写入:主分片所在的数据节点接收到请求后,将文档写入内存缓冲区,并记录到事务日志(Transaction Log)。事务日志用于保证数据的持久性,即使节点故障,也能通过重放日志恢复未持久化的数据。
  4. 副本分片同步:主分片写入成功后,数据节点将文档同步到其所有副本分片。副本分片同样将文档写入内存缓冲区和事务日志。
  5. 刷新操作:每隔 1 秒,内存缓冲区中的文档被刷新到磁盘,生成新的段。新段被打开后,其中的文档即可被搜索。
  6. 提交操作:为保证数据的持久性和一致性,定期(默认 30 分钟或事务日志达到一定大小)会进行提交操作。提交时,内存缓冲区被清空,事务日志被截断,并记录一个新的提交点(Commit Point)。提交点记录了当前索引的状态,包括哪些段是已提交的,可用于恢复操作。

搜索流程

  1. 客户端请求:客户端发送搜索请求到协调节点。
  2. 广播请求:协调节点将搜索请求广播到索引的所有主分片和副本分片。
  3. 本地搜索:每个分片接收到请求后,在本地进行搜索。Lucene 在段中执行倒排索引查找,找出符合查询条件的文档,并返回文档 ID 和相关分数(Score)等信息。
  4. 结果收集与排序:协调节点收集各分片返回的结果,根据分数对文档进行排序,并根据请求中的分页参数返回最终结果给客户端。

代码示例

以下通过 Python 的 Elasticsearch 客户端库演示如何实现基本的实时搜索功能。

首先,安装 Elasticsearch 客户端库:

pip install elasticsearch

连接到 ES 集群并创建索引:

from elasticsearch import Elasticsearch

# 连接到 ES 集群
es = Elasticsearch(['localhost:9200'])

# 创建索引
index_name = 'test_index'
body = {
    "mappings": {
        "properties": {
            "title": {
                "type": "text"
            },
            "content": {
                "type": "text"
            }
        }
    }
}
es.indices.create(index=index_name, body=body)

插入文档:

document = {
    "title": "示例文档标题",
    "content": "这是示例文档的内容,用于演示实时搜索功能。"
}
es.index(index=index_name, id=1, body=document)

实时搜索:

query = {
    "query": {
        "match": {
            "content": "实时搜索"
        }
    }
}
result = es.search(index=index_name, body=query)
for hit in result['hits']['hits']:
    print(hit['_source'])

上述代码展示了如何使用 Python 的 Elasticsearch 客户端库连接 ES 集群、创建索引、插入文档以及进行实时搜索。通过这些操作,可以直观感受到 ES 的实时搜索能力。

深入优化实时搜索性能

虽然 ES 默认的配置和机制能实现近实时搜索,但在实际应用中,可能需要根据业务需求进一步优化。

  • 调整刷新间隔:通过修改索引的 refresh_interval 参数可调整刷新间隔。如果业务对实时性要求极高,可适当缩短刷新间隔,但这会增加磁盘 I/O 压力,因为频繁刷新会导致更多的段生成。例如,将刷新间隔设置为 0.5 秒:
es.indices.put_settings(index=index_name, body={
    "index": {
        "refresh_interval": "0.5s"
    }
})
  • 优化段合并:段过多会影响搜索性能,因为每个段都需要独立进行查询。ES 会自动进行段合并操作,将多个小段合并为大段。可通过调整 index.merge.policy 相关参数控制合并策略,如 max_merge_at_once 控制一次合并的最大段数,merge_factor 影响段合并的频率等。
  • 使用过滤器缓存:ES 支持过滤器缓存,用于缓存过滤查询的结果。对于一些经常使用的过滤器查询,启用缓存可显著提高搜索性能。例如,在查询中使用过滤器并启用缓存:
query = {
    "query": {
        "bool": {
            "filter": {
                "term": {
                    "category": "电子产品"
                }
            }
        }
    },
    "cache": true
}
result = es.search(index=index_name, body=query)

分布式与并发处理对实时性的影响

ES 的分布式特性为实时搜索带来挑战与机遇。在分布式环境中,数据分散在多个节点和分片上,节点间的网络通信延迟、副本同步等因素会影响实时性。为保证数据一致性和实时性,ES 采用了一些机制。

在并发处理方面,ES 支持高并发的写入和搜索操作。然而,高并发可能导致资源竞争,如磁盘 I/O 、网络带宽和 CPU 资源等。合理配置节点资源、优化索引设计以及采用适当的并发控制策略,对于维持实时搜索性能至关重要。例如,通过调整每个节点的线程池配置,控制并发请求的处理数量,避免资源过度消耗。

故障恢复与实时性保障

ES 具备强大的故障恢复能力,以保障实时搜索的可用性。当数据节点发生故障时,主节点会检测到并重新分配故障节点上的分片。副本分片会被提升为新的主分片,保证数据的可用性和一致性。

在故障恢复过程中,事务日志起到关键作用。通过重放事务日志,可恢复故障前未持久化的数据,确保搜索结果的完整性。虽然故障恢复会带来一定的延迟,但 ES 的设计旨在尽量减少这种影响,尽快恢复实时搜索功能。

实时搜索与大数据量

随着数据量的增长,实时搜索面临新的挑战。大数据量下,索引的构建、搜索性能以及存储管理都变得更加复杂。

ES 通过分片机制和分布式存储,能有效应对大数据量场景。合理规划分片数量和分布,对于实时搜索性能至关重要。过少的分片可能导致单个节点负载过高,影响实时性;过多的分片则会增加管理开销和搜索时的协调成本。

此外,在大数据量下,段合并操作的影响更为显著。因为大数据量会产生更多的段,频繁的段合并会消耗大量资源。通过优化段合并策略、合理设置索引参数以及使用高性能的硬件设备,可在大数据量下维持较好的实时搜索性能。

结论

ElasticSearch 的实时搜索功能是通过其独特的架构、索引结构和操作流程实现的。从文档写入、刷新到搜索的整个过程,ES 在保证数据一致性和持久性的同时,实现了近实时的搜索响应。通过深入理解其原理,并结合实际业务场景进行优化,能充分发挥 ES 在实时搜索方面的强大能力,为用户提供高效、即时的搜索体验。在面对分布式、并发、大数据量以及故障恢复等复杂场景时,ES 也提供了相应的机制和策略来保障实时搜索的性能和可用性。无论是小型应用还是大规模的企业级系统,ES 的实时搜索功能都能满足多样化的搜索需求。