一个索引就是一个拥有几分相似特征的文档的集合。比如说,学生信息可以作为一个索引,学校专业信息可以作为一个索引,公司的员工信息也可以作为一个索引。一个索引由一个小写字母组成的名字来标识。我们必须有索引,才方便我们快速的查询文档。比如:书有目录后,才能快速的找到对应章节。
在一个索引内,可以定义一种或多种类型。但是在6.x版本一个索引只支持一种 type 。在7.x版本默认不再支持自定义索引类型(默认类型为 _doc)。之所以废除 type 是因为:
我们⼀直认为ES中的“index”类似于关系型数据库的“database”,⽽“type”相当于⼀个数据表。ES的开发者们认为
这是⼀个糟糕的认识。例如:关系型数据库中两个数据表⽰是独⽴的,即使他们⾥⾯有相同名称的列也不影响使⽤,但ES中
不是这样的。
我们都知道elasticsearch是基于Lucene开发的搜索引擎,⽽ES中不同type下名称相同的filed最终在Lucene中的处理⽅式
是⼀样的。举个例⼦,两个不同type下的两个user_name,在ES同⼀个索引下其实被认为是同⼀个filed,你必须在两个不
同的type中定义相同的filed映射。否则,不同type中的相同字段名称就会在处理中出现冲突的情况,导致Lucene处理效率
下降。
去掉type能够使数据存储在独⽴的index中,这样即使有相同的字段名称也不会出现冲突,就像ElasticSearch出现的第⼀
句话⼀样“你知道的,为了搜索····”,去掉type就是为了提⾼ES处理数据的效率。
除此之外,在同⼀个索引的不同type下存储字段数不⼀样的实体会导致存储中出现稀疏数据,影响Lucene压缩⽂档的能⼒,导致ES查询效率的降低。
一个文档,即一条数据。它是一个可被索引的基础信息单元。比如:学生信息 索引中 的一个学生的信息,即一个文档。文档以 JSON 格式来表示。
相当于数据表的字段、对象里的属性,对文档数据根据不同属性进行的分类标识。
mapping 是处理数据的方式和规则方面做一些限制。在关系数据库中,我们也会设置 字段的数据类型、默认值、以及大小等等。在 ES 中我们也需要设置 文档字段的 数据类型、默认值、分析器、是否被索引等等。这些都是在映射里设置的。除此之外,处理ES中的数据的一些使用规则设置也叫做映射。按着最优规则限制、处理数据对性能提高很大。所以我们需要建立映射,更需要思考建立怎么样的映射 性能最优。
一个索引理论上可以存储无限个文档。但是实际上如果 一个索引 只存放在一台机器上,则其必定会受到硬件限制。 比如:一个具有10亿文档数据的索引占据 1TB的存储空间,单个节点可能没有如此大的存储空间,或者说单个节点难以支撑如此海量数据的搜索。那么如何让 一个索引 可以不受 单个节点 所带来的限制。答案就是分片。将一个索引划分为若干个分片,分别存储在不同的节点上。当你创建一个索引时,就可以指定你想要的分片数量。每个分片本身都可以看作一个功能完善并且独立的索引,这个索引可以被放到集群的任意节点中。
分片帮助我们解决了:
分片如何分布,分布在哪些节点以及各分片中文档如何聚合和搜索,全部由 ES 来管理和控制,这些对我们来说都是透明的,无需关心
前面我们已经将 一个索引中的数据 分散到若干个分片中,存储在 集群的节点中。然而 无论是因为网络/设备故障等原因,都可能导致分片数据的不可用或丢失。那么如何保证数据的安全以及高可用?ES 允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片(副本)。这种故障转移机制是非常有用并且是强烈推荐的。
副本帮助我们解决了:
总的说:每个索引可以被分成若干个分片。一个索引可以被复制0次(即没有副本),也可以被复制多次。一旦产生了副本,每个索引中就包含了副本分片(原分片的拷贝)和原分片(主分片)之别。分片数和副本数可以在创建索引时指定。当索引创建后,你依旧可以动态的改变副本数量(即复制几份)。但是不能再动态的改变主分片的数量了。 默认情况下,ES中的每个索引会有一个原分片和一个副本分片。
将分片分配给某个节点的过程,包括分配主分片、副本分片。其中副本分片如何从主分片复制数据,这些过程都是由 master 节点控制
在分布式集群中,分为单节点集群(伪集群)、多节点集群。为了方便观察节点或者集群的运行情况,我们可以使用 一个 浏览器插件可视化观察——ElasticSearch Head 。如果不会使用的话,可以搜一下,使用十分简单。
这里提供插件下载:
启动之前的使用过的 单节点——node。并且新建一个索引 u3 ,新建索引时在请求体中配置 分片和副本数量。
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}
这是在 ElasticSearch Head 中 我们就可以看见 u3 在 机器A1836上有三个分片,且运行正常。但是也可以看到还有 三个分片 未正常部署,其实这三个分片就是 副本分片。未能正常部署是因为 此时集群中只有单个节点,而副本分片必须和原分片在不同节点上(部署在一个节点上是没有意义的)。所以副本分片没有被分配到任何节点。也因此,集群健康处于 yellow(即分片正常、副本运行不正常)状态,集群运行正常,但若出现硬件故障数据容易丢失。
所谓多节点集群,即节点大于等于2个。当集群中有第二个节点运行时,副本就可以部署,以实现故障转移 。这里我们使用之前配置的 3个节点的 ES集群。启动节点后,在 ES Head 中观察到 三个节点都运行起来了。
这时我们新建一个索引 u3 ,并配置 分片数和副本数。
我们可以看到 一共有 6个分片(3个原分片、3个副本分片)被分配给 3个节点了。这时我们模拟 node3 节点故障(手动关闭),过一会 ES 就会帮助我们自动进行故障转移,将 0、1、2 分片及其对应副本分片 重新分配。下图中:有加粗绿色边框的代表的是 主分片(原分片),没有加粗的是 副本分片。加 黑色星号★ 的节点是 master节点。此时集群健康是绿色的green,代表集群的主分片运行正常,且副本已经分配(解决单点故障问题)。
比如,按上图所示:此时集群有两个节点,每个节点就会有三个分片。而每个分片若文档很多,那么如何扩容,让每个节点的负载小一点呢。其实当我们为集群增加一个节点时,master 就会对分片进行重新分配(每个节点就会少负载一个分片)。每个节点所负载的分片数减少 对分片而言,其能使用的节点硬件资源也就相对变多。这就实现了 水平扩容。
主分片数在索引创建时就确定下来了。这个数目的设定要适合这个索引之后的最大存储量。虽然主分片数不能动态增加了,但是副本数可以增加。而常规的读操作(搜索)是可以被 主分片或副本分片处理,即副本增加可以使得 ES 拥有更多吞吐量。所以可以在运行时动态调整副本数量,来伸缩集群。
这里我们演示一下动态增加副本数量,以集群u3为例,一开始只有两个节点,1份副本。这里使用postman请求:
localhost:9201/u3/_settings //put 请求
{ //请求体内容
"number_of_replicas" : 2
}
这样集群就有了 3 * 3 = 9 个分片
如果集群中多个节点,那当需要存储数据时,ES 会将数据存储在哪个节点呢?这个决策是 通过 路由计算 得出的。
路由计算:hash(id) % 主分片数量 = 0/1/2
然而对于查询数据:在查询之前,插入完成之后,主分片和副本都会有数据。但是不是随意的访问节点进行查询,而是要进行分片控制。比如:我们每次将查询请求发送到 node-1 节点,此时 node-1 节点可以称为 协调节点。因为 node-1 不能处理所有的查询请求,它会将查询请求转发给集群的其他节点。一般是使用轮询算法转发给集群中所有节点。
写流程
当写请求发送到 es 集群的某个(任意)节点——协调节点 时,协调节点会通过路由计算,将请求转发到应该写入的节点的主分片中。当数据写入主分片后,将数据写请求转发给该主分片的副本。副本保存后进行反馈给主分片,主分片再向客户端反馈 存储成功。
当然 ES 可以使用一些可选请求参数,允许你主分片保存完毕后,即返回客户端响应结果。不过这可能是以数据安全为代价提升性能
读流程
当读请求发送到 es 集群的某个节点上,此时接受到请求的节点作为协调节点,将会查找该请求数据的所在的分片及其副本分片信息,然后轮询选择(负载均衡)分片处理读请求。分片所在节点返回查询结果给客户端。
更新流程
客户端向协调节点发送更新请求,协调节点负责计算、控制请求转发到数据所在主分片的节点上。节点此时会从主分片中检索文档,尝试修改文档。如果文档正在被其他线程进行修改,则会一直重试修改操作,直到超过 retry_on_conflict 次数。修改文档成功后,会尝试重新索引主分片的文档,同时会将新版本的文档并行转发处于其他节点的副本分片上并重新建立索引。当所有副本分片都更新成功后,文档所在主分片的节点才会向协调节点返回成功,协调节点向客户端返回成功。
主分片将更改转发到副本分片时,它不是转发更新请求。而是转发完整文档的新版本。
传统的数据库每个字段存储单个值,但这对全文检索并不够。文本字段中的每个单词需要被搜索,对数据库意味着需要单个字段有索引多值的额能力。最好的支持是一个字段多个值需求的数据结构就
什么是正排索引呢?我们知道在关系型数据库中几乎都存在主键,而且主键一定是唯一性索引。而正排表正是通过索引(此处为文档ID)来寻找文档位置。如果我们想要从寻找文档数据中包含 maker 的文档,那么我们就需要检索每个文档,并且对文档内数据进行模糊匹配。这也是 MySQL进行全文检索很慢的原因。其实 MySQL 并不是只能设置 一个唯一性索引,它可以设置多个索引。但是一般索引是针对一个字段进行设置的。我们不能将大量文本数据放在一个字段,并将这个字段设置索引。
如果 我们将文本数据进行分词 然后每个单词 作为 索引。那么我们就可以很轻松实现 全文检索。比如:希望寻找文档中包含 maker 的文档,那我们可以求 marker的hash值,确定索引——masker的位置。拿到索引对应的数据值——文档iD。那我们就可以通过文档ID获取文档了。
这里提及了分词。对于英文来说分词很简单,单词与单词之间都是用 空格 间隔。但是对中文来说,这就比较难了。中文是由字组成,一个句子如何划分 字 组成词 很复杂。分词器,其实是依靠一些数据集(词汇库),来判断哪些字可以组成一个词汇。但即使这样,分词也不一定完美满足需求。比如检索 大智慧 。在分词器 会将 大智慧 拆分为 大、智慧 两个词,来去检索。这虽然符合常规认知,但是实际上 大智慧 是一个公司名称,显然如果不拆分去检索 反而效果会更好。
ElasticSearch 内置了分词器,如标准分词器、简单分词器、空白词器等。但这些分词器对我们最常使用的中文并不友好,不能按我们的语言习惯进行分词。ik分词器就是一个标准的中文分词器。它可以根据定义的字典对域进行分词,并且支持用户配置自己的字典,所以它除了可以按通用的习惯分词外,我们还可以定制化分词。ik分词器是一个插件包,我们可以用插件的方式将它接入到ES。
具体的安装使用可查看 4.7
词典又称为字典,是词条的集合。而词条,它是索引中最小的存储和查询单元。倒排索引,就是在查询关键字时按词典进行分词 拆分成若干个词条,然后就可以查询 词条 在倒排列表中的指针。通过倒排列表获取 该词条 所在的文档ID列表。
文档分析主要目的:
将文本分成适合于倒排索引的独立的 词条
将这些词条统一化为标准格式 以提高他们的 可搜索性
为了实现这些,主要使用了 字符过滤器、分词器、token过滤器
字符过滤器:首先字符串按顺序通过每个字符过滤器。他们的任务是在分词前整理字符串。一个字符过滤器可以用来去掉HTML,或者将 & 转化成 and。
分词器:字符串被分词器分成单个的词条。一个简单的分词器遇到空格和标点的时候,可能会将文本拆分成词条。
token过滤器:词条按顺序通过每个token过滤器。这个过程可能会改变词条(例如:小写化 Quick)、删除词条(例如:a,and,the等无用词),或者增加词条(例如:像jump和leap这种同义词)
ElasticSearch 还附带了可以直接使用的预包装的分析器。接下来我们会列出最重要的分析器。
分析器使用场景:
当我们检索一个文档时,它的全文域被分析成词条以用来创建倒排索引。但是,当我们在全文域 搜索 的时候,我们需要将 查询字符串通过 相同的分析过程,以保证 我们搜索的词条格式与索引中的词条格式一致。
ES是 分布式的。当文档创建、更新或删除时,新版本的文档必须复制到集群的其他节点中。ElasticSearch 也是异步和并发的,这意味着这些复制请求被并发发送,并且到达目的地时 也许顺序是 混乱的。比如:user1 更改的 id=001 文档的 sum 字段,将其 sum = 10。同时user2希望将其改成该文档的sum=9。此时两份更新都在从主分片复制到副本分片。但是由于是异步的,可能有些副本处理了sum=9的更新请求,后处理了sum=10的更新请求。 造成了不同副本 sum 字段不一致。因此,我们必须需要一种方法确保文档的旧版本不会覆盖新版本
在 GET 和 DELETE 请求时,我们指出每个文档都有一个 _version (版本号)。当文档被修改时版本号递增。ElasticSearch 使用这个 version 来确保变更以正确顺序得以执行。如果旧版本的文档在新版本之后到达,它可以被简单的忽略。
我们可以利用 version 号来确保 应用中相互冲突的变更不会导致数据丢失。我们通过指定想要修改文档的 version 号来达到这个目的。如果该版本不是当前版本号,我们的请求就会失败。但是新的es,修改时不是 只携带 version就可以 了,而且携带需要修改文档的 if_seq_no 和 if_primary_term。如果 if_seq_no 和 if_primary_term 不正确——(在我们更新文档成功之前,文档已经被更新),则我们的更新就会失败。我们需要重新获取 最新文档的 if_seq_no 和 if_primary_term 进行更新请求。这其实就是 乐观锁
localhost:9200/shopping/_update/0001?if_seq_no=7&if_primary_term=7
//如果想兼容,也是有办法的,使用如下命令即可,此时version应当比最新文档version大1
localhost:9200/shopping/_doc/0001?version=6&version_type=external
IK分词器下载、安装
首先我们需要从 github上下载 IK 分词器。下载包的版本要和 ES 的版本一致,这里我们下载的就是 7.10.2 版本。(不用下载源码)
https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.10.2/elasticsearch-analysis-ik-7.10.2.zip
在ES 的plugins文件下建立新文件夹 ik,并在下载完成后,解压到 ik 文件夹。
然后重启 es 。如果重启失败,可以去 logs文件夹中查看日志。一般都是因为 es 版本和 ik 分词器 版本不一致才会重启失败。
分词器测试
在 es 重启后,可以使用 Postman发送get请求,路径和请求体内容如下:
localhost:9200/_analyze
{
"analyzer" : "ik_max_word", // ik分词器进行最细粒度划分
"text" : "程序员"
}
当然也可以使用之前 安装的 kibana ,运行启动 kibana,注意配置文件中 es ip 地址 要正确。运行起来之后,访问 5601 端口,进入 Kibana网页,使用开发工具。
在控制台请求:
GET _analyze
{
"analyzer" : "ik_max_word",
"text" : "程序员"
}
就会返回 对 text 内容进行分词的结果:
自定义字典
很多时候 IK的分词器,并不能满足你业务中分词的需要。比如:大智慧 ,它往往会分成 大智慧、智慧、大智。但是在业务中 大智慧 是一个公司的简称,不应拆分。这时,我就可以使用自自定义字典,来告知 ik分词器 大智慧 是一个完整的词汇。
为了优化分词效果,我们需要 把 大智慧 视为一个词汇,并且 停止 将大智慧分词 成 大智。这样 大智慧就可被分词为:大智慧、智慧
在 ik 的config文件下的 IKAnalyzer.cfg.xml文件,明确说明了 自己的扩展字典和扩展停止字典的名称。
那我们就可以在目录下,新建两个dic文件,并将文件名配置到 配置文件中。然后重启 es,就可以查看到 es已经加载了我们的字典了
重新在 kibana中 测试 分词效果,可以看出自定义字典的使用已经达到预期效果了。
如果此时你没有发现重启后没有生效,很有可能是你 字典文件 的编码格式 不是UTF-8
学习笔记 课程地址:
感受:
@CSDN: https://blog.csdn.net/qq_39668099
@Author:[email protected]