Elaticsearch,简称为es, es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。es也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
Lucence 比较老牌,API比较繁琐
Solr 基于Lucence 封装而来
Elaticsearch ES 分布式全文检索引擎
当单纯的对已有数据进行搜索时,Solr更快。
当实时建立索引时, Solr会产生io阻塞,查询性能较差, Elasticsearch具有明显的优势。
随着数据量的增加,Solr的搜索效率会变得更低,而Elasticsearch却没有明显的变化。
综上所述,Solr的架构不适合实时搜索的应用。
实际生产环境测试,下图为将搜索引擎从Solr转到Elasticsearch以后的平均查询速度有了50倍的提升。
ElasticSearch vs Solr 总结
(1)二者安装都很简单。
(2)Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能。
(3)Solr 支持更多格式的数据,比如JSON、XML、CSV,而 Elasticsearch 仅支持json文件格式。
(4)Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供
(5)Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于 Elasticsearch。
(6)Solr 是传统搜索应用的有力解决方案,但 Elasticsearch 更适用于新兴的实时搜索应用。
ElasticSearch分为Linux和Window版本,基于我们主要学习的是ElasticSearch的Java客户端的使用,所以我们课程中使用的是安装较为简便的Window版本,项目上线后,公司的运维人员会安装Linux版的ES供我们连接使用。
ElasticSearch的官方地址: https://www.elastic.co/products/elasticsearch
在资料中已经提供了下载好的5.6.8的压缩包:
Window版的ElasticSearch的安装很简单,类似Window版的Tomcat,解压开即安装完毕,解压后的ElasticSearch
的目录结构如下:
修改elasticsearch配置文件:config/elasticsearch.yml,增加以下两句命令:
http.cors.enabled: true
http.cors.allow-origin: "*"
此步为允许elasticsearch跨越访问,如果不安装后面的elasticsearch-head是可以不修改,直接启动。
点击ElasticSearch下的bin目录下的elasticsearch.bat启动,控制台显示的日志信息如下:
注意:9300是tcp通讯端口,集群间和TCPClient都执行该端口,9200是http协议的RESTful接口 。
通过浏览器访问ElasticSearch服务器,看到如下返回的json信息,代表服务启动成功:
注意:ElasticSearch是使用java开发的,且本版本的es需要的jdk版本要是1.8以上,所以安装ElasticSearch之前保证JDK1.8+安装完毕,并正确的配置好JDK环境变量,否则启动ElasticSearch失败。
ElasticSearch不同于Solr自带图形化界面,我们可以通过安装ElasticSearch的head插件,完成图形化界面的效果,完成索引数据的查看。安装插件的方式有两种,在线安装和本地安装。本文档采用本地安装方式进行head插件的安装。elasticsearch-5-*以上版本安装head需要安装node和grunt
1)下载head插件:https://github.com/mobz/elasticsearch-head
在资料中已经提供了elasticsearch-head-master插件压缩包:
2)将elasticsearch-head-master压缩包解压到任意目录,但是要和elasticsearch的安装目录区别开
3)下载nodejs:https://nodejs.org/en/download/
在资料中已经提供了nodejs安装程序:
双击安装程序,步骤截图如下:
安装完毕,可以通过cmd控制台输入:node -v 查看版本
5)将grunt安装为全局命令 ,Grunt是基于Node.js的项目构建工具
在cmd控制台中输入如下执行命令:
npm install -g grunt -cli
执行结果如下图:
6)进入elasticsearch-head-master目录启动head,在命令提示符下输入命令:
npm install
grunt server
7)打开浏览器,输入 http://localhost:9100,看到如下页面:
如果不能成功连接到es服务,需要修改ElasticSearch的config目录下的配置文件:config/elasticsearch.yml,增加
以下两句命令:
http.cors.enabled: true
http.cors.allow-origin: "*"
然后重新启动ElasticSearch服务,重新连接。
Elasticsearch是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在Elasticsearch中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。Elasticsearch比传统关系型数据库如下:
Relational DB(Mysql) ‐> Databases(数据库) ‐> Tables(表) ‐> Rows(行) ‐> Columns(列)Elasticsearch ‐> Indices(索引库) ‐> Types(类型) ‐> Documents(文档) ‐> Fields(字段)
一个索引就是一个拥有几分相似特征的文档的集合。比如说,你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。在一个集群中,可以定义任意多的索引。
在一个索引中,你可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定。通常,会为具有一组共同字段的文档定义一个类型。比如说,我们假设你运营一个博客平台并且将你所有的数据存储到一个索引中。在这个索引中,你可以为用户数据定义一个类型,为博客数据定义另一个类型,当然,也可以为评论数据定义另一个类型。
相当于是数据表的字段,对文档数据根据不同属性进行的分类标识。
mapping是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分析器、是否被索引等等,这些都是映射里面可以设置的,其它就是处理es里面数据的一些使用规则设置也叫做映射,按着最优规则处理数据对性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能对性能更好。
一个文档是一个可被索引的基础信息单元。比如,你可以拥有某一个客户的文档,某一个产品的一个文档,当然,也可以拥有某个订单的一个文档。文档以JSON(Javascript Object Notation)格式来表示,而JSON是一个到处存在的互联网数据交互格式。
在一个index/type里面,你可以存储任意多的文档。注意,尽管一个文档,物理上存在于一个索引之中,文档必须被索引/赋予一个索引的type。
Elasticsearch是一个接近实时的搜索平台。这意味着,从索引一个文档直到这个文档能够被搜索到有一个轻微的延迟(通常是1秒以内)
一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”。这个名字是重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群
一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。和集群类似,一个节点也是由一个名字来标识的,默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。
一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。
在一个集群里,只要你想,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。
一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。分片很重要,主要有两方面的原因:
1)允许你水平分割/扩展你的内容容量。
2)允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量。
至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由Elasticsearch管理的,对于作为用户的你来说,这些都是透明的。
在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。
复制之所以重要,有两个主要原因: 在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行。总之,每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制的数量,但是你事后不能改变分片的数量。
默认情况下,Elasticsearch中的每个索引被分片5个主分片和1个复制,这意味着,如果你的集群中至少有两个节点,你的索引将会有5个主分片和另外5个复制分片(1个完全拷贝),这样的话每个索引总共就有10个分片。
实际开发中,主要有三种方式可以作为elasticsearch服务的客户端:
第一种,elasticsearch-head插件
第二种,使用elasticsearch提供的Restful接口直接访问第三种,使用elasticsearch提供的API进行访问
Postman中文版是postman这款强大网页调试工具的windows客户端,提供功能强大的Web API & HTTP 请求调试。软件功能非常强大,界面简洁明晰、操作方便快捷,设计得很人性化。Postman中文版能够发送任何类型的HTTP 请求 (GET, HEAD, POST, PUT…),且可以附带任何数量的参数。
Postman官网:https://www.getpostman.com
课程资料中已经提供了安装包
curl ‐X '://:/?' ‐d '
其中:
参数 | 解释 |
---|---|
VERB | 适当的 HTTP 方法 或 谓词 : GET 、 POST 、 PUT 、 HEAD 或者 DELETE 。 |
PROTOCOL | http 或者 https (如果你在 Elasticsearch 前面有一个 https 代理) |
HOST | Elasticsearch 集群中任意节点的主机名,或者用 localhost 代表本地机器上的节点。 |
PORT | 运行 Elasticsearch HTTP 服务的端口号,默认是 9200 。 |
PATH | API 的终端路径(例如 _count 将返回集群中文档数量)。Path 可能包含多个组件,例如:_cluster/stats 和 _nodes/stats/jvm 。 |
QUERY_STRING | 任意可选的查询字符串参数 (例如 ?pretty 将格式化地输出 JSON 返回值,使其更容易阅读) |
BODY | 一个 JSON 格式的请求体 (如果请求需要的话) |
请求url:
PUT localhost:9200/blog1
请求体:
{"mappings": {"article": {"properties": {"id": {"type": "long","store": true,"index":"not_analyzed"},"title": {"type": "text", # 数据类型"store": true, # 是否存储"index":"analyzed", # 是否索引"analyzer":"standard" # 是否分词},"content": {"type": "text","store": true,"index":"analyzed","analyzer":"standard"}}}}
}
postman截图:
elasticsearch-head查看:
我们可以在创建索引时设置mapping信息,当然也可以先创建索引然后再设置mapping。
在上一个步骤中不设置maping信息,直接使用put方法创建一个索引,然后设置mapping信息。
请求的url:
POST http://127.0.0.1:9200/blog2/hello/_mapping
请求体:
{"hello": {"properties": {"id":{"type":"long","store":true},"title":{"type":"text","store":true,"index":true,"analyzer":"standard"},"content":{"type":"text","store":true,"index":true,"analyzer":"standard"}}}}
PostMan截图
请求url:
DELETE localhost:9200/blog1
postman截图:
elasticsearch-head查看:
请求url:
POST localhost:9200/blog1/article/1
请求体:
{"id":1,"title":"ElasticSearch是一个基于Lucene的搜索服务器","content":"它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。"
}
postman截图:
elasticsearch-head查看:
请求url:
POST localhost:9200/blog1/article/1
请求体:
{"id":1,"title":"【修改】ElasticSearch是一个基于Lucene的搜索服务器","content":"【修改】它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。"
}
postman截图:
elasticsearch-head查看:
请求url:
DELETE localhost:9200/blog1/article/1
postman截图:
elasticsearch-head查看:
请求url:
GET localhost:9200/blog1/article/1
postman截图:
请求url:
POST localhost:9200/blog1/article/_search
请求体:
{"query": {"query_string": {"default_field": "title","query": "搜索服务器"}}
}
postman截图:
注意:
将搜索内容"搜索服务器"修改为"钢索",同样也能搜索到文档,该原因会在下面讲解中得到答案
{"query": {"query_string": {"default_field": "title","query": "钢索"}}
}
请求url:
POST localhost:9200/blog1/article/_search
请求体:
{"query": {"term": {"title": "搜索"}}
}
postman截图:
在进行字符串查询时,我们发现去搜索"搜索服务器"和"钢索"都可以搜索到数据;
而在进行词条查询时,我们搜索"搜索"却没有搜索到数据;
究其原因是ElasticSearch的标准分词器导致的,当我们创建索引时,字段使用的是标准分词器:
{"mappings": {"article": {"properties": {"id": {"type": "long","store": true,"index":"not_analyzed"},"title": {"type": "text","store": true,"index":"analyzed", //是否要分词"analyzer":"standard" //使用 标准分词器},"content": {"type": "text","store": true,"index":"analyzed","analyzer":"standard" //标准分词器}}}}
}
例如对 “我是程序员” 进行分词
标准分词器分词效果测试:
http://127.0.0.1:9200/_analyze?analyzer=standard&pretty=true&text=我是程序员
分词结果:
{"tokens" : [{"token" : "我","start_offset" : 0,"end_offset" : 1,"type" : "","position" : 0},{"token" : "是","start_offset" : 1,"end_offset" : 2,"type" : "","position" : 1},{"token" : "程","start_offset" : 2,"end_offset" : 3,"type" : "","position" : 2},{"token" : "序","start_offset" : 3,"end_offset" : 4,"type" : "","position" : 3},{"token" : "员","start_offset" : 4,"end_offset" : 5,"type" : "","position" : 4}]
}
而我们需要的分词效果是:我、是、程序、程序员
这样的话就需要对中文支持良好的分析器的支持,支持中文分词的分词器有很多,word分词器、庖丁解牛、盘古分词、Ansj分词等,但我们常用的还是下面要介绍的IK分词器。
IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始,IKAnalyzer已经推出 了3个大版本。最初,它是以开源项目Lucene为应用主体的,结合词典分词和文法分析算法的中文分词组件。新版本的IKAnalyzer3.0则发展为 面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。
IK分词器3.0的特性如下:
1)采用了特有的“正向迭代最细粒度切分算法“,具有60万字/秒的高速处理能力。
2)采用了多子处理器分析模式,支持:英文字母(IP地址、Email、URL)、数字(日期,常用中文数量词,罗马数字,科学计数法),中文词汇(姓名、地名处理)等分词处理。
3)对中英联合支持不是很好,在这方面的处理比较麻烦.需再做一次查询,同时是支持个人词条的优化的词典存储,更小的内存占用。
4)支持用户词典扩展定义。
5)针对Lucene全文检索优化的查询分析器IKQueryParser;采用歧义分析算法优化查询关键字的搜索排列组合,能极大的提高Lucene检索的命中率。
1)下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases
课程资料也提供了IK分词器的压缩包:
2)解压,将解压后的elasticsearch文件夹拷贝到elasticsearch-5.6.8\plugins下,并重命名文件夹为analysis-ik
3)重新启动ElasticSearch,即可加载IK分词器
IK提供了两个分词算法ik_smart 和 ik_max_word
其中 ik_smart 为最少切分,ik_max_word为最细粒度划分
我们分别来试一下
1)最小切分:在浏览器地址栏输入地址
http://127.0.0.1:9200/_analyze?analyzer=ik_smart&pretty=true&text=我是程序员
输出的结果为:
{"tokens" : [{"token" : "我","start_offset" : 0,"end_offset" : 1,"type" : "CN_CHAR","position" : 0},{"token" : "是","start_offset" : 1,"end_offset" : 2,"type" : "CN_CHAR","position" : 1},{"token" : "程序员","start_offset" : 2,"end_offset" : 5,"type" : "CN_WORD","position" : 2}]
}
2)最细切分:在浏览器地址栏输入地址
http://127.0.0.1:9200/_analyze?analyzer=ik_max_word&pretty=true&text=我是程序员
输出的结果为:
{"tokens" : [{"token" : "我","start_offset" : 0,"end_offset" : 1,"type" : "CN_CHAR","position" : 0},{"token" : "是","start_offset" : 1,"end_offset" : 2,"type" : "CN_CHAR","position" : 1},{"token" : "程序员","start_offset" : 2,"end_offset" : 5,"type" : "CN_WORD","position" : 2},{"token" : "程序","start_offset" : 2,"end_offset" : 4,"type" : "CN_WORD","position" : 3},{"token" : "员","start_offset" : 4,"end_offset" : 5,"type" : "CN_CHAR","position" : 4}]
}
删除原有blog1索引
DELETE localhost:9200/blog1
创建blog1索引,此时分词器使用ik_max_word
PUT localhost:9200/blog1
{"mappings": {"article": {"properties": {"id": {"type": "long","store": true,"index":"not_analyzed"},"title": {"type": "text","store": true,"index":"analyzed","analyzer":"ik_max_word"},"content": {"type": "text","store": true,"index":"analyzed","analyzer":"ik_max_word"}}}}
}
创建文档
POST localhost:9200/blog1/article/1
{"id":1,"title":"ElasticSearch是一个基于Lucene的搜索服务器","content":"它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。"
}
请求url:
POST localhost:9200/blog1/article/_search
请求体:
{"query": {"query_string": {"default_field": "title","query": "搜索服务器"}}
}
postman截图:
将请求体搜索字符串修改为"钢索",再次查询:
{"query": {"query_string": {"default_field": "title","query": "钢索"}}
}
postman截图:
请求url:
POST localhost:9200/blog1/article/_search
请求体:
{"query": {"term": {"title": "搜索"}}
}
postman截图:
ES集群是一个 P2P类型(使用 gossip 协议)的分布式系统,除了集群状态管理以外,其他所有的请求都可以发送到集群内任意一台节点上,这个节点可以自己找到需要转发给哪些节点,并且直接跟这些节点通信。所以,从网络架构及服务配置上来说,构建集群所需要的配置极其简单。在Elasticsearch 2.0 之前,无阻碍的网络下,所有配置了相同 cluster.name 的节点都自动归属到一个集群中。
2.0 版本之后,基于安全的考虑避免开发环境过于随便造成的麻烦,从 2.0 版本开始,默认的自动发现方式改为了单播(unicast)方式。配置里提供几台节点的地址,ES 将其视作gossip router 角色,借以完成集群的发现。由于这只是 ES 内一个很小的功能,所以 gossip router 角色并不需要单独配置,每个 ES 节点都可以担任。所以,采用单播方式的集群,各节点都配置相同的几个节点列表作为 router即可。
集群中节点数量没有限制,一般大于等于2个节点就可以看做是集群了。一般处于高性能及高可用方面来考虑一般集群中的节点数量都是3个及3个以上。
一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”。这个名字是重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群
一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。和集群类似,一个节点也是由一个名字来标识的,默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。
一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。
在一个集群里,只要你想,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。
一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。分片很重要,主要有两方面的原因:
1)允许你水平分割/扩展你的内容容量。
2)允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量。
至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由Elasticsearch管理的,对于作为用户的你来说,这些都是透明的。
在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。
复制之所以重要,有两个主要原因: 在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行。总之,每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制的数量,但是你事后不能改变分片的数量。
默认情况下,Elasticsearch中的每个索引被分片5个主分片和1个复制,这意味着,如果你的集群中至少有两个节点,你的索引将会有5个主分片和另外5个复制分片(1个完全拷贝),这样的话每个索引总共就有10个分片。
创建elasticsearch-cluster文件夹,在内部复制三个elasticsearch服务
修改elasticsearch-cluster\node*\config\elasticsearch.yml配置文件
#节点1的配置信息:
#集群名称,保证唯一
cluster.name: my-elasticsearch
#节点名称,必须不一样
node.name: node-1
#必须为本机的ip地址
network.host: 127.0.0.1
#服务端口号,在同一机器下必须不一样
http.port: 9200
#集群间通信端口号,在同一机器下必须不一样
transport.tcp.port: 9300
#设置集群自动发现机器ip集合
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]
#节点2的配置信息:
#集群名称,保证唯一
cluster.name: my-elasticsearch
#节点名称,必须不一样
node.name: node-2
#必须为本机的ip地址
network.host: 127.0.0.1
#服务端口号,在同一机器下必须不一样
http.port: 9201
#集群间通信端口号,在同一机器下必须不一样
transport.tcp.port: 9301
#设置集群自动发现机器ip集合
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]
#节点3的配置信息:
#集群名称,保证唯一
cluster.name: my-elasticsearch
#节点名称,必须不一样
node.name: node-3
#必须为本机的ip地址
network.host: 127.0.0.1
#服务端口号,在同一机器下必须不一样
http.port: 9202
#集群间通信端口号,在同一机器下必须不一样
transport.tcp.port: 9302
#设置集群自动发现机器ip集合
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]
双击elasticsearch-cluster\node*\bin\elasticsearch.bat
PUT localhost:9200/blog1
{"mappings": {"article": {"properties": {"id": {"type": "long","store": true,"index":"not_analyzed"},"title": {"type": "text","store": true,"index":"analyzed","analyzer":"standard"},"content": {"type": "text","store": true,"index":"analyzed","analyzer":"standard"}}}}
}
POST localhost:9200/blog1/article/1
{"id":1,"title":"ElasticSearch是一个基于Lucene的搜索服务器","content":"它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。"
}
注意配置跨域访问:
http.cors.enabled: true
http.cors.allow-origin: “*”注意:window10搭建集群,文件名字要简单和不能出现空格,要不然会出现文件加载不成功的情况
elasticsearch 集群无法启动出现如下提示 failed to send join request to master(删除data下面的数据)
Spring Data Elasticsearch是Spring Data项目下的一个子模块。
查看 Spring Data的官网:http://projects.spring.io/spring-data/
Spring Data ElasticSearch 基于 spring data API 简化 elasticSearch操作,将原始操作elasticSearch的客户端API 进行封装 。Spring Data为Elasticsearch项目提供集成搜索引擎。Spring Data Elasticsearch POJO的关键功能区域为中心的模型与Elastichsearch交互文档和轻松地编写一个存储库数据访问层。
官方网站:http://projects.spring.io/spring-data-elasticsearch
###1.SpringData ES说明
在后期项目开发中,XML整合方式已经落伍了,目前市面上的项目都是拥抱SpringBoot框架,来实现项目的构建,那么课程就使用SpringBoot完成Spring Data ES的整合。就不用管传统applicationContext.xml整合方式
easygo com.bruce.easygo 1.0-SNAPSHOT 4.0.0 easygo-search-service org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test com.bruce.easygo easygo-pojo 1.0-SNAPSHOT compile org.springframework.cloud spring-cloud-starter-netflix-eureka-client com.bruce.easygo easygo-commons 1.0-SNAPSHOT compile org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import
server.port=9009# 配置Eureka信息
# 服务的名字,注册到注册中心的名字,后期消费者来根据名字调用服务 可以重复
spring.application.name=easygo-search-service
# EurekaServer地址
eureka.client.service-url.defaultZone=http://127.0.0.1:9001/eureka
# 当调用getHostname获取实例的hostname时,返回ip而不是host名称
eureka.instance.prefer-ip-address=true
# 指定自己的ip信息,不指定的话会自己寻找
eureka.instance.ip-address=127.0.0.1
# 执行当前服务的应用ID 不可以重复 标识的是每一个具体的的服务
eureka.instance.instance-id=easygo-search-service-9009
org.springframework.boot spring-boot-starter-data-elasticsearch
# Spring Data elasticsearch配置
spring.data.elasticsearch.cluster-name=elasticsearch
# 连接地址
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
#设置连接超时时间
spring.data.elasticsearch.properties.transport.tcp.connect_timeout=120s
package com.easygo.test;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.test.context.junit4.SpringRunner;import javax.annotation.Resource;/*** @BelongsProject: easygo* @BelongsPackage: com.easygo.test* @Author: bruceliu* @QQ:1241488705* @CreateTime: 2020-04-10 15:20* @Description: TODO*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestES {//如果要操作Redis,需要获取一个 RedisTemplate//如果要操作ES,需要获取 ElasticsearchTemplate,天下我有!@ResourceElasticsearchTemplate elasticsearchTemplate;/*** 01-测试工具类*/@Testpublic void testConn(){System.out.println("ES操作的工具类:"+elasticsearchTemplate);}/***02-创建一个索引库? Goods索引库,实际的项目搜索的是商品数据!*/@Testpublic void testCreateIndex(){//创建索库elasticsearchTemplate.createIndex("goodsindexs");System.out.println("测试创建索引库");}}
package com.easygo.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Goods implements Serializable {private static final long serialVersionUID = 8972263575352384971L;private Integer id;private String seller_id; //卖家IDprivate String goods_name; //商品的标题private Integer default_item_id; //默认上架商品IDprivate String audit_status; //当前状态private String is_marketable; //是否上架private Integer brand_id; //商品的品牌IDprivate String caption; //商品的副标题private Integer category1_id;private Integer category2_id;private Integer category3_id;private String small_pic; //商品的小图private Double price; //商品的价格private Integer type_template_id;private String is_enable_spec;private String is_delete;
}
org.springframework.boot spring-boot-starter-data-elasticsearch
package com.easygo.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;import java.io.Serializable;@Data
@NoArgsConstructor
@AllArgsConstructor
//文档对象是(索引库名,类型)
@Document(indexName = "goodsindex",type = "goods")
public class Goods implements Serializable {private static final long serialVersionUID = 8972263575352384971L;//字段的配置 类型 是否索引 是否存储@Field(store = true,index = false,type = FieldType.Integer)private Integer id;private String seller_id; //卖家ID@Field(store = true,analyzer = "ik_max_word",index = true,searchAnalyzer ="ik_max_word",type = FieldType.Text)private String goods_name; //商品的标题private Integer default_item_id; //默认上架商品IDprivate String audit_status; //当前状态private String is_marketable; //是否上架@Field(store = true,index = false,type = FieldType.Integer)private Integer brand_id; //商品的品牌ID@Field(store = true,analyzer = "ik_max_word",index = true,searchAnalyzer ="ik_max_word",type = FieldType.Text)private String caption; //商品的副标题private Integer category1_id;private Integer category2_id;private Integer category3_id;@Field(store = true,index = false,type = FieldType.Text)private String small_pic; //商品的小图@Field(store = true,index = false,type = FieldType.Double)private Double price; //商品的价格private Integer type_template_id;private String is_enable_spec;private String is_delete;
}
package com.easygo.test;import com.easygo.client.GoodsClient;
import com.easygo.pojo.Goods;
import com.easygo.service.GoodsService;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.junit4.SpringRunner;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;@RunWith(SpringRunner.class)
@SpringBootTest
public class TestES {//如果要操作Redis,需要获取一个 RedisTemplate//如果要操作ES,需要获取 ElasticsearchTemplate,天下我有!@ResourceElasticsearchTemplate elasticsearchTemplate;@ResourceGoodsClient goodsClient;@AutowiredGoodsService goodsService;/*** 01-测试工具类*/@Testpublic void testConn(){System.out.println("ES操作的工具类:"+elasticsearchTemplate);}/***02-创建一个索引库? Goods索引库,实际的项目搜索的是商品数据!*/@Testpublic void testCreateIndex(){//创建索库,索引库的名字是??elasticsearchTemplate.createIndex(Goods.class);System.out.println("测试创建索引库成功~");elasticsearchTemplate.putMapping(Goods.class);//因为类上面有注解System.out.println("创建Goods的Mapping完成");}/*** 新增数据库中的数据到ES索引库中*/@Testpublic void testAddDocument(){List goodsList = goodsClient.getGoods(1);for (Goods goods : goodsList) {System.out.println("正在导入:"+goods);}goodsService.saveDocuments(goodsList);System.out.println("批量新增索引库数据成功......");}/*** 根据ID查询*/@Testpublic void testgetDocumentById(){Goods goods = goodsService.getDocumentById(149187842868047L);System.out.println("查询的对象是:"+goods);}/*** 根据ID更新*/@Testpublic void testUpdateById(){Goods goods = goodsService.getDocumentById(149187842867986L);System.out.println("查询的原对象是:"+goods);goods.setGoods_name("阿玛尼装逼神器");goods.setCaption("阿玛尼装逼神器,泡妞必备,值得哟拥有");goods.setPrice(250.0);goodsService.updateDocumentById(goods);System.out.println("更新document");}/*** 根据ID删除*/@Testpublic void testDeleteByid(){goodsService.deleteDocumentById(149187842867914L);System.out.println("删除成功");}/*** 删除所有*/@Testpublic void testDeleteAll(){goodsService.deleteAllDocument();System.out.println("全部干光了");}/*** 测试关键词查询01 条件查询分页*/@Testpublic void testQuery1(){int pageIndex=4; //当前页码int pageSize=3; //页码大小String keywords="手机"; //用户输入的关键词//构建一个查询对象NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()//设置查询条件,可以构建多个条件.withQuery(QueryBuilders.queryStringQuery(keywords).defaultField("goods_name"))//设置分页的信息,页码从0开始计算.withPageable(PageRequest.of(pageIndex - 1, pageSize)).build();//条件查询分页,返回分页对象AggregatedPage page = elasticsearchTemplate.queryForPage(searchQuery, Goods.class);System.out.println("当前页码:"+pageIndex);System.out.println("页面大小:"+pageSize);System.out.println("总页数:"+page.getTotalPages());System.out.println("总条数:"+page.getTotalElements());System.out.println("每页到的数据是:");List goodsList = page.getContent();for (Goods goods : goodsList) {System.out.println(goods);}}/*** 多条件搜索 分页*/@Testpublic void testtestQuery2(){int pageIndex=1; //当前页码int pageSize=3; //页码大小String keywords="火爆"; //用户输入的关键词NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();if(keywords!=null&&!"".equals(keywords)){builder.withQuery(QueryBuilders.multiMatchQuery(keywords, "goods_name", "caption"));}//设置分页的信息,页码从0开始计算builder.withPageable(PageRequest.of(pageIndex - 1, pageSize));NativeSearchQuery searchQuery = builder.build();//条件查询分页,返回分页对象AggregatedPage page = elasticsearchTemplate.queryForPage(searchQuery, Goods.class);System.out.println("当前页码:"+pageIndex);System.out.println("页面大小:"+pageSize);System.out.println("总页数:"+page.getTotalPages());System.out.println("总条数:"+page.getTotalElements());System.out.println("每页到的数据是:");List goodsList = page.getContent();for (Goods goods : goodsList) {System.out.println(goods);}}
接下来准备讲解商品搜索,商品搜索用到了ES,所以本文先讲解在Docker下搭建ES与ES的可视化工具Kibana
注意:
PS:跑起ES和ElasticSearch占内存3.4G,如下:
调高JVM线程数限制数量(一定要先设置!)
vim /etc/sysctl.conf
# 添加这个
vm.max_map_count=262144
# 保存后执行这个命令
sysctl -p
在使用docker 拉去最新的镜像时,会提示如下错误:
这里错误的意思是docker需要我们指定下载镜像的版本号。
但是我们想下载最新的版本号,该如何得知最新的版本号呢?
我们可以登录docker hub:https://hub.docker.com/u/library,搜索自己想要下载的镜像名:
点击搜索出来列表里的镜像,进入详情页面,点击Tags,第一个镜像就是最新的,可以看出,最新的镜像版本号为7.6.1:
重新使用docker执行版本号拉取,可以看到正在下载:
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -d elasticsearch:7.6.1docker run --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -v /soft/es/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /soft/es/data:/usr/share/elasticsearch/data -v /root/elasticsearch/plugins:/usr/share/elasticsearch/plugins -d elasticsearch:7.6.1
浏览器访问http://你的ip:9200/
,可以看到访问成功。
docker exec -it elasticsearch /bin/bash
cd /usr/share/elasticsearch/config/
vi elasticsearch.yml
在elasticsearch.yml的文件末尾加上:
http.cors.enabled: true
http.cors.allow-origin: "*"network.host: 0.0.0.0
exit
docker restart elasticsearch
elasticsearch
的版本和ik分词器的版本需要保持一致,不然在重启的时候会失败。ik分词器地址:https://github.com/medcl/elasticsearch-analysis-ik/releases
docker exec -it elasticsearch /bin/bash
cd /usr/share/elasticsearch/plugins/
elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.1/elasticsearch-analysis-ik-7.6.1.zip
exit
docker restart elasticsearch
exit
docker restart elasticsearch
根据一个搜索词,检索出所有包含该词的数据
例如:用户在搜索框输入一个词,客户端软件发送一个请求到后台,后台通过sql语句从数据库中找出相关条目(数据库会一条一条的对比),这就是一个最简单搜索原型
select * from product where title like ‘%关键词%’;
1, 当数据量很大时,假如500G, 效率低。从用户角度,从点击搜索按钮到看到搜索结果可能要很长时间,1小时?2小时?用户疯掉
2, 当数据量达到1T,一台电脑已经放不下了,这时候就需要多台,这就是分布式。这时候数据就在不同的服务器了,一个客户端不可能去请求每台机器,所以就需要一个管理员角色,负责把客户端请求分发到每台机器,同时汇总结果返回给客户端
ES
Elasticsearch简称ES,是一种以JSON格式进行数据存储的分布式搜索引擎
在一个电商平台项目中,会有非常多的商品,如果使用以往的MySQL进行模糊查询,需要比较长的等待时间,对数据库压力比较大,ES中使用倒排索引,查询效率非常高,可以减轻MySQL压力,而且还能进行分词查询和高亮处理,MySQL是做不到的。
不是所有的数据都会存在ES中,像商品规格表这种不常修改,且不会去模糊查询的放到缓存中就好了。如果要同步多张表到ES中,就要在Logstath中配置多个mysql的配置文件,对应到mysql的每张表
倒排索引
:将文档中的关键字和文档ID进行记录,在搜索关键词的时候,直接到指定的文档ID中查找数据
分词查询
:比如去搜索腾讯课堂,ES有自带的分词器会将腾讯课堂分词为腾讯和课堂,然后通过倒排索引去查找指定的文档id。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bhdj2Bk8-1678390025375)(F:\javaee教案\第四阶段\Day20220329-微服务项目-ES面试题解析-下午\分布式搜索-专题\面试题\ES(elasticsearch)]搜索引擎面试题.assets\1624943064635.png)
ES中有一个常用的英文分词器analyze,它只能够识别英文,对英文进行分词,对中文是每个字符都是独立的。如果要对中文进行分词识别,可以安装ik中文分词器插件。
盘古分词、庖丁解牛分词 IK分词…
迪丽热巴 蔡徐坤 IK分词可以自定义分词
(1)六个数字类型:byte、short、integer、long、float、double
(2)布尔型:boolean
(3)字符型:string(text分词,keyword不分词)
(4)二进制型:binary
ES集群原理类似Redis分片集群原理,采用分片集群+主从复制+请求转发的方式集群
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eFALMgSY-1678390025376)(F:\javaee教案\第四阶段\Day20220329-微服务项目-ES面试题解析-下午\分布式搜索-专题\面试题\ES(elasticsearch)]搜索引擎面试题.assets\1624943076118.png)
当第一次启动节点的时候,logstath把MySQL中的数据用JSON格式插入到ES中,并修改MySQL中的updatetime字段为系统时间,之后每隔一分钟去MySQL中查updatetime大于上次同步时间的数据,增量同步到ES中。
延迟!!
强一致性:上架的时候商品数据同步过去,下架的时候也同步!
同步操作 操作MySql数据库之后 再同步ES索引库! 一致性~
ELK是Elasticsearch+Logstath+Kibana,Logstath用来收集日志,并且把日志以Json格式输出到ES中,通过kibana操作数据
增:POST /mymayikt/user/1,不指定ID会随机分配一个ID
删:DELETE /mymayikt/user/1
改:PUT /mymayikt/user/1
查:GET /mymayikt/user/1
查询数据:_search
查询设置:_setting
查询映射:_mapping
条件查询:GET /mymayikt/user/_search?q=age:21
范围查询:GET /mymayikt/user/_search?q=age[30 TO 60]
降序排列:GET /mymayikt/user/_search?q=age[30 TO 60]&sort=age:desc
分页查询:GET /mymayikt/user/_search?q=age[30 TO 60]&sort=age:desc&from=0&size=2
精确查询:
GET mymayikt/user/_search
{
“query”: {
“term”: {
“name”: “xiaoming”
}
}
}
分词查询:
GET /mymayikt/user/_search
{
“from”: 0,
“size”: 2,
“query”: {
“match”: {
“car”: “奥迪a61”
}
}
}
分词器查询:
http://192.168.212.181:9200/_analyze
{
“analyzer”: “ik_smart”,
“text”: “奥迪”
}
Spring Data ES 和SpringBoot整合
Elasticsearch集群由一个主节点(可以有多个备选主节点)和多个数据节点组成。
其中主节点负责创建、删除索引、分配分片、追踪集群中的节点状态等工作,即调度节点,计算压力较轻;
数据节点负责数据存储和具体操作,如执行搜索、聚合等任务,计算压力较大。
正常情况下,当主节点无法工作时,会从备选主节点中选举一个出来变成新主节点,原主节点回归后变成备选主节点。但有时因为网络抖动等原因,主节点没能及时响应,集群误以为主节点下线了,选举了一个新主节点,此时一个Elasticsearch集群中有了两个主节点,其他节点不知道该听谁的调度,这时就发生了"脑裂"现象,通俗点就是“精神分裂”。
es脑裂:一个大的es集群分裂成了多个小的集群。
比如有 a b c d 四个es
a b c d 之间选取一个master,比如master是a。
若某时刻 c d 访问不到a ,b能访问到a。
c d之间会重新选举一个master。
这样整个a b c d的es集群就会分裂为a b 和 b c 两个集群。
(1)网络抖动。
由于是内网通信、网络通信等问题造成部分节点认为master node挂掉, 然后另选master node的情况可能性较小;可以通过检查Ganglia集群监控,没有发现异常的内网流量, 故此原因可以排除。
而外网的网络出现问题的可能性更大,更有可能造成“脑裂”现象。
(2)节点负载。
如果主节点同时承担数据节点的工作,可能会因为工作负载大而导致对应的Elasticsearch实例停止响应。此外,由于数据节点上的Elasticsearch进程占用的内存较大, 较大规模的内存回收操作(GC)也能造成Elasticsearch进程失去响应。所以,该原因出现“脑裂”现象的可能性更大。
(3)内存回收。
由于数据节点上的Elasticsearch进程占用的内存较大,较大规模的内存回收操作也能造成Elasticsearch进程失去响应。
防止脑裂: 主节点就是只当主节点,不要又当主节点 又当数据节点!
(1)不要把主节点同时设为数据节点,即node.master
和node.data
不要同时为true
。
主节点配置为:
node.master: true 候选主节点(有当主节点的潜质)
node.data: false 不作为工作节点
从节点配置为:
node.master: false
node.data: true
(2)将节点响应超时discovery.zen.ping_timeout
稍稍设置长一些(默认是3秒)。默认情况下, 一个节点会认为, 如果master节点在 3 秒之内没有应答, 那么这个节点就是挂掉了, 而增加这个值, 会增加节点等待响应的时间, 从一定程度上会减少误判。
(3)discovery.zen.minimum_master_nodes
的默认值是1,该参数表示, 一个节点需要看到的具有master节点资格的最小数量, 然后才能在集群中做操作,即重新选举主节点。官方的推荐值是(N/2)+1
,其中 N 是具有 master资格的节点的数量,即只有超过(N/2)+1
个主节点同意,才能重新选举主节点。
实际解决办法
最终考虑到资源有限,解决方案如下:
增加一台物理机,这样,一共有了三台物理机。在这三台物理机上,搭建了6个ES的节点,三个data节点,三个master节点(每台物理机分别起了一个data和一个master),3个master节点,目的是达到(n/2)+1等于2的要求,这样挂掉一台master后(不考虑data),n等于2,满足参数,其他两个master节点都认为master挂掉之后开始重新选举,
master节点上
node.master = true
node.data = false
discovery.zen.minimum_master_nodes = 2
data节点上
node.master = false
node.data = true
方案分析
1.角色分离后,当集群中某一台节点的master进程意外挂掉了,或者因负载过高停止响应,终止掉的master进程很大程度上不会影响到同一台机器上的data进程,即减小了数据丢失的可能性。
2.discovery.zen.minimum_master_nodes
设置成了2(3/2+1)当集群中两台机器都挂了或者并没有挂掉而是处于高负载的假死状态时,仅剩一台备选master节点,小于2无法触发选举行为,集群无法使用,不会造成分片混乱的情况。
而图一,两台节点假死,仅剩一台节点,选举自己为master,当真正的master苏醒后,出现了多个master,并且造成查询不同机器,查到了结果不同的情况。
以上的解决方法只能是减缓这种现象的发生, 并没有从根本上杜绝。
如果发生了脑裂, 如何解决?
所以怎么从脑裂中恢复?
第一个建议是给所有数据重新索引。
第二, 如果脑裂发生了, 要十分小心的重启你的集群。 停掉所有节点并决定哪一个节点第一个启动。 如果需要, 单独启动每个节点并分析它保存的数据。 如果不是有效的, 关掉它, 并删除它数据目录的内容( 删前先做个备份) 。 如果你找到了你想要保存数据的节点, 启动它并且检查日志确保它被选为主节点。 这之后你可以安全的启动你集群里的其他节点了。
在单台ES服务器节点上,随着业务量的发展索引文件慢慢增多,会影响到效率和内存存储问题等。
如果使用ES集群,会将单台服务器节点的索引文件使用分片技术,分布式的存放在多个不同的物理机器上,从而可以实现高可用、容错性等。
es核心存放的是索引。
将数据拆分成多台节点进行存放。
ES是如何解决高并发
ES是一个分布式全文检索框架,隐藏了复杂的处理机制,内部使用 分片机制、集群发现、分片负载均衡请求路由。
Shards 分片:代表索引分片,es可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改。
Replicas分片:代表索引副本,es可以设置多个索引的副本,副本的作用一是提高系统的容错性,当某个节点某个分片损坏或丢失时可以从副本中恢复。二是提高es的查询效率,es会自动对搜索请求进行负载均衡。
1、每个索引会被分成多个分片shards进行存储,默认创建索引是分配5个分片进行存储**,每个分片都会分布式部署在多个不同的节点上进行部署**,该分片成为primary shards 主分片。
在ES 中每一个主的分片都有一个对应的副分片,这里总共由10分片。
注意:索引的主分片数量定义好后,不能被修改。主分片数量一旦定义好之后就不能修改,但是副分片的数量是可以修改的。
2、每一个主分片为了实现高可用,都会有自己对应的备分片,主分片对应的备分片不能存放同一台服务器上,主分片可以和其他备分片存放在同一个node节点上。
单台 ES 服务器中是没有副分片的。
documnet routing(数据路由)
当客户端发起创建document的时候,es需要确定这个document放在该index哪个shard上。这个过程就是数据路由。
路由算法:shard = hash(routing) % number_of_primary_shards
如果number_of_primary_shards
在查询的时候取余发生的变化,无法获取到该数据
Cluster:代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的。es的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来说的,因为从外部来看es集群,在逻辑上是个整体,你与任何一个节点的通信和与整个es集群通信是等价的。
Shards:代表索引分片,es可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改。
replicas:代表索引副本,es可以设置多个索引的副本,副本的作用一是提高系统的容错性,当某个节点某个分片损坏或丢失时可以从副本中恢复。二是提高es的查询效率,es会自动对搜索请求进行负载均衡。
Recovery:代表数据恢复或叫数据重新分布,es在有节点加入或退出时会根据机器的负载对索引分片进行重新分配,挂掉的节点重新启动时也会进行数据恢复。
协调节点收到文档后通过路由计算找到合适的分片,并向目标分片发送写请求
当分片所在的节点接收到来自协调节点的请求后,会将请求写入到 Memory Buffer,然后定时(默认是每隔 1 秒)写入到 Filesystem Cache,这个从 Memory Buffer 到 Filesystem Cache 的过程就叫做 refresh;(写入到Filesystemcache后即可被检索到)
在某些情况下,存在 Momery Buffer 和 Filesystem Cache 的数据可能会丢失, ES 是通过 translog的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到 translog 中,当 Filesystemcache 中的数据写入到磁盘中时,才会清除掉,这个过程叫做 flush;
在 flush 过程中,内存中的缓冲将被清除,内容被写入一个新段,段的 fsync 将创建一个新的提交点,并将内容刷新到磁盘,旧的 translog 将被删除并开始一个新的 translog。
flush 触发的时机是定时触发(默认 30 分钟)或者 translog 变得太大(默认为 512M)时;
ES底层是基于Lucene,最核心的概念就是Segment(段),每个段本身就是一个倒排索引
搜索被执行成一个两阶段过程,我们称之为 Query Then Fetch:
搜索拆解为“query then fetch” 两个阶段。
query 阶段的目的:定位到位置,但不取。
步骤拆解如下:
(1)假设一个索引数据有 5 主+1 副本 共 10 分片,一次请求会命中(主或者副本分片中)的一个。
(2)每个分片在本地进行查询,结果返回到本地有序的优先队列中。
(3)第 2)步骤的结果发送到协调节点,协调节点产生一个全局的排序列表。
fetch 阶段的目的:取数据。
路由节点获取所有文档,返回给客户端。
“脑裂”问题可能的成因:
elasticsearch-head 插件。
通过 Kibana 监控 Elasticsearch。你可以实时查看你的集群健康状态和性能,也可以分析过去的集群、索引和节点指标
(1)查询 : Elasticsearch 允许执行和合并多种类型的搜索 — 结构化、非结构化、地理位置、度量指标 — 搜索方式随心而变。
(2)分析 : 找到与查询最匹配的十个文档是一回事。但是如果面对的是十亿行日志,又该如何解读呢?Elasticsearch 聚合让您能够从大处着眼,探索数据的趋势和模式。
(3)速度 : Elasticsearch 很快。真的,真的很快。
(4)可扩展性 : 可以在笔记本电脑上运行。 也可以在承载了 PB 级数据的成百上千台服务器上运行。
(5)弹性 : Elasticsearch 运行在一个分布式的环境中,从设计之初就考虑到了这一点。
(6)灵活性 : 具备多个案例场景。数字、文本、地理位置、结构化、非结构化。所有的数据类型都欢迎。
(7)HADOOP & SPARK : Elasticsearch + Hadoop
https://www.modb.pro/db/390323