时光小说 https://wap.youxs.org/
这篇文章已经是第四篇了,前面很多都只是讲了基础的使用,没有讲到内层的原理,所以这里就要补一下原理知识了。
先回顾一下我们前面学过了什么,再想想我们漏了什么。
第一篇我们认识了ElasticSearch,大概知道了ElasticSearch的作用--搜索,也了解了一些倒排索引和分词器的知识(需要补充),然后学习了如何搭建环境(需要补充一下关于基础的集群知识),然后讲了一些基础的ElasticSearch概念(重新讲述,加深了解)。
第二篇讲了索引和文档的CRUD,讲创建索引的时候,没有讲mapping,讲文档的时候没有讲文档的数据类型和元数据,这些都需要补充。
第三篇讲了文档的搜索,主要是语法方面的问题,但相关度分数是怎么计算出来的,我们并没有讲。
所以下面将对前面漏了的基础知识进行补充:
我们说了,ElasticSearch是分布式的,但我们之前只说了如何启动ElasticSearch,然后使用Kibana操作ElasticSearch,并没有讲ElasticSearch的集群式如何建立的,所以下面将会讲一下集群的知识。但如何管理集群会单独做成一篇来讲。
首先,每一个ElasticSearch服务端就是一个集群节点,当我们启动一个elasticsearch时,就相当于启动了一个集群节点。
那么,多个节点之间如何建立联系呢?当我们启动一个节点的时候,这个节点会自动创建一个集群,集群的名称默认为elasticsearch,当我们再次启动一个节点的时候,它首先会尝试寻找名称为elasticsearch的集群,然后加入其中,(所有的都是先尝试寻找,没有再自己创建)。【集群名称可以自行配置,下面讲】
当节点加入到集群中后,这个集群就算是建立起来了。
上面讲了集群名称可以自行配置,下面讲一下怎么配置。
ElasticSearch的配置文件是config/elasticsearch.yml,里面有以下几个配置项:
【左边是配置项,右边是值,修改配置也就是修改右边的值】
当集群建立后,我们可以使用命令来查看集群状态:
集群状态解析:
集群状态受当前的primary shared和replica shared的数量影响。
当每个索引的primary shared和replica shared都是active的时候,状态为green;【什么是active?shard是位于节点上的,一个shard被分配到了运行的节点上,那么此时就是active的,如果shard没有分配到节点上,那么就是inactive】
当每个索引的primary shared都是active的,但replica shared不完全是active的时候,状态为yellow;
当每个索引的primary shared不完全是active的时候,此时发生了数据丢失,状态为red。
为什么现在是yellow?
当我们第一次启动的时候,kibana会默认为我们创建一个名为kibana的索引,这个索引的primary shard和replica shard都为1,由于此时只有一个节点,所以会优先分配primary shard,而忽略replica shard(不要忘了基于备份的安全性的shard排斥考虑:主分片和副本分片不能位于同一个节点上),所以此时就符合了“每个索引的primary shared都是active的,但replica shared不全是active的”,所以此时是yellow.
你可以尝试启动多一个elasticsearch节点来调整状态。【当然,我觉得你学到这里了,此时replica shard的数量可能已经变了,所以这里只启动多一个可能已经不够了,具体的下面“分片的管理”讲】
上面讲了当启动了一个elasticsearch之后,这个节点先寻找集群,没有的话就自己创建一个,然后后续的其他节点也会加入这个节点,从而自动实现了自动集群化部署。
之前讲了一些分片的知识,但并没有具体用到,可能有些人已经忘了这个概念。这里重新讲一下。
文档的数据是存储到索引中的,而索引的数据是存储在分片上的,而分片是位于节点上的。
分片shard有主分片primary shard和复制分片replica shard两种,其中主分片是主要存储的分片,可以进行读写操作;副本分片是主分片的备份,可以进行读操作(不支持写操作)。
查询可以在主分片或副本分片上进行查询,这样可以提供查询效率。【但数据的修改只发生在主分片上。】
分片是面向索引的,分片上的数据属于同一个索引,在我们创建索引的时候,可以指定主分片和副本分片的数量,默认是5个主分片,5个副本分片。
索引的replica shard的数量可以修改,但primary shard的数量不可以修改。【一个Primary Shard可以有多个Replica Shard,默认创建是1个。】
为了保证数据的不丢失,通常来说Replica Shard不能与其对应的Primary Shard处于同一个节点中。【因为万一这个节点损坏了,那么存储在这个节点上的原数据(primary shard)和备份数据(replica shard)就全部丢失了】,但是可以和其他primary shard的replica shard放在一起
replica shard是primary shard的副本分片,负责承载一定的读请求,当primary shard都挂掉后,其中一个replica shard会变成primary shard来维持写功能。
分片是分配到节点上的,但它会默认地均衡分配。
所谓均衡分配,以情况举例:【priX代表主分片XX,repX代表副本分片XX】
1.索引有1个priA,1个repA,但当前只有一个节点A,那么priA分配到A节点;repA没有分配。优先分配主分片。
2.索引有1个priA,1个repA,当前有两个节点A和B,那么priA分配到A节点;repA分配到B节点(priA分配到B节点也有可能)。
3.索引当前有2个priA,4个repA,有3个节点,那么现在每台节点上有两个分片(但要考虑主分片不能与自己的副本分片同在一个节点上,在下面的“ 主副分片的排斥”中由一个例子)。
【当后续新增节点的时候,会自动再次重新均衡分配。】
考虑到备份的安全性,不应该让主分片和副本分片位于一个节点上,不然可能完全丢失数据。(比如你为了避免丢失钥匙,你给你家的门准备两条钥匙,你有一个钱包和一个背包,结果你把两条钥匙都放到钱包中,某天你钱包丢了,于是你回不了家了。。。所以你应该把钥匙分开放。)
对应的排斥情况就是:
1.索引有1个priA,1个repA,但当前只有一个节点A,那么priA分配到A节点;repA没有分配。
2.索引有1个priA,1个repA,当前有两个节点A和B,那么priA分配到A节点;repA分配到B节点(priA分配到B节点也有可能)。
【请注意,主分片与副本分片不能在一起,但副本分片和副本分片能存放在一起】
在上面的“分片的均衡分配”没有提到主副分片的排斥的问题,下面再举一个例子【下述的分配结果还有可能是其他的】:
既然提到了备份的安全性,那么就不得不提一下容错性了。节点是有可能宕机的,宕机后,那么这个节点的数据起码会暂时性的丢失,那么对于不同情况下,最多可以宕机多少个节点呢?下面举例:【pri = x代表主分片数量为X,rep是副本分片】
1.如果你只有一个节点,那么容错性为0,你不能宕机,宕机不完全意味着数据完全丢失,但暂停服务还是有的。
2.如果你有两个节点,pri = 2,rep = 2,那么此时分片分配应该是[P1R2, P2R1],此时容错性为一个,因为某个节点上有完整的两个分片的数据【此处一提,假设丢失了P2R1,那么R1R2中的R2会升级成primary shard来保持写功能】
3.后面的自己尝试算一下吧!要综合均衡分片和排斥性来考虑。
每一个document会存储到唯一的一个primary shared和其副本分片replica shard 中,我们是怎么根据ID来知道这个document存储在哪个分片的呢?
ElasticSearch会自动根据document的id值来进行计算,最终得出一个小于分片数量的数值,这个数值就是分片在ElasticSearch中的序号,最终得出应该存储到哪个分片上。
类似原理举例,假设我现在有4个主分片,那么我给它们标序号0,1,2,3。现在进来一个ID为15的数据,我经过一系列计算之后,15入参之后假设得到243这个数值,然后我们使用243来对4求余,余数是3,所以就把这个数据放在序号为3的分片上。
依据这个原理,存储数据的时候就知道把数据放在哪个分片上;读取数据的时候也知道从哪个分片上读取数据。
【稍微提一下,在ElasticSearch全文搜索完成之后,此时内部得到的是ID组成的数组,内部再会根据ID来查找数据】
上面说了,集群状态受当前的primary shared和replica shared的数量影响。
本节重新讲了主分片和副本分片的功能,主分片和副本分片都有存储数据的功能,一个索引的数据平分到多个主分片上,副本分片拷贝对应主分片的数据;然后讲了ElasticSearch对于分片的自动均衡分配,和主分片和副本分片不能存储在一起的问题。然后讲了ElasticSearch如何根据ID来判断数据存储在哪个分片上。最后讲了节点上分片的分配对于集群健康状态的影响。
索引的数据是存储在节点上的,当一个请求发到节点上的时候,可能这个节点上并没有这个索引的数据,那么这个时候就需要把请求转发给另一个节点了,这时候原本的节点就是一个“协调节点”。
一个索引存储在多个主分片和副本分片上,索引的数据会平均分配到每一个主分片中,然后每一个副本分片拷贝对应主分片的数据。
对于数据存储,数据是存储在分片上的,而分片位于节点上,在上面说了分片会均衡分配到每一个节点上,这样也保证了节点上的数据量是平均的。
除此之外,对于读取某一个主分片及其副本分片上的数据的时候,会使用轮询算法来将读请求平均分配(大概意思就是,假设现在对于主分片1有三个副本分片,那么总数为4,假设分别编号1、2、3、4,那么可能地,第一次请求交给了1,那么第二次请求要交给2,第三次要交给3。。。以此类推,超过4则从头开始)。
本节简单说了一下读写操作的流程,ID类的读请求直接根据ID来查找文档,搜索类的读请求先搜索索引表,再根据ID来查找文档;写数据请求会根据ID来计算应该把这个数据存储到哪个主分片上,然后通过主分片来修改数据;最后讲了一下对于请求分发的负载均衡,一方面通过数据量的平均分配来均衡,一方面使用轮询算法来降低单个分片的处理压力。
你在查询文档的时候,你可能看到返回结果中有如下的内容:
上面的几个前缀有的就是元数据。
元数据与具体的文档数据无关,每一个文档都有这些数据。
对于web中的json,数据格式主要有、、和这几种。
比如:
ElasticSearch并不是这样的,因为它要考虑分词,就算是字符串,它也要考虑里面的数据是不是日期类型的,日期类型通常不会分词。
ElasticSearch主要有以下这几种数据类型:
mapping负责维护index中文档的结构,包括文档的字段名、字段数据类型、分词器、字段是否进行分词等。这些属性会对我们的搜索造成影响。
在前面,其实我们都没有定义过mapping,直接就是插入数据了。其实这时候ElasticSearch会帮我们自动定义mapping,这个mapping会依据文档的数据来自动生成。
此时,如果数据是字符串的,会认为是text类型,并且默认进行分词;如果数据是日期类型类(字符串里面的数据是日期格式的),那么这个字段会认为是date类型的,是不分词的;如果数据是整数,那么这个字段会认为是long类型的数据;如果数据是小数,那么这个字段会认为是float类型的;如果是true或者false,会认为是boolean类型的。
举例:
我们插入以下数据【如果你用了这个索引,那么可以自定义一个索引,避免我随意创建的数据污染了你的测试数据】:
然后查看索引,其中mapping定义了我们刚刚定义的字段的信息:
【之前在第二篇中有提到,可以通过查看索引来查看mapping】
语法:
【内容补充:在旧版中,index的值有not_analyzed这样的值,现在只有true或false,它的原意是不分词,现在是不索引,在旧版中,不分词则会保留原始数据,在新版中使用keyword来保留原始数据】
举例:
mapping只能新增字段,不能修改原有的字段。
1.之前说过了,可以通过查看索引来查看mapping:,例如
2.通过,例子:
3.你也可以附加type来查看指定type包裹的mapping。
上面定义mapping的时候有定义一个keyword,这是什么?有什么用?
ElasticSearch对于一些类型的字段,例如text类型的字段,默认是会进行分词的。但如果我们并不想分词呢?一些数据我们想要非常精确地查找,并且只找到我们搜索的数据的时候,这个字段是不应该分词的。那么我们可以使用keyword来存储完整的原有的数据,keyword会作为一个索引词,然后我们针对来搜索。【例子上面已经举例了】
理论上,这个不分词的字段的数据应该是比较简短的,太长的话可能就没有必要不分词了。所以在定义keyword的时候还可以提供来限制超过多少个字符就不使用原有数据创建keyword.
现在版本中,对于默认分词的字段,现在会默认附加一个keyword。
1.数据类型的影响:
以date和text类型存储的“日期”类型的数据的分词差异化为例:
date和string这两者都是使用双引号包裹的,与其他数值型的不分词的数据类型不一样。所以以他们两个为例。
2.不索引的影响:
不索引的时候不会进行分词,甚至不能用于搜索。【低版本对于properties中的index设置不一样,5.x以下是不分词,5.x以上是不索引,不索引就不可以用于搜索】
3.keyword的影响:
keyword适用于不分词的搜索的情况,在keyword中的数据不会分词。
4.其他:
mapping还可以设置分词器来使用不同的分词器来分词。
本节介绍了什么是mapping,mapping负责管理索引的数据结构和字段的分词等一些配置。在直接存储数据的时候,会dynamic mapping;然后介绍了如何创建mapping,如何修改mapping,如何查看mapping;然后介绍了keyword这个保留原数据的一个特殊的字段。
相关度分数的具体算法我们其实并不需要关心。但可能还是需要大概了解一下计算的方式。
为什么说不需要关心呢?因为实际上相关度是由索引词直接决定的,分析好哪些是用于搜索的词就可以大致分析出相关度排序了。
当然,这只是大概的,因为内部可能因为索引词的重复性问题会降低某个词的score,但差距一般不会太大。
TF算法,全称Term frequency,索引词频率算法。
意义就像它的名字,会根据索引词的频率来计算,索引词出现的次数越多,分数越高。
例子如下:
搜索
有两份文档:A文档:,B文档:
结果是B文档的score大于A文档。
搜索
有两份文档:A文档:,B文档:
结果是A文档的score大于B文档。
要根据索引词来综合考虑。
IDF算法全称Inverse Document Frequency,逆文本频率。
搜索文本的词在整个索引的所有文档中出现的次数越多,这个词所占的score的比重就越低。
例子如下:
搜索,其中索引中hello出现次数1000次,world出现100次。
有三份文档:A文档,B文档,C文档
结果是:C>B>A
由于hello出现频率高,所以单个hello得到的score比不上world。
例子如下:
搜索
有两份文档:A文档,B文档
结果是:A>B
(下面属于理论分析,并不真实这样计算)
TF算法针对在Field中,索引词出现的频率;
IDF算法针对在整个索引中的索引词出现的频率;
Field-length norm算法针对Field的长度。
那么可以这样分析,由于Field-length norm算法并不直接针对score,所以它是最后起作用的,它理论上类似于一个除数。而TF和IDF是平等的,IDF计算出每一个索引词的score量,TF来计算整个文档中索引词的score的加和。
也就是如下的计算:
1.IDF:计算索引词的单位score,比如hello=0.1,world=0.2,
2.TF:计算整个文档的sum(score),得到0.1+0.2=0.3
3.Field-length norm:将sum(score)/对应Field的长度,得出的结果就是score。
elasticsearch提供了测试计算score的API,语法类似如下:
返回结果数据解析:
_explanation是score的原因,_explanation的格式是先列出分数,然后detail部分解释分数,内层也是。
这节介绍了相关度分数的计算方式,可以大致了解一下score是怎么得出来得,TF是索引词频率算法,是对索引词分数的加和;IDF是基于整个索引对每个索引词的计算分数;Field-length用于降低数据长的文档的相关度分数。
什么是分词器?
分词器负责对document进行处理,以提高搜索效率。
分词器通常由分解器tokenizer和词元过滤器token filter组成。
分词器对数据的分词处理:为了提高索引的效率,ElasticSearch会数据进行处理,处理方式主要有字符过滤、词转换、词拆分
字符过滤:过滤一些特殊字符,例如、、html标签,因为这些词通常搜索意义不大。
词转换:把一些意义相同的词统一转成一个词,(同词义转换)比如mom,mother统一转成mom;(大小写转换)he,He统一归为He;还处理一些词意义不大的词(停用词清除),比如英文的“the”,“to”,这些词使用频率很高,但没有具体意义。
词拆分:进行数据的拆分,拆分成词,比如把拆分成,,。另外,词拆分并不完全是按照数据的最小单位分解的,某一些分词器会把一些词进行组合,因为一些词的组合起来才有索引的意义,比如中文的一些词通常要组合起来才有意义,比如“大”和“家”要组成“大家”才有比较具体的意义,这是为了确保索引词的最小单位是有意义的(比如英文mom的最小单位是m,o,m,内部的分词器要能够区分出mom整个是有意义的才可以确保是采用mom作为索引,而不是采用m和o,也正是因为这个问题,所以英文分词器不能用于中文分词器)。【分词器有很多个,默认的分词器是不能适当对中文数据分词的,它只能把一个个数据按最小的单位拆分,因为英文分词器不能分清楚怎么把词拆分才有意义,由于配置分词器是一个较为靠后的知识点,所以前期将以英文数据为测试数据。】
每一个分词器的都有一些自己的规则,比如一些分词器会把a当成停用词,有些则不会;有些分词器会去掉标点符号,有些则不会;有些分词器能识别英文词,但识别不了中文词。要按需要来选择分词器。
可以使用下面的格式的命令来测试不同分词器的分词效果:
示例:
原句:
修改分词器就是修改mapping中的analyzer,mapping中某个字段一但创建就不能针对这个字段修改,所以只能删除再修改或新增。
在前面,我们都是使用英文作为字段的数据,因为ElasticSearch默认情况下无法很好地对中文进行分词,它默认只能一个个字地分词,所以它会把“中国人”这个词拆分成“中”“国”“人”。
所以我们需要使用“插件”来对ElasticSearch来进行扩展。
我们常用的中文分词器就是IK分词器。
如何安装
1.去github上下载IK的zip文件,下载的插件版本要与当前使用的elasticsearch版本一致,比如你是elasticsearch6.2.3,就下载6.2.3的【对于没有自己版本的,这时候可能需要下载新的elasticsearch,有些人说可以通过修改pom.xml来强行适配,但还是存在一些问题的。】。github-IK
2.把zip文件中的文件夹解压到中,并且重命名文件夹为
3.然后重启elasticsearch
使用
IK分词器提供了两种分词器ik_max_word和ik_smart
本节重新解释了分词器的作用(字符过滤,词转换,词拆分),介绍了几种常见的内置的分词器(standard,english这些),并举了一些分词的例子,以及如何修改字段的分词器(mapping的analyzer),最后还介绍了怎么安装支持中文分词的ik分词器。
文档的ID其实是可以不指定的,不指定的时候会随机生成唯一的一串字符串,
手动指定和不指定的区别:
手动指定通常适用于一些将数据库的数据转存到ElasticSearch的场景,因为这时候ID有特殊意义,让数据库的数据与ElasticSearch的数据关联起来,(有时候可能需要同时查数据库和ElasticSearch,那么可以直接根据数据库中的ID来查ElasticSearch中的数据)。
不指定的时候(这时候通常ID是没有特殊意义的),ElasticSearch会自动地帮我们生成一个ID值,自动生成的ID长度为20个字符,这个ID它能确保是不会重复的,就算是在分布式并发情况下也不会发生冲突。【由于此时的ID是随机字符串,所以根据ID来查询数据会比较麻烦】
这里讲的还是比较偏应用方面的基础知识,后面可能还会补充偏底层的一些基础知识,比如底层写入流程和NRT这些。