96SEO 2026-02-19 11:01 0
为每个节点创建一个批量请求并将这些请求并行转发到每个包含主分片的节点主机。

主分片一个接一个按顺序执行每个操作。
当每个操作成功时主分片并行转发新文档或删除到副本分片然后执行下一个操作。
一旦所有的副本分片报告所有操作成功该节点将向协调节点报告成功协调节点将这些响应收集整理并返回给客户端。
在考虑或分析一个分布式系统的写操作时一般需要从下面几个方面考虑
可靠性或者是持久性数据写入系统成功后数据不会被回滚或丢失。
一致性数据写入成功后再次查询时必须能保证读取到最新版本的数据不能读取到旧数据。
原子性一个写入或者更新操作要么完全成功要么完全失败不允许出现中间状态。
隔离性多个写入操作相互不影响。
实时性写入后是否可以立即被查询到。
性能写入性能吞吐量到底怎么样。
可靠性由于Lucene的设计中不考虑可靠性在Elasticsearch中通过Replica和TransLog两套机制保证数据的可靠性。
一致性Lucene中的Flush锁只保证Update接口里面Delete和Add中间不会Flush但是Add完成后仍然有可能立即发生Flush导致Segment可读。
这样就没法保证Primary和所有其他Replica可以同一时间Flush就会出现查询不稳定的情况这里只能实现最终一致性。
原子性Add和Delete都是直接调用Lucene的接口是原子的。
当部分更新时使用Version和锁保证更新是原子的。
隔离性仍然采用Version和局部锁来保证更新的是特定版本的数据。
实时性使用定期Refresh
Segment方式保证搜索可以在较短时间比如1秒内被搜索到。
通过将未刷新到磁盘数据记入TransLog保证对未提交数据可以通过ID实时访问到。
性能性能是一个系统性工程所有环节都要考虑对性能的影响在Elasticsearch中在很多地方的设计都考虑到了性能一是不需要所有Replica都返回后才能返回给用户只需要返回特定数目的就行二是生成的Segment现在内存中提供服务等一段时间后才刷新到磁盘Segment在内存这段时间的可靠性由TransLog保证三是TransLog可以配置为周期性的Flush但这个会给可靠性带来伤害四是每个线程持有一个Segment多线程时相互不影响相互独立性能更好五是系统的写入流程对版本依赖较重读取频率较高因此采用了versionMap减少热点数据的多次磁盘IO开销。
Lucene中针对性能做了大量的优化。
Elasticsearch采用多Shard方式通过配置routing规则将数据分成多个数据子集每个数据子集提供独立的索引和搜索功能。
当写入文档的时候根据routing规则将文档发送给特定Shard中建立索引。
这样就能实现分布式了。
每个Index由多个Shard组成每个Shard有一个主节点和多个副本节点副本个数可配。
但每次写入的时候写入请求会先根据_routing规则选择发给哪个ShardIndex
Request中可以设置使用哪个Filed的值作为路由参数如果没有设置则使用Mapping中的配置如果mapping中也没有配置则使用_id作为路由参数然后通过_routing的Hash值选择出Shard在OperationRouting类中最后从集群的Meta中找出出该Shard的Primary节点。
Write)。
只要有副本在写入延时最小也是两次单Shard的写入时延总和写入效率会较低但是这样的好处也很明显避免写入后单机或磁盘故障导致数据丢失在数据重要性和性能方面一般都是优先选择数据除非一些允许丢数据的特殊场景。
采用多个副本后避免了单机或磁盘故障发生时对已经持久化后的数据造成损害但是Elasticsearch里为了减少磁盘IO保证读写性能一般是每隔一段时间比如5分钟才会把Lucene的Segment写入磁盘持久化对于写入内存但还未Flush到磁盘的Lucene数据如果发生机器宕机或者掉电那么内存中的数据也会丢失这时候如何保证
对于这种问题Elasticsearch学习了数据库中的处理方式增加CommitLog模块Elasticsearch中叫TransLog。
在每一个Shard中写入流程分为两部分先写入Lucene再写入TransLog。
写入请求到达Shard后先写Lucene文件创建好索引此时索引还在内存里面接着去写TransLog写完TransLog后刷新TransLog数据到磁盘上写磁盘成功后请求返回给用户。
这里有几个关键点:
一是和数据库不同数据库是先写CommitLog然后再写内存而Elasticsearch是先写内存最后才写TransLog一种可能的原因是Lucene的内存写入会有很复杂的逻辑很容易失败比如分词字段长度超过限制等比较重为了避免TransLog中有大量无效记录减少recover的复杂度和提高速度所以就把写Lucene放在了最前面。
二是写Lucene内存后并不是可被搜索的需要通过Refresh把内存的对象转成完整的Segment后然后再次reopen后才能被搜索一般这个时间设置为1秒钟导致写入Elasticsearch的文档最快要1秒钟才可被从搜索到所以Elasticsearch在搜索方面是NRTNear
Time近实时的系统。
三是当Elasticsearch作为NoSQL数据库时查询方式是GetById这种查询可以直接从TransLog中查询这时候就成了RTReal
Time实时系统。
四是每隔一段比较长的时间比如30分钟后Lucene会把内存中生成的新Segment刷新到磁盘上刷新后索引文件已经持久化了历史的TransLog就没用了会清空掉旧的TransLog。
Lucene中不支持部分字段的Update所以需要在Elasticsearch中实现该功能具体流程如下
收到Update请求后从Segment或者TransLog中读取同id的完整Doc记录版本号为V1。
将版本V1的全量Doc和请求中的部分字段Doc合并为一个完整的Doc同时更新内存中的VersionMap。
获取到完整Doc后Update请求就变成了Index请求。
加锁。
再次从VersionMap中读取该id的最大版本号V2如果VersionMap中没有则从Segment或者TransLog中读取这里基本都会从VersionMap中获取到。
检查版本是否冲突(V1V2)如果冲突则回退到开始的“Update
doc”阶段重新执行。
如果不冲突则执行最新的Add请求。
在Index
1得到V3再将Doc加入到Lucene中去Lucene中会先删同id下的已存在doc
id然后再增加新Doc。
写入Lucene成功后将当前V3更新到VersionMap中。
释放锁部分更新的流程就结束了。
在处理读取请求时协调结点在每次请求的时候都会通过轮询所有的副本分片来达到负载均衡。
在文档被检索时已经被索引的文档可能已经存在于主分片上但是还没有复制到副本分片。
在这种情况下副本分片可能会报告文档不存在但是主分片可能成功返回文档。
为每个分片构建多文档获取请求然后并行转发这些请求到托管在每个所需的主分片或者副本分片的节点上。
一旦收到所有答复
第一阶段查询到匹配的DocID第二阶段再查询DocID对应的完整文档这种在Elasticsearch中称为query_then_fetch。
除了一阶段两阶段外还有一种三阶段查询的情况。
搜索里面有一种算分逻辑是根据TFTerm
Frequency计算基础分但是Elasticsearch中查询的时候是在每个Shard中独立查询的每个Shard中的TF和DF也是独立的虽然在写入的时候通过_routing保证Doc分布均匀但是没法保证TF和DF均匀那么就有会导致局部的TF和DF不准的情况出现这个时候基于TF、DF的算分就不准。
为了解决这个问题Elasticsearch中引入了DFS查询比如DFS_query_then_fetch会先收集所有Shard中的TF和DF值然后将这些值带入请求中再次执行query_then_fetch这样算分的时候TF和DF就是准确的类似的有DFS_query_and_fetch。
这种查询的优势是算分更加精准但是效率会变差。
在初始查询阶段时查询会广播到索引中每一个分片拷贝主分片或者副本分片。
给协调节点它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。
接下来就是
取回阶段协调节点辨别出哪些文档需要被取回并向相关的分片提交多个
请求。
每个分片加载并丰富文档如果有需要的话接着返回文档给协调节点。
一旦所有的文档都被取回了协调节点返回结果给客户端。
判断是否需要跨集群访问如果需要则获取到要访问的Shard列表。
获取当前Cluster中要访问的Shard和上一步中的Remove
上一步将请求发送给多个Shard后这一步就是异步等待返回结果然后对结果合并。
这里的合并策略是维护一个Top
N大小的优先级队列每当收到一个shard的返回就把结果放入优先级队列做一次排序直到所有的Shard都返回。
40的结果这个的意思是所有Shard查询结果中的第30到40的结果那么在每个Shard中无法确定最终的结果每个Shard需要返回Top
上述翻页逻辑有一个明显的缺点就是每次Shard返回的数据中包括了已经翻过的历史结果如果翻页很深则在这里需要排序的Docs会很多比如Shard有1000取第9990到10000的结果那么这次查询Shard总共需要返回1000
另一种翻页方式是使用search_after这种方式会更轻量级如果每次只需要返回10条结构则每个Shard只需要返回search_after之后的10个结果即可返回的总数据量只是和Shard个数以及本次需要的个数有关和历史已读取的个数无关。
这种方式更安全一些推荐使用这种。
如果有aggregate也会在这里做聚合但是不同的aggregate类型的merge策略不一样具体的可以在后面的aggregate文章中再介绍。
Context之后Search过程中的所有中间状态都会存在Context中这些状态总共有50多个具体可以查看DefaultSearchContext或者其他SearchContext的子类。
Context。
这里会根据请求中Query类型的不同创建不同的Query对象比如TermQuery、FuzzyQuery等最终真正执行TermQuery、FuzzyQuery等语义的地方是在Lucene中。
这里包括了dfsPhase、queryPhase和fetchPhase三个阶段的preProcess部分只有queryPhase的preProcess中有执行逻辑其他两个都是空逻辑执行完preProcess后所有需要的参数都会设置完成。
由于Elasticsearch中有些请求之间是相互关联的并非独立的比如scroll请求所以这里同时会设置Context的生命周期。
同时会设置lowLevelCancellation是否打开这个参数是集群级别配置同时也能动态开关打开后会在后面执行时做更多的检测检测是否需要停止后续逻辑直接返回。
判断请求是否允许被Cache如果允许则检查Cache中是否已经有结果如果有则直接读取Cache如果没有则继续执行后续步骤执行完后再将结果加入Cache。
Collector主要目标是收集查询结果实现排序对自定义结果集过滤和收集等。
这一步会增加多个Collectors多个Collector组成一个List。
Filter用于SearchAgg等结束后再次对结果做Filter希望Filter不影响Agg结果。
如果有Post
Filter则创建一个FilteredCollector加入Collector
List中。
PluginInMultiCollector判断请求中是否制定了自定义的一些Collector如果有则创建后加入Collector
List。
MinimumScoreCollector判断请求中是否制定了最小分数阈值如果指定了则创建MinimumScoreCollector加入Collector
List中在后续收集结果时会过滤掉得分小于最小分数的Doc。
EarlyTerminatingCollector判断请求中是否提前结束Doc的Seek如果是则创建EarlyTerminatingCollector加入Collector
List中。
在后续Seek和收集Doc的过程中当Seek的Doc数达到Early
Terminating后会停止Seek后续倒排链。
CancellableCollector判断当前操作是否可以被中断结束比如是否已经超时等如果是会抛出一个TaskCancelledException异常。
该功能一般用来提前结束较长的查询请求可以用来保护系统。
EarlyTerminatingSortingCollector如果Index是排序的那么可以提前结束对倒排链的Seek相当于在一个排序递减链表上返回最大的N个值只需要直接返回前N个值就可以了。
这个Collector会加到Collector
List的头部。
EarlyTerminatingSorting和EarlyTerminating的区别是EarlyTerminatingSorting是一种对结果无损伤的优化而EarlyTerminating是有损的人为掐断执行的优化。
TopDocsCollector这个是最核心的Top
List的头部。
TopScoreDocCollector和TopFieldCollector都是TopDocsCollector的子类TopScoreDocCollector会按照固定的方式算分排序会按照分数doc
id小的文档。
而TopFieldCollector则是根据用户指定的Field的值排序。
这一步会调用Lucene中IndexSearch的search接口执行真正的搜索逻辑。
每个Shard中会有多个Segment每个Segment对应一个LeafReaderContext这里会遍历每个Segment到每个Segment中去Search结果然后计算分数。
搜索里面一般有两阶段算分第一阶段是在这里算的会对每个Seek到的Doc都计算分数为了减少CPU消耗一般是算一个基本分数。
这一阶段完成后会有个排序。
然后在第二阶段再对Top
的结果做一次二阶段算分在二阶段算分的时候会考虑更多的因子。
二阶段算分在后续操作中。
具体请求比如TermQuery、WildcardQuery的查询逻辑都在Lucene中后面会有专门文章介绍。
根据Request中是否包含rescore配置决定是否进行二阶段排序如果有则执行二阶段算分逻辑会考虑更多的算分因子。
二阶段算分也是一种计算机中常见的多层设计是一种资源消耗和效率的折中。
Elasticsearch中支持配置多个Rescore这些rescore逻辑会顺序遍历执行。
每个rescore内部会先按照请求参数window选择出Top
window的doc然后对这些doc排序排完后再合并回原有的Top
如果有推荐请求则在这里执行推荐请求。
如果请求中只包含了推荐的部分则很多地方可以优化。
推荐不是今天的重点这里就不介绍了后面有机会再介绍。
如果含有聚合统计请求则在这里执行。
Elasticsearch中的aggregate的处理逻辑也类似于Search通过多个Collector来实现。
在Client
Node中也需要对aggregation做合并。
aggregate逻辑更复杂一些就不在这里赘述了后面有需要就再单独开文章介绍。
上述逻辑都执行完成后如果当前查询请求只需要查询一个Shard那么会直接在当前Node执行Fetch
ExplainFetchSubPhaseDocValueFieldsFetchSubPhaseScriptFieldsFetchSubPhaseFetchSourceSubPhaseVersionFetchSubPhaseMatchedQueriesFetchSubPhaseHighlightPhaseParentFieldSubFetchPhase
除了系统默认的8种外还有通过插件的形式注册自定义的功能这些SubPhase中最重要的是Source和HighlightSource是加载原文Highlight是计算高亮显示的内容片断。
上述多个SubPhase会针对每个Doc顺序执行可能会产生多次的随机IO这里会有一些优化方案但是都是针对特定场景的不具有通用性。
Elasticsearch作为搜索系统时或者任何搜索系统中除了Query阶段外还会有一个Fetch阶段这个Fetch阶段在数据库类系统中是没有的是搜索系统中额外增加的阶段。
搜索系统中额外增加Fetch阶段的原因是搜索系统中数据分布导致的在搜索中数据通过routing分Shard的时候只能根据一个主字段值来决定但是查询的时候可能会根据其他非主字段查询那么这个时候所有Shard中都可能会存在相同非主字段值的Doc所以需要查询所有Shard才能不会出现结果遗漏。
同时如果查询主字段那么这个时候就能直接定位到Shard就只需要查询特定Shard即可这个时候就类似于数据库系统了。
另外数据库中的二级索引又是另外一种情况但类似于查主字段的情况这里就不多说了。
基于上述原因第一阶段查询的时候并不知道最终结果会在哪个Shard上所以每个Shard中管都需要查询完整结果比如需要Top
10那么每个Shard都需要查询当前Shard的所有数据找出当前Shard的Top
Doc内容的操作比较耗费IO和CPU如果在第一阶段就Fetch
N读取Doc内容。
通过增加一点网络开销而避免大量IO和CPU操作这个折中是非常划算的。
Fetch阶段的目的是通过DocID获取到用户需要的完整Doc内容。
这些内容包括了DocValuesStoreSourceScript和Highlight等具体的功能点是在SearchModule中注册的系统默认注册的有
Lucene和EIasticsearch内部打分是如何运作的提升特定查询或字段的得分使用解释的API接口来理解词频、逆文档频率、相关性得分通过重新计算文档子集的得分来减少评分操作的性能影响使用function—score查询获取终极的打分能力字段数据的缓存以及它是如何影响实例的
随机性分歧Divergencefromrandomness即DFR相似度基于信息的lnformation即IB相似度LMDirichIet相似度LMJeIinekMercer相似度。
{similarity:{customSimilarity:{type:BM25,k1:1.2,b:0.75,discount_overlaps:false}}},mappings:
}在setting.similarity参数定义评分算法并且在字段的similarity指定自定义的评分算法。
BM25有3种主要的设置即k1、b和discount_overlaps:
k1:k1控制对于得分而言词频词条出现在文档里的频繁程度或者是TF的重要性。
b:b是介于0到1之间的数值它控制了文档篇幅对于得分的影响程度。
discount_overlaps:discount_overlaps的设置告知Elasticsearch在某个字段中多个分词出现在同一个位置是否应该影响长度的标准化。
默认值是true.
如果希望全局修改评分算法在elasticsearch.yml配置文件中配置
boosting有两种类型当索引或者查询文档的时候可以提升一篇文档的得分。
在索引期间修改的文档boosting是存储在索引中的修改boosting值唯一的方法是重新索引这篇文档。
鉴于此我们当然建议用户使用查询期间的boosting因为这样更为灵活并允许用户改变主意在不重新索引数据的前提下改变字段或者词条的重要性。
}在设置该索引的映射后任何自动索引的文档就拥有一个boost值运用于name字段的词条中。
再次强调一下请记住这个boost的值是固定的(fixed也就是说如果决定修改这个值你必须重新索引文档。
精度丢失boost的值是以低精度的数值存储在Lucene的内部索引结构中。
只有一个字节用于存储浮点型数值所以计算文档的最终得分时可能会丢失精度。
boost是运用于一个词条的。
因此在被boost的字段中如果匹配上了多个词条就意味着多次的boost将会进一步增加字段的权重。
当进行搜索的时候有几种方法进行boosting。
如果使用基本的match、multi_match、simple_query_string或query_string查询就可以基于每个词条或者每个字段来控制boost.几乎所有的Elasticsearch查询类型都支持boosting.如果这个还不够灵活那么可以通过function_score查询以更为精细的方式来控制boosting.
keyword}}},attribute:{type:text},desc:{type:text},price:{type:
Max采用了全新的设计语言机身采用航空级铝合金打造正面是一块6.7英寸的超视网膜XDR显示屏分辨率高达4K显示效果惊艳。
同时这款手机还支持120Hz的高刷新率让您在浏览网页、玩游戏时更加流畅。
,price:
Max采用了全新的设计语言机身采用航空级铝合金打造正面是一块6.7英寸的超视网膜XDR显示屏分辨率高达4K显示效果惊艳。
同时这款手机还支持120Hz的高刷新率让您在浏览网页、玩游戏时更加流畅。
,price:
Max采用了全新的设计语言机身采用航空级铝合金打造正面是一块6.7英寸的超视网膜XDR显示屏分辨率高达4K显示效果惊艳。
同时这款手机还支持120Hz的高刷新率让您在浏览网页、玩游戏时更加流畅。
,price:
}增加boost参数默认1.0。
在搜索中我们认为attribute
当使用bool或and/or/not组合多个查询时boost查询才有意义。
multi_match和match类似多个字段指定同一个boost值。
match、multi_match查询是为字段指定boost值query_string可以为搜索文本中的某个词设置boost。
}认为256比iPhone关键词相关性更加高为256设置更高的boost。
/mall_goods_test/_doc/1/_explain
}查看_id1的文档为何没有匹配“Huawei”从结果明显看出是不匹配。
1.Lucene和EIasticsearch内部打分是如何运作的
Elasticsearch中的写入请求类型主要包括下列几个Index(Create)UpdateDelete和Bulk其中前3个是单文档操作后一个Bulk是多文档操作其中Bulk中可以包括Index(Create)Update和Delete。
在6.0.0及其之后的版本中前3个单文档操作的实现基本都和Bulk操作一致甚至有些就是通过调用Bulk的接口实现的。
估计接下来几个版本后Index(Create)UpdateDelete都会被当做Bulk的一种特例化操作被处理。
这样代码和逻辑都会更清晰一些。
在这一步可以对原始文档做一些处理比如HTML解析自定义的处理具体处理逻辑可以通过插件来实现。
在Elasticsearch中由于Ingest
Pipeline会比较耗费CPU等资源可以设置专门的Ingest
判断当前Index是否存在如果不存在则需要自动创建Index这里需要和Master交互。
也可以通过配置关闭自动创建Index的功能。
设置路由条件如果Request中指定了路由条件则直接使用Request中的Routing否则使用Mapping中配置的如果Mapping中无配置则使用默认的_id字段值。
在这一步中如果没有指定id字段则会自动生成一个唯一的_id字段目前使用的是UUID。
Request中会包括多个(Index/Update/Delete)请求这些请求根据routing可能会落在多个Shard上执行这一步会按Shard挑拣Single
Request同一个Shard中的请求聚集在一起构建BulkShardRequest每个BulkShardRequest对应一个Shard。
这一步会将每一个BulkShardRequest请求发送给相应Shard的Primary
请求的入口是在PrimaryOperationTransportHandler的messageReceived.
Request对于每个Request根据操作类型(CREATE/INDEX/UPDATE/DELETE)选择不同的处理逻辑。
其中Create/Index是直接新增DocDelete是直接根据_id删除DocUpdate会稍微复杂些我们下面就以Update为例来介绍。
这一步是Update操作的特有步骤在这里会将Update请求转换为Index或者Delete请求。
首先会通过GetRequest查询到已经存在的同_id
Doc如果有的完整字段和值依赖_source字段然后和请求中的Doc合并。
同时这里会获取到读到的Doc版本号记做V1。
这里会解析Doc中各个字段。
生成ParsedDocument对象同时会生成uid
_id对用户_Id可见而Elasticsearch中存储的是_uid。
这一部分生成的ParsedDocument中也有Elasticsearch的系统字段大部分会根据当前内容填充部分未知的会在后面继续填充ParsedDocument。
Elasticsearch中有个自动更新Mapping的功能就在这一步生效。
会先挑选出Mapping中未包含的新Field然后判断是否运行自动更新Mapping如果允许则更新Mapping。
Service获取一个sequenceID和Version。
SequenceID在Shard级别每次递增1SequenceID在写入Doc成功后会用来初始化LocalCheckpoint。
Version则是根据当前Doc的最大Version递增1。
这一步开始的时候会给特定_uid加锁然后判断该_uid对应的Version是否等于之前Translate
Index步骤里获取到的Version如果不相等则说明刚才读取Doc后该Doc发生了变化出现了版本冲突这时候会抛出一个VersionConflict的异常该异常会在Primary
如果Version相等则继续执行如果已经存在同id的Doc则会调用Lucene的UpdateDocument(uid,
doc)接口先根据uid删除Doc然后再Index新Doc。
如果是首次写入则直接调用Lucene的AddDocument接口完成Doc的IndexAddDocument也是通过UpdateDocument实现。
这一步中有个问题是如何保证Delete-Then-Add的原子性怎么避免中间状态时被Refresh答案是在开始Delete之前会加一个Refresh
Lock禁止被Refresh只有等Add完后释放了Refresh
Lock后才能被Refresh这样就保证了Delete-Then-Add的原子性。
Lucene的UpdateDocument接口中就只是处理多个Field会遍历每个Field逐个处理处理顺序是invert
写完Lucene的Segment后会以key-value的形式写TransLogKey是_idValue是Doc内容。
当查询的时候如果请求是GetDocByID则可以直接根据_id从TransLog中读取到满足NoSQL场景下的实时性要去。
需要注意的是这里只是写入到内存的TransLog是否Sync到磁盘的逻辑还在后面。
这一步的最后会标记当前SequenceID已经成功执行接着会更新当前Shard的LocalCheckPoint。
Request原因是前面已经将UpdateRequest翻译成了Index或Delete请求则后续所有Replica中只需要执行Index或Delete请求就可以了不需要再执行Update逻辑一是保证Replica中逻辑更简单性能更好二是保证同一个请求在Primary和Replica中的执行结果一样。
这里会根据TransLog的策略选择不同的执行方式要么是立即Flush到磁盘要么是等到以后再Flush。
Flush的频率越高可靠性越高对写入性能影响越大。
Request并行发送给多个Replica然后等待Replica的返回这里需要等待所有Replica返回后可能有成功也有可能失败Primary
Node才会返回用户。
如果某个Replica失败了则Primary会给Master发送一个Remove
这里同时会将SequenceIDPrimaryTermGlobalCheckPoint等传递给Replica。
[R]这里的R表示Replica。
通过这个[R]的不同可以找到处理Replica请求的Handler。
请求的入口是在ReplicaOperationTransportHandler的messageReceived.
根据请求类型是Index还是Delete选择不同的执行逻辑。
这里没有Update是因为在Primary
Node中已经将Update转换成了Index或Delete请求了。
ID和Version然后放入ReplicaRequest中这里只需要从Request中获取到就行。
Node中将部分Update请求转换成了Index或Delete请求这里只需要处理Index和Delete两种请求不再需要处理Update请求了。
比Primary
Elasticsearch查询的详细过程是复杂的而它的核心部分就是倒排索引。
这里将概述查询的过程包括分片的运用、主从节点的交互以及数据排序和结果汇总。
当一个查询到达Elasticsearch时它首先被发送到一个协调节点coordinating
node。
这个节点负责解析查询确定需要从哪些分片shards检索数据并将查询请求转发到这些分片所在的节点。
在Elasticsearch中每个索引被分为多个分片并且每个分片可以有一个或多个副本。
副本分为主副本primary
shard。
查询可以在主分片或任何一个从分片上执行这有助于分散读取负载并提高查询性能。
协调节点通常会根据分片的当前负载情况来决定发送查询请求给主分片或某个从分片。
查询被发送到负责存储相关分片的节点后每个节点上的Elasticsearch进程会执行实际的查询操作。
查询操作通常分为两个阶段
Phase在这一阶段中Elasticsearch会检查查询条件并从倒排索引中查找匹配的文档ID。
由于Elasticsearch使用的是倒排索引它可以非常高效地进行文本搜索。
Phase一旦文档ID被查找到Elasticsearch就会进入取回阶段此时会根据需要从存储中检索文档的完整内容。
如果查询包括了排序或聚合还可能需要在这个阶段对结果进行进一步处理。
如果查询请求指定了排序条件各个节点需要在本地对查询结果进行排序。
每个节点只返回最顶端的结果例如如果请求指定了size:
10则每个节点只返回排名前10的结果给协调节点这样可以避免回传大量不必要的数据提高效率。
协调节点收到所有涉及的节点返回的结果后会进行最终的排序和汇总。
然后协调节点会将最终的查询结果返回给客户端。
Elasticsearch还有一个查询缓存机制它会缓存经常被执行的过滤器查询的结果。
这能够使得相同或相似的后续查询更快地返回结果。
查询复杂性可以根据查询中涉及的文档数量、查询类型如文本搜索、范围查询等、是否有聚合操作等因素有很大的不同。
尽管Elasticsearch旨在使查询尽可能快速但一些复杂的查询还是会因为涉及大量的数据处理和传输而耗费较多时间。
对于真正的性能要求通常需要结合监控、索引优化、查询优化等一系列措施来确保系统的查询响应效率。
分析(analysis)是在文档被发送并加入倒排索引之前,Elasticsearch在其主体上进行的操作。
在文档被加入索引之前,Elasticsearch让每个被分析字段经过一系列的处理步骤。
字符过滤使用字符过滤器转变字符。
文本切分为分词将文本切分为单个或多个分词。
分词过滤使用分词过滤器转变每个分词。
分词索引将这些分词存储到索引中。
使用到ElasticSearch的字符过滤器、分词器、分词过滤器和分词索引它们便组成了分析器analyzer。
字符过滤器将特定的字符序列转变为其他的字符序列。
可以用于将HTML从文本中剥离或者是将任意数量的字符转化为其他字符也许是将i
分词器是从文本片段生成的可能会产生任意数量甚至是0的分词(token)。
例如在英文中一个通用的分词是标准分词器它根据空格、换行和破折号等其他字符将文本分割为分词。
technologies分解为分词share、your、experience、with、NoSql、and、big、data
分词器文本块被转换为分词将会对每个分词运用分词过滤器tokenfilter。
分词过滤器可以将一个分词作为输人然后根据需要进行修改添加或者是删除。
最为有用的和常用的分词过滤器是小写分词过滤器它将输人的分词变为小写确保在搜索词条nosql的时候可以发现关于“nosql的聚会。
分词可以经过多于1个的分词过滤器每个过滤器对分词进行不同的操作将数据塑造为最佳的形式便于之后的索引。
当经历了零个或者多个分词过滤器它们将被发送到Lucene进行文档的索引。
这些分词组成了倒排索引。
当创建索引的时候为特定的索引进行设置。
在Elasticsearch的配置文件中设置全局的分析器。
通常来说出于灵活性的考虑在创建索引或者是配置映射的时候指定分析器是更简单的。
设置分析器名称myCustomAnalyzer:{#定制化类型token:custom,#
字符过滤器char_filter:[myCustomCharFilter],#
分词器tokenizer:myCustomTokenizer,#
分词过滤器filter:[myCustomFilter1,myCustomFilter2],}}},#
定义字符过滤器char_filter:{myCustomCharFilter:{#
类型为映射把字符转成其他字符type:mapping,mappings:[phf,uyou]}}#
分词器tokenizer:{myCustomTokenizer:{#
分词过滤器filter:{myCustomFilter1:{#
小写分词过滤器type:lowercase},myCustomFilter2:{#
mappings:{properties:{orderTile:{type:text,#指定分词器es内置有多种analyzer:whitespace、standard、simple、stopanalyzer:
}如果想一个字段不被任何分析处理需要增加not_analyzed7.x之前
{mappings:{properties:{orderTile:{type:keyword}}}
通常情况下可以同时搜索字段分析后的文本和原始、未经分析的文本是非常有用的。
index:analysis:analyer:myCustomAnalyzer:type:
[customFilter1,customFilter2]char_filter:customCharFilter:type:
tokenizer:customTokenizer:type:
letterfilter:customFilter1:type:
一个分析器包括一个可选的字符过滤器、一个单个分词器、0个或多个分词过滤器。
它综合了对大多欧洲语言来说合理的默认模块包括标准分词器、标准分词过滤器、小写转换分词过滤器和停用词分词过滤器。
简单分析器simpleanalyzer就是那么简单它只使用了小写转换分词器这意味着在非字母处进行分词并将分词自动转变为小写。
简单分析器对于亚洲语言来说效果不佳因为亚洲语言不是根据空白来分词所以请仅仅针对欧洲语言使用它。
空白分析器whitespaceanalyzer什么事情都不做只是根据空白将文本切分为若干分词非常简单
停用词分析器analyzer和简单分析器的行为很相像只是在分词流中额外地过滤了停用词。
关键词分析器keywordanalyzer将整个字段当作一个单独的分词。
请记住最好是将index设置指定为notanalyzed7.x只有改为
analyzer允许你指定一个分词切分的模式。
但是由于可能无论如何都要指定模式通常更有意义的做法是使用定制分析器组合现有的模式分词器和所需的分词过滤器。
是一个基于正则表达式的分词器(tokenizer)它允许用户使用正则表达式来定义如何将文本拆分为词(token)。
下面举一个
地址、日期时间、请求方法、请求资源、响应状态码和响应大小等信息感兴趣。
下面是如何在
pattern它用于匹配一个或多个空白字符(\\s)以此来分割日志的每一部分。
我们还设置了
来保持文本的原始大小写因为对于日志分析区分大小写可能是重要的。
请注意为简单起见这个例子只用了空格来分割日志条目并没有详细地去解析每个字段如
地址等。
在实际应用中你可能需要一个更复杂的正则表达式来准确提取和分析每个感兴趣的部分。
此外对于复杂的日志分析可能需要定义更详细的字段映射来区分不同的日志组成部分如分别用不同字段存储
雪球分析器snowbal1analyzer除了使用标准的分词器和分词过滤器和标准分析器一样也使用了小写分词过滤器和停用词过滤器。
它还使用了雪球词干器对文本进行词干提取。
标准分词器standardtokenizer是一个基于语法的分词器对于大多数欧洲语言来说是不错的。
它还处理了Unicode文本的切分不过分词默认的最大长度是255。
它也移除了逗号和句号这样的标点符号。
tokenizer是一种简单的分词器将整个文本作为单个的分词提供给分词过滤器。
只想应用分词过滤器而不做任何分词操作时它可能非常有用。
tokenizer根据非字母的符号将文本切分成分词。
例如对于句子share
technologies”分词是share、your、experience、with、NoSql、big、data、technologies因为、空格和句号都不是字母
tokenizer结合了常规的字母分词器和小写分词过滤器如你所想它将整个分词转化为小写的行为。
通过一个单独的分词器来实现的主要原因是一次进行两项操作会获得更好的性能。
tokenizer通过空白来分隔不同的分词空白包括空格、制表符、换行等。
请注意这种分词器不会删除任何标点符号。
tokenizer允许指定一个任意的模式将文本切分为分词。
被指定的模式应该匹配间隔符号。
在处理英语单词的时候标准分词器是非常好的选择。
但是当下存在不少以网站地址和电子邮件地址结束的文本。
标准分析器可能在你未注意的地方对其进行了切分。
}分词结果domain、is、https://www.baidu.com?siPhone
tokenizer允许以特定的方式索引文件系统的路径这样在搜索时共享同样路径的文件将被作为结果返回。
例如假设有一个文件名想要索引看上去是这样的/usr/local/var/log/elasticsearch.logo路径层次分词器将其切分为
/usr/local/var/log/elasticsearch.logo
/usr/local/var/log,start_offset
/usr/local/var/log/elasticsearch.logo,start_offset
filter进行了什么复杂的计算实际上它什么事情也没做在更老版本的Lucene中它用于去除单词结尾的s”字符还有不必要的句点字符但是现在这些都被其他的分词过滤器和分词器处理掉了。
{analysis:{analyzer:{customLowercaseAnalyzer:{type:custom,tokenizer:standard,filter:[lowercase,stop]}}}}},mappings:
customLowercaseAnalyzer,fields:
}由于使用了lowercase分词过滤器Jay会被转换成jay通过Jay或者jay都可以进行搜索。
filter将长度超出最短和最长限制范围的单词过滤掉。
举个例子如果将min设置为2并将max设置为5任何小于2个字符和任何大于5个字符的分词将会被移除。
}max参数表示最大分词长度默认为Integer.MAX_VALUE就是2147483647。
{filter:{customLengthFilter:{type:length,min:2,max:5}},analyzer:
[customLengthFilter]}}}}},mappings:
filter将停用词从分词流中移除。
对于英文而言这意味着停用词列表中的所有分词都将每会被完全移除。
可以添加指定一个待移除单词的列表。
{filter:{customStopFilter:{type:stop,stopwords:[am]}},analyzer:{customStopAnalyzer:{type:custom,tokenizer:standard,filter:[customStopFilter]}}}}},mappings:
}除stopwords参数还也可以使用stopwords_path参数指定停用词文件。
{analysis:{analyzer:{customReverseAnalyzer:{type:custom,tokenizer:standard,filter:[reverse]}}}}},mappings:
filter只保留唯一的分词它保留第一个匹配分词的元数据而将其后出现的重复删除
{analysis:{analyzer:{customUniqueAnalyzer:{type:custom,tokenizer:standard,filter:[unique]}}}}},mappings:
filter在分词流中的同样位移处使用关键词的同义词取代原分词。
[{type:synonym,synonyms:[automobilecar]}
{analysis:{filter:{customSynonymFilter:{type:synonym,synonyms:[automobilecar]}},analyzer:{customSynonymAnalyzer:{type:custom,tokenizer:standard,filter:[customSynonymFilter]}}}}},mappings:
automobile,age:20,birth:2002-01-01
N元语法ngram是Elasticsearch中更为独特的分词方式。
N元语法是将一个单词切分为多个子单词。
1元语法1-ngram分词“automobile”结果时a、u、t、o、m、o、b、i、l、e。
2元语法2-ngram分词“automobile”结果时au、ut、to、om、mo、ob、bi、il、le。
[{type:ngram,min_gram:1,max_gram:1}
[{type:ngram,min_gram:2,max_gram:2}
如果min_gram1、max_gram2automobile分割成a、u、t、o、m、o、b、i、l、e、au、ut、to、om、mo、ob、bi、il、le。
{analysis:{filter:{customNgramFilter:{type:ngram,min_gram:1,max_gram:1}},analyzer:{customNgramAnalyzer:{type:custom,tokenizer:standard,filter:[customNgramFilter]}}}}},mappings:
{name:automobile,age:20,birth:2002-01-01
侧边N元语法是普通N元语法切分的一种变体仅仅从左边的边缘开始构建N元语法。
例如“automobile”从左边“a”的边缘开始截取N个词。
[{type:edge_ngram,min_gram:2,max_gram:4}
}输出au、aut、auto此时已经注意到超过max_ngram部分不会分割意味着不能搜索。
{analysis:{filter:{customEdgeNgramFilter:{type:edge_ngram,min_gram:2,max_gram:4}},analyzer:{customEdgeNgramAnalyzer:{type:custom,tokenizer:standard,filter:[customEdgeNgramFilter]}}}}},mappings:
customEdgeNgramAnalyzer,fields:
{name:automobile,age:20,birth:2002-01-01
滑动窗口分词过滤器shingles和N元语法以及侧边N元语法沿用了同样的方式。
滑动窗口分词过滤器基本上是分词级别的N元语法而不是字符级别的N元语法。
[{type:shingle,min_shingle_size:2,max_shingle_size:2}
{analysis:{filter:{customShingleFilter:{type:shingle,min_shingle_size:2,max_shingle_size:2}},analyzer:{customShingleAnalyzer:{type:custom,tokenizer:standard,filter:[customShingleFilter]}}}}},mappings:
automobile,age:20,birth:2002-01-01
提取词干是将单词缩减到基本或词根的形式。
在搜索的时候这种处理是非常方便的因为这意味着用户可以匹配单词的复数以及有同样词根的单词因此名字称为“提取词干。
提取词干的算法有snowball过滤器、porter_stem过滤器、kstem过滤器。
他们表现基本一致不过在提取词干有多激进的方面有一些细微的差别。
这里的“激进是指相对于不激进的词干提取器更为激进的词干提取器会砍掉单词更多的部分。
算法administrationsadministratorsAdministratesnowballadministradministrAdministrporter_stemadministradministrAdministrkstemadministrationadministratorAdministrate
有的时候算法词干提取会以一种奇怪的方式来提取单词的词干因为它们并不理解基层的语言。
正因为此存在更为精确的方式来提取词干那就是使用单词字典。
在Elasticsearch中可以使用hunspell分词过滤器结合一个字典来处理词干。
基于此词干提取的质量就和所用字典的质量是直接相关的。
词干提取器只能处理字典里存在的单词。
当创建一个hunspell分析器的时候字典文件应该是在名为hunspell的和elasticsearch.yml处于同一个中每种语言的字典是一个以其关联地区命名的目录。
当跟踪信息是如何在Elasticsearch索引中存储的时候使用分析API来测试分析的过程是十分有用的。
API允许你向Elasticsearch发送任何文本指定所使用的分析器、分词器或者分词过滤器然后获取分析后的分词。
使用标准分析分析了文本share
}分析API中最为重要的输出是token键。
输出是一组这样映射的列表代表了处理后的分词实际上这些分词将会被写人到索引中。
通过_termvectors查询已经索引的文档orderTitle字段的分词词条操作消耗比较大
/mall_order_test/_doc/sZNUsIwBSLqPVZ3tlB7z/_termvectors?prettytrue
的快照和恢复功能允许用户备份集群中的数据并在需要时恢复。
快照可以是手动触发的也可以通过自动化机制进行定期备份。
以下是有关如何配置和使用
在配置自动快照之前首先需要定义一个快照仓库。
Elasticsearch
/_snapshot/my_backup_repository
/mount/backups/my_backup,compress:
my_backup_repository并指定了备份文件的位置和压缩选项。
Management功能你可以定期自动创建快照。
你需要创建一个快照生命周期策略如下所示
/_snapshot/my_backup_repository/my-snap-2023.04.06_01:30/_restore
index_1,index_2,ignore_unavailable:
/_snapshot/my_backup_repository/my-snap-2023.04.06_01:30或者获取当前生命周期策略的状态
自动快照和恢复机制的基本步骤。
请注意一切配置和操作都要考虑到你的实际环境和需求。
Elasticsearch
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback