栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

ElasticSearch——进阶(一)

ElasticSearch——进阶(一)

ElasticSearch——进阶(一)

前言核心概念

索引类型文档字段映射(mapping)分片(Shards)副本分配 分布式集群

单节点集群水平扩容路由算法 & 分片控制数据流程

写流程读流程更新流程 & 批量操作流程 倒排索引

词条词典倒排表 文档相关

文档搜索

动态更新索引 文档刷新 & 文档刷写 & 文档合并

近实时搜索刷写流程 文档分析

内置分析器测试分析器指定分析器

IK分词器自定义分析器 文档控制

文档冲突(并发问题)乐观并发控制外部系统版本控制 文档展示-Kibana end...

前言

前面,我们学习了Elasticsearch的基本概念、基本操作,还学习了Elasticsearch在windows和linux环境下的集群部署,下面我们将继续深入,学习Elasticsearch的核心和原理。文章课程链接:ElasticSearch教程入门到精通

核心概念

在入门学习时我们已经了解过一些,这里回顾并补充一些新的概念

索引

一个索引就是一个拥有几分相似特征的文档的集合,比如说:你可以有一个客户数据的索引,有一个产品目录的索引,还有一个订单数据的索引。一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。在一个集群中,可以定义任意多的索引。 可以把索引理解为数据库。ElasticSearch索引的精髓:一切设计都是为了提高搜索的性能

类型

一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定。通常,会为具有一组共同字段的文档定义为一个类型,不同的ElasticSearch版本,类型有不同的变化,5.0支持多种type,6.0支持一种,7.0移除了这个概念,因为索引下就是数据,不需要类型这个概念了

文档

一个文档是一个可被索引的基础信息单元,也就是一条数据,文档以JSON格式来表示。一个索引中,可以存储任意多的文档

字段

相当于是数据表的字段,对文档数据根据不同属性进行的分类标识

映射(mapping)

mapping是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分析器、是否被索引等等,这些都是映射里面可以设置的,其它就是处理es里面数据的一些使用规则设置也叫做映射,按着最优规则处理数据对性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能对性能更好

分片(Shards)

一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。比如,我们存储用户信息,将其分为男女两个索引(提高查询效率和减少存储压力),查询男用户时直接去找对应的索引,它们合起来就是一个完整的索引
分片很重要,主要有两个方面

1)允许你水平分割/扩展你的内容容量。
2)允许你在分片之上进行分布式的、并行的操作,进而提高性能/吞吐量。

副本

在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片(副本)
复制分片重要的两个原因

1)在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。
2)扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行

分配

将分片分配给某个节点的过程,包括分配主分片或者副本。如果是副本,还包含从主分片复制数据的过程。这个过程是由master节点完成的

分布式集群 单节点集群

我们在包含一个空节点的集群内创建名为 users 的索引,并分配3个主分片和一份副本(每个主分片拥有一个副本分片)
用postman PUT 方式请求地址,并设置参数

http://127.0.0.1:1001/users

{
	"settings":{
		"number_of_shards":3, // 三个主分片
		"number_of_replicas":1 // 一个副本,每个主分片都会有
	}
}

我们的集群现在是拥有一个索引的单节点集群,所有3个主分片都会分配在node-1,可以通过 elasticsearch-head插件查看情况,这个插件要在谷歌浏览器上配置下,然后根据提示访问elasticsearch地址,即可展示,如图,其中星星代表master节点,加粗的0、1、2代表主分片

此时,健康值为yellow,表示当前集群的全部主分片都正常运行,但是副分片没有完全处在正常状态,Unassigned(副本)0、1、2为灰色状态(未分配),这是因为主分片和副本在一个节点上,如果出现故障仍会丢失数据,副本就不存在意义了,因此,我们还需要在启动一个ElasticSearch,这样就能正常使用副本了,就能实现故障转移了,启动好另一个节点后,副本的1、2、3就会变为绿色了

水平扩容

当启动了第三个节点,我们的集群会为了分散负载而对分片进行重新分配,分配的规则为

主分片和副本不在同一个节点上
尽量的分布均匀

在启动好第三个节点后,我们的主分片和副本分布如下

其中主分片0、1被分配到master节点,主分片3被分配到node-1003节点,且主分片和对应的副本不在同一个节点上,每个节点都是2个,满足前面的两点
现在一共是6个分片,如果我们的节点超过6台,那么有一台就用不到,也就浪费掉了,因此,我们在创建索引的时候要考虑到主分片的数量(尽可能的充分利用资源),其次,索引的主分片只能在创建时设置,创建好索引后不能修改,如果想要提高效率,我们可以更改副本分片的数量来实现,具体实现为
地址

http://127.0.0.1:1001/users/_settings

参数

{
	// 每个主分片两个副本分片
	"number_of_replicas":2
}

修改成功后,每个节点的将会分配到三个分片(主分片3+副本3*2)

应对故障
当有一个节点宕掉了,这时,主分片仍然存在,只是每个主分片的副本分片都会少一个(少个0、1、2),当节点又重新工作时,除了master节点可能会被其他节点代替(master节点被宕掉的情况),其他跟以前一样

路由算法 & 分片控制

如上图,我们的三个节点共有九个分片,现在我们需要存数据了,那么该存到哪个分片?我们要知道,存储数据肯定都是操作主分片,副本分片的功能是备份,那就只有0、1、2三个主分片,那么该怎么选择呢,如果随便选一个,我们在取数据时,怎么知道在哪?相信看到这里,你已经想到了hash,最常见的就是java中HsahMap的取模运算了,那ElasticSearch是如何实现的呢,也是一样的

路由计算:hash(id)% 主分片数量

现在我们数据通过hash存进去了,但是取的时候,我们有三个分片(1主+2副),该从哪个分片去取呢,这就是分片控制的事情了

分片控制:我们可以发送请求到集群中的任一节点。 每个节点都有能力处理任意请求。 每个节点都知道集群中任一文档位置,所以可以直接将请求转发到需要的节点上。 例如,将所有的请求发送到 Node-1001(真实情况应该为轮询等),我们将其称为协调节点(coordinating node) 。

数据流程 写流程

如图

实现过程:

1.客户端请求集群节点(任意节点,这个节点叫协调节点)
2.协调节点将请求转换到指定的节点
3.主分片保存数据
4.主分片将数据发送到副本
5.副本保存好后,进行反馈
6.主分片进行反馈
7.客户端获取反馈

在副本分片保存好数据后,才给客户端反馈成功(变更安全),如果想提升效率(ElasticSearch本身已经很快,一般不需要这样做),我们还可以更改一些参数,让其只保存好主分片数据就反馈成功

参数:consistency(一致性)
取值:one(主分片保存好就行) all(主副全部保存好) quorum(默认值,大多数副分片保存成功就行)
quorum计算规则(三个副本分片):(primary + 3replicas)/2 + 1 = 3

读流程

如图

实现过程:

1.客户端发送查询请求(任意节点,这个节点叫协调节点)
2.协调节点计算数据所在的分片位置(主分片和副本分片的位置)
3.为了能够负载均衡,可以轮询所有节点
4.将请求转发给具体的节点
5.节点返回查询结果,反馈给客户端

更新流程 & 批量操作流程

更新流程需要做读和写的流程,先按读流程找到数据,然后写入数据,然后反馈给客户端,如图

实现过程:

1.客户端发送请求(任意节点,这个节点叫协调节点)
2.协调节点计算数据所在主分片位置
3.将请求转发给主分片
4.检索文档,尝试修改,如果有其他进程占用(也在修改该文档),就会一直重试,当超过重试次数,就会放弃
5.修改成功,将新版本的文档并行转发到副本分片上,重新建立索引,所有的副本分片返回成功后,该节点反馈给协调节点成功,协调节点在反馈给客户端修改成功

批量操作流程:区别在于协调节点知道每个文档存在于哪个分片中,它将整个多文档请求分解成每个分片的多文档请求,并且这些请求并行转发到每个参与节点,简单的理解就是协调节点先计算知道文档的主分片位置,然后将其分开归类,主分片是P1的一份,是P2的一份,然后在发送给相应的主分片

倒排索引

一个索引的数据量太大,必然会影响其查询效率,因此,我们将索引分为几个部分(分片),几个分片加起来就是一个完整的索引数据,因此,分片是ElasticSearch最小的工作单元,分片完成数据的写入就会建立倒排索引
前面讲到,ElasticSearch使用的是倒排索引的结构,因为它适用于快速的全文搜索,关于它的介绍,在入门篇已经做过介绍,这里不再赘述,我们引入一些新的概念

词条

索引中最小的存储和查询单元

英文一般为一个单词,中文一般为一个词组

词典

字典,词条的集合,一般使用B+树或HashMap存储,B+树也是mysql底层的一种存储结构

倒排表

关键词出现在什么位置,每条记录称之为倒排项

文档相关 文档搜索

