ES 查询过程
ES查询
Coordinating 节点
每个节点都是协调节点。当客户端向一个节点发送查询请求时,这个节点就充当协调节点,协调节点需要处理本次请求:
解析请求参数,计算当前索引的分片;
广播查询请求到索引的各个分片,并行查询各分片数据;
合并所有分片的查询结果,返回到客户端。
两步查询
查询分两个阶段:
查询阶段协调节点将查询请求发送到当前索引的所有分片后:
各个分片根据 term 查询倒排索引取到 docId;
每个分片生成一个优先队列(根据 from size 确定),只包含 doc_id 和排序值;
所有分片将队列返回到协调节点。
取回阶段协调节点根据查询阶段查到的队列,汇总所有队列,截取 size 内容,向队列中 doc 所在的分片发起 Fetch 请求,取到 doc 的正排信息组装结果,最终返回给客户端。
深翻页请求深翻页请求为什么那么慢?在深翻页请求时:每一个分片都需要生成一个 from size 大小的队列,深翻页的 from 值一般都非常大,每个分片都需要计算资源来处理前 from 条数据,但是最终只取 size 大小的数据。CPU 和 memory 消耗都是随 ...
为什么需要 SearchEngine
为什么关系型数据库无法全文检索?慢关系型数据库通常以 B+ 树作为底层索引。一般我们在 MySQL 中模糊搜索这样用:
1select * from my_table where title like '%term%';
首先这个查询一定会触发全表扫描,即使 title 字段上建立了索引,like 语句中的 % 也会导致索引生效。
全表扫描有多慢?全表扫描本质上就是要把整张表的数据从磁盘中顺序读取出来,读取速度取决于存储介质的 I/O,因此耗时与数据量强相关,数据量越大,扫描时间越久。
相关性排序除了慢,传统数据库无法计算查询词与存储字段的相关性,数据库只保证查的出来,不保证查的相关性够强。
倒排索引
搜索引擎利用倒排索引解决上述问题。
倒排索引的本质上就是:term -> posting list 的映射。
如何构建倒排索引呢?分词 + 索引
分词搜索引擎中提供分词器 Analyzer,用于数据在写入倒排或者查询倒排时将内容切分成 term,成为倒排索引中的 key:term。
eg:有如下文本:小米手机, 为发烧而生,经过 Analyzer 处 ...
Lucene 创建索引
Lucene 创建索引SegmentLucene 将索引数据存储在 segment 中,segment 一旦写入磁盘,就不可再变:不可修改、不会删除。删除仅仅是逻辑删除,并不会物理删除;修改则是逻辑删除+写入新 segment。
写入链路
当有新索引需要写入,需要如下步骤:
写入索引缓冲区(index buffer),同时记录 translog,即事务日志,用于崩溃恢复;
Refresh:每隔 1s 将 buffer 中的 doc 刷新到 filesystem cache 中成为新的 segment,此时文档就可以被检索到了。 但还没有持久化到磁盘中。
间隔时间 refresh_interval 可以在 setting 中自定义,我们之前业务设定为 10s,意味着增量数据写入后大约 10s 可以被检索到,es 提供近实时搜索,属于业务可接受范围。
Flush:每当 30 分钟(默认) ,或者 filesystem cache 积累到一定程度、或者 translog 过大时,filesystem cache 会 flush 到磁盘中:
执行一次 refresh,清空 buffer; ...
分布式的 ES
分布式的 ES节点:一个物理或者虚拟的 ElasticSearch 进程,拥有 CPU、内存、磁盘等资源。只有 Data 节点可以持有分片;
分片:索引数据会被分成多个分片(都是主分片),副本分片是每个主分片的 copy,每个分片都是一个独立的 Lucene 索引。
ES 中一个索引(Index)逻辑上就是一个完整数据库,物理上会被分成多个分片(Shard),每个分片都是一个 Lucene 实例,Index 在创建时,分片数就确定了。
为什么要分片?
水平扩展:单个节点放不下的数据,可以分布在多个节点上;
并行处理请求:查询请求可以在多个分片中并行查询,最后合并,提高 I/O。
分片的分配创建索引时,索引一般有多个主分片,存储互不重复的数据子集。Master 节点需要计算这三个分片存到哪些 Data 节点上。
路由
前一篇描述了 Lucene 创建索引,因为在 es 中,每个分片都是一个 Lucene 节点,那么索引请求应该写入到哪个分片中呢?实际上会通过 doc_id 哈希取模得出分片 id:
1shard_num = hash(doc_id) % num_primary ...
