基本概念:
索引Index
es吧数据放到一个或者多个索引中,如果用关系型数据库模型对比,索引的地位与数据库实例(db)相当。索引存放和读取的基本单元是文档(document)。es内部使用的是apache lucene实现的索引中数据的读写。(es被视为单独的一个索引,在lucene中不止一个,因为分布式中,es会用到分区shards和备份replicas机制讲一个索引存储多份)。
文档document
在es中,文档主要是存储实体。所有的es应用需求最后都需要统一建成一个检索模型:检索相关文档。
文档由一个或多个域,每个域field由一个域名或多个值组成(有多个值的称为多值域)。
在es中每个文档都可能会有不同的域field集合;也就是说文档是没有固定的模式和同意的结构的。文档之间保持的相似性即可。
在客户端角度来看,文档就是一个json对象。
参数映射 所有的文档在存储之前都必须分析(analyze)流程,用户可以配置输入文本分解成token的方式:哪些token呗滤掉;或者其它的处理流程,比如去除html标签。
文档类型(type)
每个文档在es中都必须设定它的类型。文档类型使得同一个索引中在存储结构不同文档时,只需根据文档类型就可以找到对应的参数映射信息,方便文档的存取。
节点Node
单独es服务器实例成为一个节点。
集群Cluster
集群能够存储超出单机容量的信息。由于目前单点就可以满足咱们的需求,就不详细介绍了。
索引副本Replica
通过索引分片机制可以想es集群中导入超过单机容量的数据,客户端操作任意一个节点接口实现对集群数据的读写。(不做详细解释了)
时间之门gateway
在运行的过程中,es会收到集群的状态,索引的参数等信息。这些呗存储在gateway中。
es背后核心理念:
es是构造极少数的几个极少数的概念之上的。
开箱急用。
天生集群。
自动容错。
扩展性强。
es工作原理:
启动过程:
当es节点启动后,会利用 多播(multicast)或单播(别问我什么是单播,多播,没必要纠结这些)寻找病简历链接。如图:
在集群中,一个节点呗选举成主节点。这个节点扶着管理集群的状态,当集群的拓展结构改变时把索引分区分派到相对性的节点上。
在用户角度看节点在es中并没有占据主要位置,这与其它系统是不同的(数据库系统)。实际上用户并不需要知道哪个节点是主节点;所有的操作需求可以发到任意节点,es内部完成这些工作。必要时任意节点都可以并发的把查询自居分发到其他节点,然后合并各个节点返回的查询结果。最后返回用户一个完整结果集。所有的这些工作不需要经过主节点转发(节点之前通过p2p的方式通讯)。
在必要时,会进行恢复工作。这时主节点会去检查哪些分片可用,决定用哪些分片。处理完后,集群转入黄色状态。
这意味着集群可以处理搜索请求了。但是还没有火力全开(这主要是由于所有的主索引分区都已经分配好了,但是索引副本还没有)。接下来就是找到复制好的分区,病设置成索引副本。当一个分区数量太少时,主节点会界定将缺少的分区放到哪个节点中,并且依照主分区创建副本。所有工作完成后,集群就会变成绿色状态(标示所有的主分区的索引副本都已经分配完成了)。
探测是吧节点
在正常工作时,主节点会监控所有节点,查看各个节点是否工作正常。如果在指定的时间里,节点无法访问,就呗视为出现故障了,接下来错误处理程序就会启动。集群需要均衡——由于该节点出现故障,分配到该节点的索引分片丢失。其实节点上相应的分区就会吧工作接管过来的这个过程可以通过配置满足用户需求。
由于只是展示es的工作原理,就以下图三个节点的集群为例。集群中有一个主节点和两个数据节点。主节点想其他节点发送ping命令后等待回应。如果得到回应(实际上可能得不到恢复ping命令个数,取决于用户配置),改节点就会被移出集群。
与es进行通讯
归根到底,最重要的是如何往es中添加数据以及如何查询数据。es提供了api,这些api都是基于rest风格。而且这些api非常容易与其他能够处理http的系统进行集群。
es为数据应该伴随在url中,或作为请求主体requst body。以一种json格式的文档发送给服务器。
es内部,节点之间通讯解释用的先关javaapi。
重点来咯
索引数据
es提供了4中索引数据的方法。最简单的就是索引api。通过它可以将文档添加到指定的索引中去。比如curl工具。我可以通过如下命令创建一个新的文档
第2、3中方法,可以通过bulk API和UDP API批量添加文档。通常的bulk API采用HTTP协议,UDP bulk api采用非连接的数据包协议。UDP协议传输速度更快,但可靠性差点、最后一种是通过rivier插件。river运行在es集群的节点上,能够从外部系统中获取数据。
有一点需要注意,索引数据的曹组只会发生在主分区上,而不会发生在分区副本上。如果索引数据的请求发送到节点上没有合适的分片或者分片副本,那么请求就会被转发到含有主分区的节点。
数据查询
查询api在es中有着很大的比重。通过query DSL(基于json,用于构造复杂的语言)
使用类型查询:简单关键词、短语、区间、布尔、模糊、跨度、通配符、地理位置等查询方式。
通过组合简单查询构造复杂的查询。
过滤文档,去除不符合标准的文档而且不影响打分排序。
查找给定文档的相似文档。
查找给定短语的搜索建议和查询短语修正。
通过faceting构建动态的导航和数据统计。
使用prospective search而且找到匹配写定文档的查询语句(prospective search一种推送方式。用户的查询语句存储在索引中,如果新的文档添加到索引中,就把文档关联到匹配的查询语句中。这种适合于新闻,博客等定时更新的场景)。
关于数据查询,其核心在于查询过程不是一个简单、单一的流程。通过这个过程分为两个阶段:查询阶段和结果汇总阶段。在查询分发阶段,会从各个分支中查询数据;在结果汇总阶段,会从各个分群上查询到结果进行合并,排序等其他处理过程,然后返回给用户。
用户可以通过指定搜索类型来控制查询的分发和汇总过程。
索引参数设置
es索引参数会自动配置
文档结构以及域类型会自动识别。当然es也允许用户自定修改默认配置。
比如,自行配置很多参数,比如通过mapping配置索引中的文档结构,设置分区shard和副本replica的个数,设置文本组件……
集群管理和监控
通过管理和监控部分的api,用户可以更改集群设置。比如调整节点发现机制或更改索引的分片策略。用户可以查看集群状态信息,或者每个节点和索引和统计信息。集群监控的api非常广泛。
强大的用户查询语言DSL
if/idf打分公式
这个就是打分公式的真面目。如果只是为了调整查询语句之间的关联关系,用户不必去理解它的原理。但只搜啊要知道它是如何工作的。
lucene概念上的打分公式
上面展示了布尔信息检索和向量空间信息检索模型的组合。(这个暂时忽略)
可以了解更多东西可以去这里
从es的角度看打分排序
最重要的是利用lucene构建起来的es允许用户修改默认的打分算法。但es不仅仅是lucene的简单封装,因为es中,文档排序并非完全依赖apache lucene的打分算法。es实现了多种不同的查询类型,这些查询类型可以完全依赖与文档的打分计算方式,es允许通过脚本定制文档的打分方式。
查询重写机制
如果你曾经使用过很多不同的查询类型,比如前缀查询和通配符查询,从本质上,任何的查询都可以视为对多个关键词查询。查询重写(query rewrite),es对用户查询进行了重写,这样做为了保证性能。重写过程是吧lucene角度认为原始的、开销大的查询对象转变成一系列开销小的查询对象的一个过程。
前缀查询:
例如:
我是知道所有字符以j开头的文档。这个需求非常简单,在client索引上运行
查询结果的重打分
有些场景对查询语句的结果文档进行重新打分是很有必要的。重新打分的原因可能各有相同。
其中一个原因可能是处于性能考虑,比如对整个有序的结果集进行重排序开销会很大,通常就会只对结果集进行重排序。
理解重打分
在es中,重打分是一个对限定数目的查询结果进行再次打分的一个过程。这意味着es会根据新的打分规则对结果的前n个文档重新进行一次排序
例
rescore query的结构:
重打分的参数
在查询语句的rescore对象中,用户可以添加如下参数
window_size提供了与N个文档的相关信息。用于执行分片上用于重打分的文档个数
query_weight默认1;原查询的打分会先乘以query_weight,然后与rescore的得分相加。
rescore_query_weight默认1,rescore的打分会先乘该值,在与原查询的得分相加。
rescore_mode默认tatal;在es0.90.0中引入用来指定重打分文档的打分方式。可选值:total,max,avg和multiply。
total:最终得分为原查询得分和rescore得分的和;
max,最终得分为原查询得分和rescore得分的最大值;
min,最终得分为原查询得分和rescore得分的最小值;
avg,最终得分为原查询得分和rescore得分的平均值;
multiply,两种查询的得分相乘。
例如设置recore_mode参数值为total,文档最终得分是
查询结果的排序
当给es发送查询命令时,返回的文档集合默认会按照计算出来的文档打分排序。这个通常是
用户希望的:结果集中的第一个文档就是查询命令想要的文档。然而,有事我们希望改变这种排序
。
update API
当往索引中添加新的文档到索引中时,底层的lucene工具包会分析每个域,生成token流
token流过滤后得到倒排索引。在这个过程中输入文版中一系诶不必要的信息会丢掉。
这些不必要的信息可能是一些特殊词的位置,一些停用的词或用同义词代替的词,或者词尾
。这也是为什么无法对lucene中的文档进行修改,每次修改一个文档时,必须吧文档所有域 添加到索引中。es通过_source这个代理域来存储或检索文档中的真是数据。
当我们想更新文档时,es会把数据放到_souce域中,然后做出修改,最后吧更新后的文档
添加到索引中。让然前提是_source域的这项特性必须生效。文档更新命令只能更新一个文档
查询命令的文档更新还没有出来。
更新:
使用update API创建或删除文档
update API不仅可以修改某个域,同时也能操作整个文档。
upsert特性使得在定位到一个不存在的文档是,它会被创建爱你出来:
如果文档存在,该命令将重置year域中的值;否则会被创建。新的文档包含upset中定义的titile域。当然,上面的命令还有可以使用脚本:
update还允许用户选择性的删除整个文档。
filters优化查询:
es支持多种类型的查询,但是查询那个匹配成功,哪个应该呈现给用户,查询并不是唯一的。es查询dsl允许用户使用绝大数查询都会有各自的标示。
过滤器(filters)和缓存
ES提供了特殊的缓存,filter cache来存储filters得到的结果集。此外,存储filters不需要太多的内存(它只保留一种信息,即哪些文档与filter相匹配),同时它可以与其他查询复用,极大的提高查询的性能。
例:
该命令查询得到满足如下条件的文档:name域值为joe同时值为1981。
用上面命令格式构建查询,查询对象会将所有的条件绑定到一起存储到缓存中;因此如果我们查询人名相同但出生年份不同的运动员,es无法重用上面查询命令中的任何信息。
因此,我们需要优化下查询。由于1千个人可能有一千个人名,所以人名不太适合缓存起来;但是年份比较适合。因此我们引入一个不同的查询命令,将一个简单的query与一个filter结合起来。
我们使用一个filtered类型的查询对象,查询对象将query元素与filter元素包含进去。第一次运行查询命令后,es会吧filter缓存起来,如果有查询用到一样的filter,就会直接用缓存。
并非所有的filters都会被默认缓存起来
实际上es不会缓存所有的filters。这是因为部分filters会用到域数据缓存(field date cache)。该缓存一般用于按域值排序和faceting操作场景中。默认情况,如下filters不会呗缓存:
尽管最后三种不会用到域缓存,它们主要用于控制filters,因此不会被缓存,但是它们控制的filters在用时都已经缓存好了。
更改es缓存行为
es允许用户通过_cache和_cache_key属性自行开启或关闭filters功能。
假设我们将关键词过滤器结果缓存起来,病给缓存项的key取名为year_1981_cache:
也可以用下面命令关闭过滤器缓存:
为了获取更多的控制权,我们需要给缓存项的key取名。
比如,有些查询复用机不多,我们希望指定定时清理这些查询的缓存。如果不指定_cache_key。就只能清除整个过滤器缓存filter cacahe;反之,只需执行如下命令:
关键词查找过滤器
缓存和标准的查询并不是全部内容。
索引定义了id信息,名字,用户购书的id列表。
一些测试数据:
假设我们希望展示某个用户购买的所有数据,以id为1的user为例。
term filter的工作原理
一个普通的es查询命令。它只是一个过滤查询,包含一个全量查询和一个terms过滤器。
只是查询命令中,terms过滤器使用了一种不同的技巧——不明确指定某些term值,而是从索引中动态加载。
过滤器基于id域,因为我们只需要id域就整合其它所有的属性。
index属性指明了加载terms的索引源。
type属性告诉es我们的目标文档类型。
id属性指明我们在执行索引文档的books域。
path属性告诉es应该从哪个域中加载term。
terms lookup filter的缓存设置
filters和scope在es faceting的应用
faceting的结果只基于查询结果。如使用filters,那filters不会对facet用来的统计计算文档产生影响。
scope,可以扩展facet统计计算的文档范围。