倒排索引被写入磁盘后是不可改变的,这样有几个好处

不需要锁:如果你从来不更新索引,就不需要担心多进程同时修改数据的问题
提高性能:一旦索引被读入内核的文件系统缓存,便会留在那,由于其不变性,只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘,提高了效率
其他索引(像filter缓存),在索引的生命周期内始终有效
写入单个大的倒排索引允许数据被压缩,减少磁盘的I/O和需要被缓存到内存的索引的使用量

因为不可改变,如果你需要让一个新的文档可被搜索,你需要重建整个索引,很大限制了索引能包含的数据量,由此,我们引出了动态更新索引

动态更新索引

由于倒排索引的不可变,我们又想要更新索引,我们就得用更多的索引,通过增加新的补充索引来反映新的修改,而不是直接重写整个倒排索引。每一个倒排索引都会被轮流查询到,从最早的开始查询后再对结果进行合并。

ElasticSearch基于Lucene,这个java库引入了按段搜索的概念,每一段本身都是一个倒排索引

文档刷新 & 文档刷写 & 文档合并 近实时搜索

es是按段搜索的,最新的数据存储在最新的段上,es不是实时的, 而是近实时的,是基于分段搜索进行的

数据写入的时候,先找到协调节点,协调节点会有路由计算规则,hash(_id) % 分片数量,计算主分片的位置,数据写入主分片,之后写入副本
延时: 主分片的延时 + 并行写入副本的最大延时

刷写流程

在内存中建立索引 --> 形成分段的数据对象 --> 数据落磁盘 (flush) --> 用户进行查询 (必须要落盘才能查询) --> 索引建立完毕 -->写translog (内存中) --> 磁盘写入translog (flush)

1、由于es写入过程较为复杂,因此没有和数据库一样先写日志再写索引,避免无效日志
2、由于数据在内存中并不能搜索,在磁盘中才能搜索,因此可以优化,先将内存中的数据写入操作系统的文件缓冲区中(刷新,默认一秒),此时数据可以被查询,并每隔半个小时落盘。并且由于有translog,也保证了数据的安全性
3、由于不断的刷写,会形成很多的文件段,此时就会进行文件的合并,提高效率,合并的时候,也会把标记删除的文件物理删除

图解过程

文档分析

分析包含下面的过程

1、将一块文本分成适合于倒排索引的独立的词条
2、将这些词条统一化为标准格式以提高它们的“可搜索性”,或者 recall

分析器执行上面的工作。分析器实际上是将三个功能封装到了一个包里:

字符过滤器
首先,字符串按顺序通过每个 字符过滤器 。他们的任务是在分词前整理字符串。一个字符过滤器可以用来去>掉HTML,或者将 & 转化成 and
分词器
其次,字符串被 分词器 分为单个的词条。一个简单的分词器遇到空格和标点的时候,可能会将文本拆分成词>条
Token过滤器
最后,词条按顺序通过每个 token 过滤器 。这个过程可能会改变词条(例如,小写化 Quick ),删除词条(例如, 像 a, and, the 等无用词),或者增加词条(例如,像 jump 和 leap 这种同义词)

内置分析器

Elasticsearch还附带了可以直接使用的预包装的分析器。接下来我们会列出最重要的分析器。为了证明它们的差异,我们看看每个分析器会从下面的字符串得到哪些词条:
“Set the shape to semi-transparent by calling set_trans(5)”
1、标准分析器

标准分析器是Elasticsearch默认使用的分析器。它是分析各种语言文本最常用的选择。它根据 Unicode 联盟 定义的 单词边界 划分文本。删除绝大部分标点。最后,将词条小写。它会产生:
Set, the, shape, to, semi, transparent, by, calling, set_trans, 5

2、简单分析器

简单分析器在任何不是字母的地方分隔文本,将词条小写。它会产生:
Set, the, shape, to, semi, transparent, by, calling, set, trans

3、空格分析器

空格分析器在空格的地方划分文本。它会产生:
Set, the, shape, to, semi-transparent, by, calling, set_trans(5)

4、语言分析器

特定语言分析器可用于 很多语言。它们可以考虑指定语言的特点。例如, 英语分析器附带了一组英语无用词(常用单词,例如 and 或者 the ,它们对相关性没有多少影响),它们会被删除。 由于理解英语语法的规则,这个分词器可以提取英语单词的词干
英语 分词器会产生下面的词条:
set, shape, semi, transpar, call, set_tran, 5
注意看 transparent、 calling 和 set_trans 已经变为词根格式

