产生的背景
Shay Banon为了给他的妻子做一个食谱搜索引擎,他使用Lucene的一个早期版本,由于接使用Lucene很困难,因此使用java做了一个抽象层,这样Java开发者可以简单的给他们的程序添加搜索功能。他发布了他的第一个开源项目Compass,为了应对高性能、实时、分布式搜索引擎的场景需求,他决定重写Compass,把它变为一个独立的服务,这样开发者可以通过程序与它提供的简单的RESTful API进行通信,并取名Elasticsearch。
有什么作用
一个分布式的实时文档存储,每个字段可以被索引与搜索
一个分布式实时分析搜索引擎
能胜任上百个服务节点的扩展,并支持__PB级别的结构化或者非结构化数据
集群内的原理
ElasticSearch的宗旨是随时可用和按需扩容。而扩容可以通过购买性能更强大或者数量更多的服务器来实现。
集群作用
一个实例节点可以构成一个集群,无论请求那个节点,都能够从其他节点收集回数据。
集群健康
green所有的主分片和副本分片都正常运行。
yellow所有的主分片都正常运行,但不是所有的副本分片都正常运行。
red 有主分片没能正常运行。
索引的作用
索引是Elasticsearch存储数据的地方,索引实际上是指向一个或者多个物理分片的逻辑命名空间。
故障转移
当集群中只有一个节点在运行时,意味着会有一个单点故障问题——没有冗余。 我们只需再启动一个节点即可防止数据丢失。
水平扩容
在运行中的集群上是可以动态调整副本分片数目的,我们可以按需伸缩集群。
应对故障
如果单个节点发生故障,其他节点存储了当前节点上分片的完整副本。当故障节点恢复,集群可以将缺失的分片重新分配。
分布式文档存储原理
路由一个文档到一个分片中
分片计算公式:shard = hash(routing) % number_of_primary_shards
routing是一个可变值,默认是文档的_id
number_of_primary_shards主分片的数量
所有的文档API(get、index、delete、bulk、update、mget)都接受一个叫做routing
的路由参数 ,通过这个参数我们可以自定义文档到分片的映射。
主分片和副本分片如何交互
每个分片都知道文档具体位置,通过主节点协调分片。
新建索引和删除文档(写操作)
通过文件定位到主分片,主分片操作成功之后,发起命令操作副本分片。
consistency:一致性
即使仅仅是在试图执行一个_写_操作之前,主分片都会要求 必须要有规定数量(quorum)。公式为:int( (primary + number_of_replicas) / 2 ) + 1
timeout: 如果分片数不够,默认情况下等待1分钟结束操作,可以通过设置这个参数,使它更早结束。
取回一个文档
可以通过任意分片检索文件,协调节点通过轮询的方式做分片之间的负载均衡。
局部更新文档
主分片检索文档之后,通过修改_source中JSON,如果文档修改中,通过重试机制等待,超过retry_on_conflict次后结束,知道所有副本分片操作成功之后,主分片返回成功消息。
多文档模式
协调节点将单个文档请求分解成每个分片的多文档请求,处理完毕之后,将响应收集整理并返回给客户端。
Elasticsearch可以直接读取被网络缓冲区接收的原始数据。 协调节点使用换行符字符来识别和解析小的action/metadata行来决定哪个分片应该处理每个请求。
执行分布式检索原理
一个CRUD操作只对单个文档进行处理,文档的唯一性由_index, _type,和routing values(通常默认是该文档的_id)的组合来确定。
查询阶段
优先队列:一个优先队列仅仅是一个存有top-n匹配文档的有序列表。优先队列的大小取决于分页参数form 和size。
请求发送到节点时,会创建一个大小为form+size的空优先队列。节点将请求转发到索引的每个主分片或副本分片中 ,每个分片将查询结果添加到本地优先队列中,每个分片返回各自优先队列中所有文档的 ID 和排序值给协调节点,协调节点合并排序全局结果返回。
取回阶段
协调节点辨别出哪些文档需要被取回并向相关的分片提交多个
GET
请求。每个分片加载并丰富文档,如果有需要的话,接着返回文档给协调节点。
一旦所有的文档都被取回了,协调节点返回结果给客户端。
深分页(Deep Pagination):
取决于你的文档的大小,分片的数量和你使用的硬件,给 10,000 到 50,000 的结果文档深分页( 1,000 到 5,000 页)是完全可行的。但是使用足够大的form 值,排序过程可能会变得非常沉重,使用大量的CPU、内存和带宽。
搜索选项
有几个 查询参数可以影响搜索过程?
偏好:偏好这个参数preference允许 用来控制由哪些分片或节点来处理搜索请求。 Bouncing Results:想象一下有两个文档有同样值的时间戳字段,搜索结果用timestamp字段来排序。 由于搜索请求是在所有有效的分片副本间轮询的,那就有可能发生主分片处理请求时,这两个文档是一种顺序, 而副本分片处理请求时又是另一种顺序。
超时问题:timeout允许分片处理的最大时间。
路由: 通过路由直接定位分片位置。
搜索类型:可以从所有相关分片获取词频来计算全局词频。
游标查询scroll
有效地执行大批量的文档查询,而又不用付出深度分页那种代价。游标查询允许我们
先做查询初始化,然后再批量地拉取结果。
分片内部原理
倒排索引
一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表。
使文本可被搜索
全文检索会为整个文档集合建立一个很大的倒排索引并将其写入到磁盘。 一旦新的索引就绪,旧的就会被其替换,这样最近的变化便可以被检索到
不变性
不需要锁。如果你从来不更新索引,你就不需要担心多进程同时修改数据的问题。
一旦索引被读入内核的文件系统缓存,便会留在哪里,由于其不变性。只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘。这提供了很大的性能提升。
其它缓存(像filter缓存),在索引的生命周期内始终有效。它们不需要在每次数据改变时被重建,因为数据不会变化。
写入单个大的倒排索引允许数据被压缩,减少磁盘I/O和 需要被缓存到内存的索引的使用量。
动态更新索引
通过增加新的补充索引来反映新近的修改,而不是直接重写整个倒排索引。每一个倒排索引都会被轮流查询到—从最早的开始—查询完后再对结果进行合并。
新文档收集到内存索引缓存,一个新的段— 一个追加的倒排索引—被写入磁盘。一个新的包含新段名字的提交点被写入磁盘。磁盘进行同步—所有在文件系统缓存中等待的写入都刷新到磁盘,以确保它们被写入物理文件。新阶段开发让文件可以被检索,内存清空等待新的内容。
近实时搜索
新段会被先写入到文件系统缓存—这一步代价会比较低,稍后再被刷新到磁盘—这一步代价比较高。不过只要文件已经在缓存中, 就可以像其它文件一样被打开和读取了。
持久化变更
translog事务日志,在每一次对节点进行操作时均进行了日志记录。一个文件被索引后就会放入内存缓冲并追加事务日志,事务日志一直存在,当fsync执行flush命令至磁盘,事务日志删除。
段合并
当检索时,刷新会创建新的段并打开供新的搜索使用,合并一小部分大小小同的段,不会中断检索和搜索。新的段flush到磁盘,新的段打开提供检索,老的段删除。
近似匹配
理解分词之间的关系是一个复杂的难题,我们也无法通过换一种查询方式去解决。但我们至少可以通过出现在彼此附近或者仅仅是彼此相邻的分词来判断一些似乎相关的分词。
短语匹配
一个被认定为和短语 quick brown fox匹配的文档:
quick、brown和fox需要全部出现在域中。
brown的位置应该比quick的位置大1。
fox的位置应该比quick的位置大2。
混合起来
我们能够通过使用slop参数将灵活度引入短语匹配中,slop参数告诉match_phrase查询词条相隔多远时仍然能将文档视为匹配 。
多值字段
position_increment_gap的简单的解决方案,首先删除映射groups以及这个类型内的所有文档,然后创建一个有正确值的新的映射groups。
越近越好
鉴于一个短语查询仅仅排除了不包含确切查询短语的文档,而邻近查询—一个slop大于0—的短语查询将查询词条的邻近度考虑到最终相关度_score中。 通过设置一个像50或者100这样的高slop值,你能够排除单词距离太远的文档, 但是也给予了那些单词临近的的文档更高的分数。
使用邻进度提高相关度
我们可以将一个简单的match查询作为一个must子句。 这个查询将决定哪些文档需要被包含到结果集中。 我们可以用minimum_should_match参数去除长尾。 然后我们可以以should
子句的形式添加更多特定查询。 每一个匹配成功的都会增加匹配文档的相关度。
性能优化方式
短语查询和邻近查询都比简单的query查询代价更高。 一个match查询仅仅是看词条是否存在于倒排索引中,而一个match_phrase查询是必须计算并比较多个可能重复词项的位置。Lucene nightly benchmarks表明一个简单的term查询比一个短语查询大约快10倍,比邻近查询(有slop的短语 查询)大约快20倍。当然,这个代价指的是在搜索时而不是索引时。
寻找相关词
Shingles需要在索引时作为分析过程的一部分被创建。我们可以将unigrams和bigrams都索引到单个字段中, 但将它们分开保存在能被独立查询的字段会更清晰。unigrams字段将构成我们搜索的基础部分,而bigrams字段用来提高相关度。
shingles不仅比短语查询更灵活,而且性能也更好。shingles查询跟一个简单的match查询一样高效,而不用每次搜索花费短语查询的代价。只是在索引期间因为更多词项需要被索引付出一些小的代价, 这也意味着有shingles的字段会占用更多的磁盘空间。 然而,大多数应用写入一次而读取多次,所以在索引期间优化我们的查询速度是有意义的。
作者:逆风_探架构
共同学习,写下你的评论
评论加载中...
作者其他优质文章