ES基础原理

文章介绍了ElasticSearch的基本概念,包括它的分布式特性、高可用设计,以及节点、集群和分片的概念。主分片和副本分片用于数据冗余和高可用,而倒排索引和异步写入优化了检索性能。文章还讨论了查询和过滤的区别,并概述了文档写入过程及索引压缩的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 初识:集群、节点、分片

1.1 什么是ES

        ES全称ElasticSearch,是一个基于Lucene的搜索分析引擎,它使用 Java 编写,提供了一套简单实用的 RESTful API 来帮助我们实现存储和检索。

1.2 主要特点

        分布式:无需其他中间件,可快速搭建集群,分布式存储和检索;

        高可用:提供主分片和副本分片,主从备份;集群支持自动选主;

        异步写入:集群本身异步写入;支持客户端通过BulkProcessor 异步批量写入;

1.3 基本概念

        ES和关系型数据库概念的对应关系:

RDBMSES

数据库集群

集群 
数据库实例节点
数据库(DB)索引(index)
主库(master)分片(shard)
从库(slave)副本分片(replicas)
表(table)类型(type,6.0版本废弃)
行(row)文档(doc)
列(column)字段(field)
表结构映射(mapping)
索引倒排索引

为什么要废弃type:

1. 如果字段名和类型一样,那就可以合并成一个type;

2. 如果字段名或类型不一样,结构稀疏,影响索引压缩性能和结果;

综合而言,不实用,所以废弃了。

1.4 集群 & 节点 & 分片

  • 一个运行中的 Elasticsearch 实例称为一个节点;
  • 一个或者多个拥有相同 cluster.name 配置的节点组成一个集群;
  • 集群中节点数量发生变化时,集群会rebalance所有数据;
  • 一个节点被选举成为主节点时,它负责管理集群范围内的所有变更;(增加、删除索引,或者增加、删除节点)
  • 主节点并不需要涉及到文档级别的变更和搜索等操作;
  • 请求发送到集群中的任何节点 ,包括主节点;
  • 每个节点都知道任意文档所处的位置,并转发请求,收集、返回数据;

1.4.1 节点角色

主节点&候选主节点:候选主节点经过选举可以成为主节点,主节点的主要职责是和集群操作相关的内容,如:创建或删除索引,跟踪哪些节点是群集的⼀部分,并决定哪些分⽚分配给相关的节点。一个节点既可以是候选主节点也可以是数据节点,但是由于数据节点对CPU、内存消耗都很大,所以如果某个节点既是数据节点又是主节点,那么可能会对主节点产生影响从而对整个集群的状态产生影响。因此为了提高集群的健康性,我们应该对集群中的节点做好角色上的划分和隔离。可以使用几个配置较低的机器群作为候选主节点群。

数据节点:数据节点主要是存储索引数据的节点,主要对⽂档进⾏增删改查操作,聚合操作等。数据节点对cpu,内存,io要求较⾼。

协调节点:每一个节点都隐式的是一个协调节点,如果同时设置了data.master = false和data.data=false,那么此节点将成为仅协调节点。如果无特别高的QPS,协调节点最好不要分离,跟数据节点在一起就行,分开的意义不大。

以上内容可以在ES的官方文档中查阅:

https://www.elastic.co/guide/en/elasticsearch/reference/5.4/modulesnode.html

Adding too many coordinating only nodes to a cluster can increase the burden on the entire cluster because the elected master node must await acknowledgement of cluster state updates from every node! The benefit of coordinating only nodes should not be overstated — data nodes can happily serve the same purpose.

1.4.2 分片

主分片(shard):当有大量的文档时,由于内存的限制、磁盘处理能力不足、无法足够快的响应客户端的请求等,一个节点可能不够。这种情况下,数据可以分为较小的分片。每个分片放到不同的服务器上。当你查询的索引分布在多个分片上时,ES会把查询发送给每个相关的分片,并将结果组合在一起,而应用程序并不知道分片的存在。

副本分片(replicas):为提高查询吞吐量或实现高可用性,可以使用分片副本。主从备份、读写分离。