测试分析器

为了理解分词的过程和实际被存储到索引中的词条,我们使用postman GET 请求地址

http://localhost:9200/_analyze

在请求头指定分析器和要分析的文本

{
  "analyzer": "standard",
  "text": "Text to analyze"
}

测试结果text会被分为三个单词 text、to、analyze

指定分析器

有时候我们不想某些单词或者词语被分析,而elasticsearch默认使用的是标准分析器,得出的结果也不是我们想要的,因此,需要自己指定分析器

IK分词器

当我们使用标准分析器时,“测试单词”会被分析为“测”、“试”、“单”、“词”,而我们想要的是“测试”、“单词”,所以我们需要下载ES对应版本的中文分词器。
我们这里采用IK中文分词器,下载地址为: https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.8.0
将解压后的后的文件夹放入ES根目录下的plugins目录下,重启ES即可使用
然后,我们再次用 postman GET 请求

// GET http://localhost:9200/_analyze
{
	"text":"测试单词",
	"analyzer":"ik_max_word"
}

然后就可以得出想要的“测试”、“单词”了
当然,我们肯定会有存入特殊词语的场景,比如“艾欧里亚”,“巴黎欧莱雅”等,如果不进行特殊设置,它们会被分析为“艾”、“欧”、“里”…但是我们不想这个名词被分析,因此,我们得配置一下:

首先进入ES根目录中的plugins文件夹下的ik文件夹,进入config目录,创建custom.dic文件,写入弗雷尔卓德。同时打开IKAnalyzer.cfg.xml文件,将新建的custom.dic配置其中,重启ES服务器

自定义分析器

虽然Elasticsearch带有一些现成的分析器,然而在分析器上Elasticsearch真正的强大之处在于,你可以通过在一个适合你的特定数据的设置之中组合字符过滤器、分词器、词汇单元过滤器来创建自定义的分析器。在分析与分析器我们说过,一个分析器就是在一个包里面组合了三种函数的一个包装器, 三种函数按照顺序被执行:

1、字符过滤器

字符过滤器用来整理一个尚未被分词的字符串。例如,如果我们的文本是HTML格式的,它会包含像 < p > 或者 < div > 这样的HTML标签,这些标签是我们不想索引的。我们可以使用 html清除 字符过滤器 来移除掉所有的HTML标签,并且像把 Á 转换为相对应的Unicode字符 Á 这样,转换HTML实体。一个分析器可能有0个或者多个字符过滤器

2、分词器

一个分析器 必须 有一个唯一的分词器。 分词器把字符串分解成单个词条或者词汇单元。 标准分析器里使用的标准分词器,把一个字符串根据单词边界分解成单个词条,并且移除掉大部分的标点符号,然而还有其他不同行为的分词器存在。
例如, 关键词分词器完整地输出接收到的同样的字符串,并不做任何分词。 空格分词器只根据空格分割文本。正则分词器根据匹配正则表达式来分割文本

3、词单元过滤器

经过分词,作为结果的词单元流会按照指定的顺序通过指定的词单元过滤器 。
词单元过滤器可以修改、添加或者移除词单元。我们已经提到过 lowercase 和 stop 词过滤器 ,但是在 Elasticsearch 里面还有很多可供选择的词单元过滤器。 词干过滤器把单词遏制为词干。 ascii_folding 过滤器移除变音符,把一个像 “très” 这样的词转换为 “tres” 。 ngram 和 edge_ngram 词单元过滤器可以产生 适合用于部分匹配或者自动补全的词单元。

创建自定义分析器

// PUT http://localhost:9200/my_index
{
    "settings": {
        "analysis": {
            "char_filter": {
                "&_to_and": {
                    "type": "mapping", 
                    "mappings": [
                        "&=> and "
                    ]
                }
            }, 
            "filter": {
                "my_stopwords": {
                    "type": "stop", 
                    "stopwords": [
                        "the", 
                        "a"
                    ]
                }
            }, 
            "analyzer": {
                "my_analyzer": {
                    "type": "custom", 
                    "char_filter": [
                        "html_strip", 
                        "&_to_and"
                    ], 
                    "tokenizer": "standard", 
                    "filter": [
                        "lowercase", 
                        "my_stopwords"
                    ]
                }
            }
        }
    }
}

成功后,我们测试

// GET http://127.0.0.1:9200/my_index/_analyze
{
    "text":"The quick & brown fox",
    "analyzer": "my_analyzer"
}

得到分析结果“quick”、“and”、“brown”、“fox”

