前言
随着大数据技术的发展,基于关系型数据库的查询无法满足庞大数据量下的秒级返回速度,es成为面试中的一项重要加分项。今天爆肝一波es相关八股文,供自己跳槽时复习参考,也跟大家分享一波。
基本概念
- index索引
- 索引类比称mysql中的database数据库,存储数据的地方
- type类型
- 类型用于定义数据结构,类似mysql的一张表,属于index中的逻辑分类
- 但是在同索引中所有相同名字的字段的 mapping 定义必须是一致的,因为在底层 Lucene 只会存一份。后续es删除了type的概念
- mapping
- 定义存储字段类型
- document
- 类比mysql中一行数据,不同之处在于es中每个文档可以有不同的字段
- field字段
- 类比mysql中的字段,属于es的最小单位
- shard分片
- 一个分片是一个底层的工作单元,仅保存全部数据的一部分,文档被存储到分片内。
- 分片是数据的容器,文档保存在分片内,分片又被分配到集群内的各个节点里。
- 单台机器无法存储大量数据,es可以将一个索引中的数据切分称多个shard,分布在多台机器上存储。有了shard就可以横向扩展,存储更多数据。
- 分散搜索、分析到多台机器上分散压力,提高吞吐量和性能
- 主分片:在索引建立的时候就已经确定了主分片数,但是副本分片数可以随时修改,索引内任意一个文档都归属于一个主分片,所以主分片的数目决定着索引能够保存的最大数据量。
- 副分片:一个副本分片只是一个主分片的拷贝。副本分片作为硬件故障时保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务。
- replica副本
- 任何一个服务器随时可能发生故障,丢失shard,因此每个shard都有多个副本,replica可以在shard发发生故障时提供备用服务,保证数据不丢失
- 多个replica还可以提升搜索操作的吞吐量和性能
什么是倒排索引
- 每个文档都对应一个文档id,文档的内容被表示为一系列关键词集合
- 比如,某个文档经过分词提取了20个关键字,每个关键字都会记录它在文档中出现的次数和位置
- 倒排索引就是关键字到文档id的映射关系,每个关键字对应一系列的文档id,这样用户搜索关键字可以根据倒排索引快速定位到文档
- 倒排索引中词汇根据字典序升序排列,一个词对应多个文档id
ElasticSearch为什么快
- 相比与传统mysql数据库,会根据主键id字段建立b+树聚簇索引,非主键字段建立b+树非聚簇索引查找到对应主键回表查询数据,而对于全文检索like%张三则走不到索引。
name:
Term |
Posting List(文档id集合) |
张三 |
【1】 |
张四 |
【2】 |
李四 |
【3】 |
李五 |
【4】 |
age:
Term |
Posting List(文档id集合) |
23 |
【2,3】 |
24 |
【1,4】 |
- elasticsearch借助倒排索引加速查询
- posting list
- Elasticsearch会为每个field都建立了一个倒排索引,张三、李四为term(分词),而[1,4]就是posting List。Posting list就是一个int的数组,存储了所有包含某个term的文档id。
- 通过posting list这种索引方式似乎可以很快进行查找,比如要找age=24的同学,很快就会找到,id是1,4的同学。但是,如果有上千万的记录呢?如果是想通过name来查找呢?所以需要将Term进行排序
- term dict
- 为了快速找到某个特定的term,将所有的term进行排序。再采用二分查找法查找term。时间复杂度logN,类似B-Tree的方式
- term index
- 包含term的一些前缀。所以term index 占用的空间只有term的的几十分之一。在内存里可以放更多的term index。缓存所有的term index到内存里是可以的。
- Term Index,就像字典里的索引页一样,A开头的有哪些term,分别在哪页,可以理解term index是一颗树存储在内存中
ElasticSerach基本语法
- query:代表查询,搜索 类似于SQL的select关键字
- aggs:代表聚合,类似于SQL的group by 关键字,对查询出来的数据进行聚合 求平均值最大值等
- highlight:对搜索出来的结果中的指定字段进行高亮显示,搜索“中华人民共和国万岁”,结果里面符合搜索关键字 全部是红色的高亮显示。
- sort: 指定字段对查询结果进行排序显示,类比SQL的order by关键字
- from和*size: 对查询结果分页,类似于SQL的limit关键字
- post_filter:后置过滤器,在聚合查询结果之后,再对查询结果进行过滤。
ElastcSearch的字段类型
- string类型
- text类型
- keyword类型
- 数值类型
- 日期类型
- 布尔类型
- 二进制
ElasticSerach的_source字段
- es在创建索引文档时,会将所有的字段json序列化,保存为_source字段
- 功能
- 重做索引
- 修改mapping和分析器
- 高亮提醒
- 便于调试
- 是否保存
- 取决于什么类型的数据,日志类型的索引基本不用保存
- 为了避免过多占用磁盘空间,可以开启文件压缩
ElasticSerach的调度处理
- 写数据
- 在文档写入时,会根据_routing来计算(OperationRouting类)得出文档要写入哪个分片
- 写入请求只会写主分片,当主分片写入成功后,会同时把写入请求发送给所有的副本分片,当副本分片写入成功后,会传回返回信息给主分片,主分片得到所有副本分片的返回信息后,再返回给客户端。(类似kafka副本机制)
- 搜索数据
- 客户端发送请求到一个 coordinate node(调度节点)
- 协调节点将搜索请求转发到该索引所有的 shard 对应的 primary shard或 replica shard 上
- query phase:
- 每个 shard 将自己的搜索结果(一些 doc id )返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果
- fetch phase(读数据过程):
- 接着由协调节点根据 doc id 去各个节点上拉取实际的 doc 数据,最终返回给客户端
- 写请求是写入 primary shard,然后同步给所有的 replica shard;
- 读请求可以从 primary shard 或 replica shard 读取,采用的是随机轮询算法
ElasticSearch深度分页
- scroll分页
- 使用scroll分页可以模拟一个游标.记录当前读取的文档位置.
- 这个分页用法会在 es服务端维护一个当前索引的快照信息,在此快照创建以后任何新增的数据,都无法在这个快照中查询到,所以这种分页用法不能用于实时查询数据,而是用于一次查询大量的数据.
- search_after分页
- 是一种假分页方式,根据上一页的最后一条数据来确定下一页的位置
- 同时在分页请求的过程中,如果有索引数据的增删改查,这些变更也会实时的反映到游标上。为了找到每一页最后一条数据,每个文档必须有一个全局唯一值
- 官方推荐使用 _uid 作为全局唯一值,但是只要能表示其唯一性就可以。
ElasticSearch的写入过程
- 数据先写入内存buffer(lucene内存),在buffer里的数据搜索不到;同时写入translog文件
- buffer在一定的时间或者容量满了,会将内存buffer数据刷新到操作系统内存(es内存)中的segment文件,此时数据可见。此动作1s执行1次
- translog文件默认每5秒刷新到磁盘中
- 异步从缓存中将segment刷新到磁盘,记录commit ponit,segment会定时merge
- 将多个segment合并成一个,将新的segement写入磁盘
- 新增一个 commit point,标识所有新的 segment
- 新的 segment 被打开供搜索使用
- 删除旧的 segment
ElasticSearch的删除和更新过程
- 删除和更新都是写操作,但是由于 Elasticsearch 中的文档是不可变的,因此不能被删除或者改动以展示其变更;所以 ES 利用 .del 文件 标记文档是否被删除,磁盘上的每个段都有一个相应的.del 文件
- 如果是删除操作,文档其实并没有真的被删除,而是在 .del 文件中被标记为 deleted 状态。该文档依然能匹配查询,但是会在结果中被过滤掉。
- 如果是更新操作,就是将旧的 doc 标识为 deleted 状态,然后创建一个新的 doc。
- 每次segment merge 的时候,会将多个 segment 文件合并成一个,同时这里会将标识为 deleted 的 doc 给物理删除掉,不写入到新的 segment 中,然后将新的 segment 文件写入磁盘,这里会写一个 commit point ,标识所有新的 segment 文件,然后打开 segment 文件供搜索使用,同时删除旧的 segment 文件
ElasticSearch的搜索过程
- 搜素分为两个阶段执行,Query、Fetch
- Query:
- 客户端发送请求到 coordinate node,协调节点将搜索请求广播到所有的 primary shard 或 replica shard。每个分片在本地执行搜索并构建一个匹配文档的大小为 from + size 的优先队列。每个分片返回各自优先队列中 所有文档的 ID 和排序值 给协调节点,由协调节点及逆行数据的合并、排序、分页等操作,产出最终结果。
- Fetch:
- 协调节点根据 doc id 去各个节点上查询实际的 document 数据,由协调节点返回结果给客户端。
- coordinate node 对 doc id 进行哈希路由,将请求转发到对应的 node,此时会使用 round-robin 随机轮询算法,在 primary shard 以及其所有 replica 中随机选择一个,让读请求负载均衡。
- 接收请求的 node 返回 document 给 coordinate node 。
- coordinate node 返回 document 给客户端。
ES优化索引性能
es官网就有一些性能优化建议,这里只是简单提一些,因为写多了也记不住。。。。
- 批量写入
- 多线程写入,写入线程数一般和机器数相当,可以配多种情况,在测试环境通过Kibana观察性能曲线。
- 增加segment刷新时间,查找完手动调用API刷新。
ES优化检索性能
- 关闭不需要字段的doc value(正排索引)
- 尽量使用keyword替代一些long或者int之类,term查询总比range查询好 (参考lucene说明
- 分页使用search_after
- 关闭不需要查询字段的_source功能,不将此存储仅ES中,以节省磁盘空间