在集群节点数一定的情况下,增加副本分片并不能提高性能,反而会有所损耗;但是,副本分片适当多时,在集群多个节点宕机时,也能尽可能的保证数据不丢失。

小tips:

一个索引有n个主分片,每个主分片有m个副本分片,那么:分片数 = n(m+1)

以下配置的总分片数量为:8*(1+1)=16

"settings": {
   "number_of_shards": "8",
   "number_of_replicas": "1"
 }

1.4.3 关系

  • 一个索引可创建n个分片,存储在不同的节点上;
  • 适当的增加主分片数量可以提升检索的速度,相反,分片过少或者过多,都会降低检索的速度;
  • 每个主分片(primary shard)不会和副本分片(replica shard)存在于同一个节点中,保证es的数据高可用性。如果当前集群除主分片所在的节点外,无其他节点创建剩余的副本分片,则集群整体的健康度为黄色。

        eg:只有3个节点,但是有3个分片和3个副本分片,这样的情况就会导致部分副本分片无法完全分配,集群健康度为黄色。

        集群状态:

        green:主分片与副本都正常分配;

        yellow:主分片全部正常分配,有副本分片未能正常分配;

        red:有主分片未能正常分配;

分片数过少:单个分片索引过大,降低整体的检索速率;

分片数过多:会导致打开比较多的文件;存储在不同机器上的,机器之间的交互通信也会增加;

建议:单个分片的数据量控制在20G-50G左右,总分片数量 = 数据总量/[20-50]G 

2 原理:一切为了检索性能

2.1 检索(query)和过滤(filter)

query: 先查询符合搜索条件的文档, 然后计算每个文档对于搜索(搜索包含分词,⼤⼩写转换、近义词转换、时态转换、单复数转换等)条件的相关度分数, 再根据评分倒序排序,没有cache功能。

filter: 只根据搜索条件过滤出符合的文档( 使用bitset <0或1> 来记录包含与否), 将这些文档的评分固定为1, 忽略TF/IDF信息, 不计算相关度分数,不用根据相关度分数进行排序, 同时ES内部还会缓存(cache)比较常用的filter的数据。

ES官网对queryfilter的解释:

query:一个评分查询,需要计算每一个文档与此查询的相关程度,同时将这个相关程度分配给表示相关性的字段 _score,并且按照相关性对匹配到的文档进行排序。这种相关性的概念是非常适合全文搜索的情况,因为全文搜索几乎没有完全“正确”的答案。

filter: 一个“不评分”或者“过滤”查询,即,这个查询仅做一个简单甄别:“这篇文档是否匹配?”,回答也是非常的简单,true 或者 false ,二者必居其一。

Tips:TF/IDF(词频/逆文档频率)算法:

1. 匹配到的关键词越稀有,文档的得分就越高(辨识度高);

2. 文档的域越小(包含比较少的Term),文档的得分就越高(避免大文档的误差);

3. 设置的权重(索引和搜索时设置的都可以)越大,文档得分越高;

本质:如果某个词或短语在⼀篇⽂章中出现的频率TF⾼,并且在其他⽂章中很少出现,则认为此词或者短语具有很好的类别区分能⼒,适合⽤来分类。但是实际上,如果⼀个词条在⼀个类的⽂档中频繁出现,则说明该词条能够很好代表这个类的⽂本的特征,这样的词条也应该给它们赋予较⾼的权重,并选来作为该类⽂本的特征词以区别与其它类⽂档,这就是IDF的不⾜之处。

2.2 文档写入

异步写入大致如下:

  1. 客户端向 ES1 节点(协调节点)发送写请求,通过路由计算公式得到值为 0,则当前数据应被写到主分片 S0 上。
  2. ES1 节点将请求转发到 S0 主分片所在的节点 ES3,ES3 接受请求并写入到磁盘。
  3. 并发将数据复制到两个副本分片 R0 上,其中通过乐观并发控制数据的冲突。一旦所有的副本分片都报告成功,则节点 ES3 将向协调节点报告成功,协调节点向客户端报告成功。

图示如下:

ES集群写入流程图