文档控制 文档冲突(并发问题)

当我们使用 index API 更新文档 ,可以一次性读取原始文档,做我们的修改,然后重新索引 整个文档 。最近的索引请求将获胜:无论最后哪一个文档被索引,都将被唯一存储在 Elasticsearch 中。如果其他人同时更改这个文档,他们的更改将丢失。

很多时候这是没有问题的。也许我们的主数据存储是一个关系型数据库,我们只是将数据复制到 Elasticsearch 中并使其可被搜索。 也许两个人同时更改相同的文档的几率很小。或者对于我们的业务来说偶尔丢失更改并不是很严重的问题。

但有时丢失了一个变更就是非常严重的。试想我们使用 Elasticsearch 存储我们网上商城商品库存的数量,每次我们卖一个商品的时候,我们在 Elasticsearch 中将库存数量减少。有一天,管理层决定做一次促销。突然地,我们一秒要卖好几个商品。就可能导致超出库存的数量。

悲观并发控制

这种方法被关系型数据库广泛使用,它假定有变更冲突可能发生,因此阻塞访问资源以防止冲突。 一个典型的例子是读取一行数据之前先将其锁住,确保只有放置锁的线程能够对这行数据进行修改。

乐观并发控制

Elasticsearch 中使用的这种方法假定冲突是不可能发生的,并且不会阻塞正在尝试的操作。然而,如果源数据在读写当中被修改,更新将会失败。应用程序接下来将决定该如何解决冲突。例如,可以重试更新、使用新的数据、或者将相关情况报告给用户。这种思想可参考java中的原子类(CAS)。

乐观并发控制

Elasticsearch 是分布式的。当文档创建、更新或删除时,新版本的文档必须复制到集群中的其他节点。Elasticsearch 也是异步和并发的,这意味着这些复制请求被并行发送,并且到达目的地时也许顺序是乱的。Elasticsearch 需要一种方法确保文档的旧版本不会覆盖新的版本。

当我们之前讨论 index ,GET 和 delete 请求时,我们指出每个文档都有一个 _version(版本)号,当文档被修改时版本号递增。 Elasticsearch 使用这个 version 号来确保变更以正确顺序得到执行。如果旧版本的文档在新版本之后到达,它可以被简单的忽略。

我们可以利用 version 号来确保应用中相互冲突的变更不会导致数据丢失。我们通过指定想要修改文档的 version 号来达到这个目的。 如果该版本不是当前版本号,我们的请求将会失败。

老的版本 es 使用 version,但是新版本不支持了,会报错误,提示我们用 if_seq_no和 if_primary_term
老版

POST http://127.0.0.1:9200/user/_update/1001?version=1

新版

POST http://127.0.0.1:9200/user/_update/1001?if_seq_no=1&if_primary_term=1

外部系统版本控制

一个常见的设置是使用其它数据库作为主要的数据存储,使用 Elasticsearch 做数据检索,这意味着主数据库的所有更改发生时都需要被复制到 Elasticsearch,如果多个进程负责这一数据同步,你可能遇到类似于之前描述的并发问题。

如果你的主数据库已经有了版本号 — 或一个能作为版本号的字段值比如 timestamp —那么你就可以在 Elasticsearch 中通过增加 version_type=external 到查询字符串的方式重用这些相同的版本号,版本号必须是大于零的整数,且小于 9.2E+18 — 一个 Java 中 long类型的正值。

外部版本号的处理方式和我们之前讨论的内部版本号的处理方式有些不同,Elasticsearch 不是检查当前 _version 和请求中指定的版本号是否相同,而是检查当前_version 是否小于指定的版本号。如果请求成功,外部的版本号作为文档的新 _version进行存储。

外部版本控制请求,当前_version为3

POST http://127.0.0.1:9200/user/_update/1001?version=4&version_type=external

文档展示-Kibana

Kibana 是为 Elasticsearch设计的开源分析和可视化平台。你可以使用 Kibana 来搜索,查看存储在 Elasticsearch 索引中的数据并与之交互。你可以很容易实现高级的数据分析和可视化,以图标的形式展现出来
Kibana 核心产品搭载了一批经典功能:柱状图、线状图、饼图、旭日图,等等
下载地址:https://elasticsearch.cn/download/
linux安装:

rpm -ivh kibana-7.6.1-x86_64.rpm

这块网上讲的很多,就不记录了

end…

**如果总结的还行,就点个赞呗 @_@ 如有错误,欢迎指点,下一篇ElasticSearch——进阶(二)!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/752834.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号