详细解析一下以上这张图,以下内容较为繁琐,各位有时间可以看下,没时间的话浏览了解即可:

  1. 写入请求会将索引(Index)存放到内存区域,叫做 Index Buffer。此时的索引文件暂时是不能被ES搜索到的。
  2. 默认情况下 ES 每秒执行一次 Refresh 操作,将 Index Buffer 中的 index 写入到 Filesystem 中的 segment,这个也是一片内存区域。把 Index Buffer 中的 index 转化为 Segment,此时的数据就可以被 ES 搜索到了,同时会清空 Index Buffer。Refresh 触发的条件有两种:一种是按照时间频率触发,默认情况是每 1 秒触发 1 次 Refresh,可通过 index.refresh_interval 设置;还有一种触发方式是当 Index Buffer 被占满的时候,会触发 Refresh,Index Buffer 的大小默认值是 JVM 所占内存容量的 10%;
  3. 当Segment 触发阈值或者达到周期性,会将Segment 文件 Flush 写入磁盘,此时 ES 会创建一个 Commit Point 文件,该文件用来标识被 Flush 到磁盘上的 Segment。

            Flush 触发的条件是,每 30 分钟或当 translog 达到一定大小(由 index.translog.flush_threshold_size 控制,默认 512mb),也就是说在满足以上条件是 ES 会触发 Flush。

            但是 Segment 在 Flush 之前仍然存在于内存中,如果此时服务器宕机,而 ES 还没有 Flush 操作保存在内存中的 Segment 数据将会丢失。为了提高 ES 的数据存储可靠性,引入了 Translog。在每次用户请求 Index Buffer 进行操作的时候都会写一份操作记录到 Translog 中,Translog 使用特有的机制保存到磁盘中。

    (1)ES 默认每个请求都会将 Translog 同步到磁盘,即配置 index.translog.durability 为 request。

    (2)但是这样会 ES 的性能有所影响,因此可以针对一些能够容忍丢失数据的场景设置异步落盘的操作。可以将 index.translog.durability 配置为 async 来提升写入 Translog 的性能,该配置会异步写入 translog 到磁盘。写入磁盘的频率通过 index.translog.sync_interval 来控制。

    是不是有点像RDB和AOF的模式呢~

另外,客户端也可以批量异步提交到集群,具体可查阅 BulkProcessor。

批量异步写入需要注意2点:

1. 单节点应用,可以在本地队列按顺序去重,减少写入的数据量;(LinkedBlockingQueue<map>)

2. 多节点应用,需要考虑同一数据在短时间内多次更新,消费到不同节点,异步写入ES集群时的顺序问题,最好能通过一些策略,将同一业务主键的数据,路由到同一应用节点写入,保证顺序性。

2.3 倒排索引 & 索引压缩

2.3.1 倒排索引

倒排索引又叫反向索引,与之对应的就必然有正向索引。那什么是正向索引,什么又是反向索引呢?

假设有如下的数据:

正向索引:

本质和书本的目录一样,是按照文档的固定顺序,顺序记录当前文档所存在的关键信息(关键字),方便检索和定位文档,是 【document -> to -> words】 模式,即: index -> content。

正向索引

 

反向索引:

按照关键字,记录出现的文档和对应的次数,是【word -> to -> documents】 模式,即:content -> [index,index]。

反向索引

 

“java”,“python”,“VUE”,“技术”这些都是term(词);

而[1,2,3]这种的叫Posting List,Posting list就是一个int的数组,存储了所有符合某个term的文档id。(是不是神似非聚簇索引呢~)

2.3.2 索引压缩

ES会为每个字段创建倒排索引,结构为:【 Term:PostingList 】形式,在此基础之上,为了提高检索效率,需要将索引全部加载到内存,那考虑到数据量较大的场景,就需要对索引做合适的压缩,具体如下:

索引压缩

总结

        本文具体阐述了ES相关的基础概念以及基本原理,其中有一些晦涩难懂的只是点,可以适当略过,真正的熟悉和学习还是要放到实践当中,后续会陆续出一些ES使用实践相关的博文,欢迎关注~ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值