admin管理员组

文章数量:1570217

文章目录

  • ES简介
    • 1.什么是ElasticSearch
    • 2.ElasticSearch的使用案例
    • 3.ElasticSearch对比Solr
  • ElasticSearch环境搭建
    • 1. 下载ES压缩包
    • 2. 安装ES服务
    • 3. 启动ES服务
    • 3. 安装ES的图形化界面插件
  • ES术语
    • 1.概述
    • 2.索引 index
    • 3.类型 type
    • 4.字段Field
    • 5.映射 mapping
    • 6.文档 document
    • 7. 接近实时 NRT
    • 8. 集群 cluster
    • 9.节点 node
    • 10.分片和复制 shards&replicas
  • ES客户端
    • 1.引言
    • 2.安装Postman工具
    • 3.安装Postman工具
    • 4.使用Postman工具进行Restful接口访问
      • 4.1.ElasticSearch的接口语法
      • 4.2 创建索引index和映射mapping
      • 4.3 创建索引后设置Mapping
      • 4.4 删除索引index
      • 4.5 创建文档document
      • 4.6 修改文档document
      • 4.7 删除文档document
      • 4.8 查询文档-根据id查询
      • 4.9 查询文档-querystring查询
      • 4.10 查询文档-term查询
  • IK分词器的集成使用
    • 1.查询存在问题分析
    • 2.IK分词器简介
    • 3. IK分词器的安装
    • 4.IK分词器测试
    • 6. 修改索引映射mapping
      • 6.1 重建索引
      • 6.2 再次测试queryString查询
      • 6.3 再次测试term测试
  • ES集群配置
    • 1.引言
    • 2.集群的相关概念
    • 2.2.集群 cluster
    • 2.3.节点 node
    • 2.4.分片和复制 shards&replicas
    • 3.集群的搭建
      • 3.1.准备三台elasticsearch服务器
      • 3.2.修改每台服务器配置
        • node1节点:
        • node2节点:
        • node3节点:
      • 3.3.启动各个节点服务器
        • 启动节点1:
        • 启动节点2:
        • 启动节点3:
      • 3.4.集群测试
        • 添加索引和映射
        • 添加文档
        • 使用elasticsearch-header查看集群情况
  • Springboot整合Spring Data Es
    • 1. 什么是Spring Data ElasticSearch
    • 2.SpringData ES整合步骤
      • 2.1.准备搜索的服务工程
      • 2.2.项目中引入依赖
      • 2.3.配置Spring Data ES的配置信息
      • 2.4.测试ES的连接
    • 3.使用面向对象思想创建索引库和Mapping
      • 3.1.准备一个实体类,索引库对应
      • 3.2. 给实体类加一些注解(Spring Data ES注解),配置Mapping规则的注解
  • Docker部署ES
    • 引言
    • 1.docker下安装ElasticSearch
    • 2.下载ES镜像
    • 3.查看镜像(注意下一步用到版本号):
    • 4.启动ES
    • 5.浏览器访问
    • 6.解决跨域
      • 6.1.修改配置,解决跨域访问问题
      • 6.2.保存退出后,重启容器
    • 7.安装IK分词器
      • 7.1.进入容器
      • 7.2.下载安装ik分词器
      • 7.3.重启
  • ES综合运用
    • 1.传统搜索?
    • 2.传统搜索面临的问题
    • 3.什么是Elasticsearch?
    • 4.为什么不使用MySQL查询,而要用ES呢?
    • 5.什么时候使用ES?什么时候使用MySQL呢?
    • 6.什么是倒排索引?什么是分词查询?
    • 7.分词器有哪些?
    • 8.ES中有哪些常用数据类型?
    • 9.ES集群方式
    • 10.ES如何与MySQL实现数据同步? 一致性问题?
    • 11.ELK是什么?如何实现日志收集?
    • 12.ES和MySQL专业名词比较
    • 13.常用的ES语句有哪些?
    • 14.ES的JavaAPI
  • ES脑裂
    • 1.什么是Elasticsearch集群脑裂 split brain
    • 2.产生的原因
    • 3.解决方法
    • 4.方案分析
    • 5.总结
  • ES综合
    • 一、基础概念
    • 1.1、什么是Elasticsearch
    • 1.2、为什么要用Elasticsearch
    • 二、底层算法相关
    • 2.1、lucene与ES
    • 2.2、ES算分的算法是什么?
    • 2.3、倒排索引
    • 2.4、正排索引
      • 2.4.1、为什么要用正排索引?
      • 2.4.2、ES中正排索引有哪几种?
      • 2.4.3、ES是如何利用正排和倒排索引的?
      • 2.4.4、如何自定义排序?
      • 2.4.5、关闭正排索引
    • 2.5、高并发下如何保证读写一致?
    • 三、使用语法相关
    • 3.1、新增文档create和index的区别
    • 3.2、更新文档update与index的区别
    • 3.3、text 和 keyword类型的区别
    • 3.4、query 和 filter 的区别?
    • 四、集群架构相关
    • 4.1、集群、主分片、副本分片的关系
    • 4.2、ES是如何实现master选举的?
    • 4.3、如何解决ES集群的脑裂问题
    • 4.4、集群健康度yellow、red是什么原因
    • 4.5、为什么主分片数不能修改
    • 4.6、写数据底层原理
      • 4.6.1、ES为什么是近实时的?
      • 4.6.2、ES如何保证断电数据不丢失?
      • 4.6.3、为什么删除文档,并不会立即释放空间?
    • 4.7、ES集群写入和搜索数据过程
      • 4.7.1、写入数据
      • 4.7.2、搜索数据
    • 4.8、跨集群搜索的问题
      • 4.8.1、深度分页
      • 4.8.2、相关性算分
    • 五、ELK相关
    • 5.1、什么是ELK
    • 5.2、配置注意
    • 六、优化手段
    • 6.1、冷热分离架构
    • 6.2、如何设计分片数、节点数、节点的设备要求
      • 6.3.1、服务器节点要求
      • 6.3.2、分片
    • 6.3、提升集群写性能
    • 6.4、提升集群读性能
    • 6.5、提升搜索准确度
      • 1、选用合适的分词器
  • 重学ElasticSearch (ES) 系列
  • 总结
    • ES的搜索
    • ID排序![在这里插入图片描述](https://i-blog.csdnimg/blog_migrate/2ef23661b6f404228eb4cd83d8a20f13.png#pic_center)

ES简介

1.什么是ElasticSearch

Elaticsearch,简称为es, es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。es也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。

Lucence 比较老牌,API比较繁琐

Solr 基于Lucence 封装而来

Elaticsearch ES 分布式全文检索引擎

2.ElasticSearch的使用案例

  • 2013年初,GitHub抛弃了Solr,采取ElasticSearch 来做PB级的搜索。 “GitHub使用ElasticSearch搜索20TB的数据,包括13亿文件和1300亿行代码”
  • 维基百科:启动以elasticsearch为基础的核心搜索架构
  • SoundCloud:“SoundCloud使用ElasticSearch为1.8亿用户提供即时而精准的音乐搜索服务”
  • 百度:百度目前广泛使用ElasticSearch作为文本数据分析,采集百度所有服务器上的各类指标数据及用户自定义数据,通过对各种数据进行多维分析展示,辅助定位分析实例异常或业务层面异常。目前覆盖百度内部20多个业务线(包括casio、云分析、网盟、预测、文库、直达号、钱包、风控等),单集群最大100台机器,200个ES节点,每天导入30TB+数据
  • 新浪使用ES 分析处理32亿条实时日志
  • 阿里使用ES 构建挖财自己的日志采集和分析体系

3.ElasticSearch对比Solr

当单纯的对已有数据进行搜索时,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环境搭建

1. 下载ES压缩包

ElasticSearch分为Linux和Window版本,基于我们主要学习的是ElasticSearch的Java客户端的使用,所以我们课程中使用的是安装较为简便的Window版本,项目上线后,公司的运维人员会安装Linux版的ES供我们连接使用。

ElasticSearch的官方地址: https://www.elastic.co/products/elasticsearch


在资料中已经提供了下载好的5.6.8的压缩包:

2. 安装ES服务

Window版的ElasticSearch的安装很简单,类似Window版的Tomcat,解压开即安装完毕,解压后的ElasticSearch
的目录结构如下:

修改elasticsearch配置文件:config/elasticsearch.yml,增加以下两句命令:

http.cors.enabled: true
http.cors.allow-origin: "*"

此步为允许elasticsearch跨越访问,如果不安装后面的elasticsearch-head是可以不修改,直接启动。

3. 启动ES服务

点击ElasticSearch下的bin目录下的elasticsearch.bat启动,控制台显示的日志信息如下:

注意:9300是tcp通讯端口,集群间和TCPClient都执行该端口,9200是http协议的RESTful接口 。

通过浏览器访问ElasticSearch服务器,看到如下返回的json信息,代表服务启动成功:

注意:ElasticSearch是使用java开发的,且本版本的es需要的jdk版本要是1.8以上,所以安装ElasticSearch之前保证JDK1.8+安装完毕,并正确的配置好JDK环境变量,否则启动ElasticSearch失败。

3. 安装ES的图形化界面插件

ElasticSearch不同于Solr自带图形化界面,我们可以通过安装ElasticSearch的head插件,完成图形化界面的效果,完成索引数据的查看。安装插件的方式有两种,在线安装和本地安装。本文档采用本地安装方式进行head插件的安装。elasticsearch-5-*以上版本安装head需要安装node和grunt

1)下载head插件:https://github/mobz/elasticsearch-head

在资料中已经提供了elasticsearch-head-master插件压缩包:
2)将elasticsearch-head-master压缩包解压到任意目录,但是要和elasticsearch的安装目录区别开

3)下载nodejs:https://nodejs/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服务,重新连接。

ES术语

1.概述

Elasticsearch是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在Elasticsearch中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。Elasticsearch比传统关系型数据库如下:

Relational DB(Mysql) ‐> Databases(数据库) ‐> Tables(表) ‐> Rows(行) ‐> Columns(列)

Elasticsearch ‐> Indices(索引库) ‐> Types(类型) ‐> Documents(文档) ‐> Fields(字段)

2.索引 index

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

3.类型 type

在一个索引中,你可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定。通常,会为具有一组共同字段的文档定义一个类型。比如说,我们假设你运营一个博客平台并且将你所有的数据存储到一个索引中。在这个索引中,你可以为用户数据定义一个类型,为博客数据定义另一个类型,当然,也可以为评论数据定义另一个类型。

4.字段Field

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

5.映射 mapping

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

6.文档 document

一个文档是一个可被索引的基础信息单元。比如,你可以拥有某一个客户的文档,某一个产品的一个文档,当然,也可以拥有某个订单的一个文档。文档以JSON(Javascript Object Notation)格式来表示,而JSON是一个到处存在的互联网数据交互格式。

在一个index/type里面,你可以存储任意多的文档。注意,尽管一个文档,物理上存在于一个索引之中,文档必须被索引/赋予一个索引的type。

7. 接近实时 NRT

Elasticsearch是一个接近实时的搜索平台。这意味着,从索引一个文档直到这个文档能够被搜索到有一个轻微的延迟(通常是1秒以内)

8. 集群 cluster

一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”。这个名字是重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群

9.节点 node

一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。和集群类似,一个节点也是由一个名字来标识的,默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。

一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。

在一个集群里,只要你想,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。

10.分片和复制 shards&replicas

一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。分片很重要,主要有两方面的原因:

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

至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由Elasticsearch管理的,对于作为用户的你来说,这些都是透明的。

在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。
复制之所以重要,有两个主要原因: 在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行。总之,每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制的数量,但是你事后不能改变分片的数量。

默认情况下,Elasticsearch中的每个索引被分片5个主分片和1个复制,这意味着,如果你的集群中至少有两个节点,你的索引将会有5个主分片和另外5个复制分片(1个完全拷贝),这样的话每个索引总共就有10个分片。

ES客户端

1.引言

实际开发中,主要有三种方式可以作为elasticsearch服务的客户端:
第一种,elasticsearch-head插件
第二种,使用elasticsearch提供的Restful接口直接访问第三种,使用elasticsearch提供的API进行访问

2.安装Postman工具

Postman中文版是postman这款强大网页调试工具的windows客户端,提供功能强大的Web API & HTTP 请求调试。软件功能非常强大,界面简洁明晰、操作方便快捷,设计得很人性化。Postman中文版能够发送任何类型的HTTP 请求 (GET, HEAD, POST, PUT…),且可以附带任何数量的参数。

3.安装Postman工具

Postman官网:https://www.getpostman
课程资料中已经提供了安装包

4.使用Postman工具进行Restful接口访问

4.1.ElasticSearch的接口语法

curl ‐X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' ‐d '<BODY>

其中:

参数解释
VERB适当的 HTTP 方法谓词 : GETPOSTPUTHEAD 或者 DELETE
PROTOCOLhttp 或者 https(如果你在 Elasticsearch 前面有一个 https 代理)
HOSTElasticsearch 集群中任意节点的主机名,或者用 localhost 代表本地机器上的节点。
PORT运行 Elasticsearch HTTP 服务的端口号,默认是 9200
PATHAPI 的终端路径(例如 _count 将返回集群中文档数量)。Path 可能包含多个组件,例如:_cluster/stats_nodes/stats/jvm
QUERY_STRING任意可选的查询字符串参数 (例如 ?pretty 将格式化地输出 JSON 返回值,使其更容易阅读)
BODY一个 JSON 格式的请求体 (如果请求需要的话)

4.2 创建索引index和映射mapping

请求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查看:

4.3 创建索引后设置Mapping

我们可以在创建索引时设置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截图

4.4 删除索引index

请求url:

DELETE		localhost:9200/blog1

postman截图:

elasticsearch-head查看:

4.5 创建文档document

请求url:

POST	localhost:9200/blog1/article/1

请求体:

{
	"id":1,
	"title":"ElasticSearch是一个基于Lucene的搜索服务器",
	"content":"它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。"
}

postman截图:
elasticsearch-head查看:

4.6 修改文档document

请求url:

POST	localhost:9200/blog1/article/1

请求体:

{
	"id":1,
	"title":"【修改】ElasticSearch是一个基于Lucene的搜索服务器",
	"content":"【修改】它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。"
}

postman截图:

elasticsearch-head查看:

4.7 删除文档document

请求url:

DELETE	localhost:9200/blog1/article/1

postman截图:

elasticsearch-head查看:

4.8 查询文档-根据id查询

请求url:

GET	localhost:9200/blog1/article/1

postman截图:

4.9 查询文档-querystring查询

请求url:

POST	localhost:9200/blog1/article/_search

请求体:

{
    "query": {
        "query_string": {
            "default_field": "title",
            "query": "搜索服务器"
        }
    }
}

postman截图:

注意:
将搜索内容"搜索服务器"修改为"钢索",同样也能搜索到文档,该原因会在下面讲解中得到答案

{
	"query": {
		"query_string": {
		"default_field": "title",
		"query": "钢索"
		}
	}
}

4.10 查询文档-term查询

请求url:

POST	localhost:9200/blog1/article/_search

请求体:

{
    "query": {
        "term": {
            "title": "搜索"
        }
    }
}

postman截图:

IK分词器的集成使用

1.查询存在问题分析

在进行字符串查询时,我们发现去搜索"搜索服务器"和"钢索"都可以搜索到数据;
而在进行词条查询时,我们搜索"搜索"却没有搜索到数据;
究其原因是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" : "<IDEOGRAPHIC>",
      "position" : 0
    },
    {
      "token" : "是",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "<IDEOGRAPHIC>",
      "position" : 1
    },
    {
      "token" : "程",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "<IDEOGRAPHIC>",
      "position" : 2
    },
    {
      "token" : "序",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "<IDEOGRAPHIC>",
      "position" : 3
    },
    {
      "token" : "员",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "<IDEOGRAPHIC>",
      "position" : 4
    }
  ]
}

而我们需要的分词效果是:我、是、程序、程序员

这样的话就需要对中文支持良好的分析器的支持,支持中文分词的分词器有很多,word分词器、庖丁解牛、盘古分词、Ansj分词等,但我们常用的还是下面要介绍的IK分词器。

2.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检索的命中率。

3. IK分词器的安装

1)下载地址:https://github/medcl/elasticsearch-analysis-ik/releases
课程资料也提供了IK分词器的压缩包:
2)解压,将解压后的elasticsearch文件夹拷贝到elasticsearch-5.6.8\plugins下,并重命名文件夹为analysis-ik

3)重新启动ElasticSearch,即可加载IK分词器

4.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
    }
  ]
}

6. 修改索引映射mapping

6.1 重建索引

删除原有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许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。"
}

6.2 再次测试queryString查询

请求url:

POST	localhost:9200/blog1/article/_search

请求体:

{
    "query": {
        "query_string": {
            "default_field": "title",
            "query": "搜索服务器"
        }
    }
}

postman截图:

将请求体搜索字符串修改为"钢索",再次查询:

{
    "query": {
        "query_string": {
            "default_field": "title",
            "query": "钢索"
        }
    }
}

postman截图:

6.3 再次测试term测试

请求url:

POST	localhost:9200/blog1/article/_search

请求体:

{
    "query": {
        "term": {
            "title": "搜索"
        }
    }
}

postman截图:

ES集群配置

1.引言

ES集群是一个 P2P类型(使用 gossip 协议)的分布式系统,除了集群状态管理以外,其他所有的请求都可以发送到集群内任意一台节点上,这个节点可以自己找到需要转发给哪些节点,并且直接跟这些节点通信。所以,从网络架构及服务配置上来说,构建集群所需要的配置极其简单。在Elasticsearch 2.0 之前,无阻碍的网络下,所有配置了相同 cluster.name 的节点都自动归属到一个集群中。

2.0 版本之后,基于安全的考虑避免开发环境过于随便造成的麻烦,从 2.0 版本开始,默认的自动发现方式改为了单播(unicast)方式。配置里提供几台节点的地址,ES 将其视作gossip router 角色,借以完成集群的发现。由于这只是 ES 内一个很小的功能,所以 gossip router 角色并不需要单独配置,每个 ES 节点都可以担任。所以,采用单播方式的集群,各节点都配置相同的几个节点列表作为 router即可。

集群中节点数量没有限制,一般大于等于2个节点就可以看做是集群了。一般处于高性能及高可用方面来考虑一般集群中的节点数量都是3个及3个以上。

2.集群的相关概念

2.2.集群 cluster

一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”。这个名字是重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群

2.3.节点 node

一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。和集群类似,一个节点也是由一个名字来标识的,默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。

一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。

在一个集群里,只要你想,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。

2.4.分片和复制 shards&replicas

一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。分片很重要,主要有两方面的原因:

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

至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由Elasticsearch管理的,对于作为用户的你来说,这些都是透明的。

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

复制之所以重要,有两个主要原因: 在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行。总之,每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制的数量,但是你事后不能改变分片的数量。

默认情况下,Elasticsearch中的每个索引被分片5个主分片和1个复制,这意味着,如果你的集群中至少有两个节点,你的索引将会有5个主分片和另外5个复制分片(1个完全拷贝),这样的话每个索引总共就有10个分片。

3.集群的搭建

3.1.准备三台elasticsearch服务器

创建elasticsearch-cluster文件夹,在内部复制三个elasticsearch服务

3.2.修改每台服务器配置

修改elasticsearch-cluster\node*\config\elasticsearch.yml配置文件

node1节点:
#节点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"]
node2节点:
#节点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"]
node3节点:
#节点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"]

3.3.启动各个节点服务器

双击elasticsearch-cluster\node*\bin\elasticsearch.bat

启动节点1:

启动节点2:

启动节点3:

3.4.集群测试

添加索引和映射
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许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。"
}
使用elasticsearch-header查看集群情况

注意配置跨域访问:
http.cors.enabled: true
http.cors.allow-origin: “*”

注意:window10搭建集群,文件名字要简单和不能出现空格,要不然会出现文件加载不成功的情况

elasticsearch 集群无法启动出现如下提示 failed to send join request to master(删除data下面的数据)

Springboot整合Spring Data Es

1. 什么是Spring Data ElasticSearch

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整合方式

2.SpringData ES整合步骤

2.1.准备搜索的服务工程

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache/POM/4.0.0"
         xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache/POM/4.0.0 http://maven.apache/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>easygo</artifactId>
        <groupId>com.bruce.easygo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>easygo-search-service</artifactId>

    <!--导入项目中的依赖-->
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--引入实体层-->
        <dependency>
            <groupId>com.bruce.easygo</groupId>
            <artifactId>easygo-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

        <!-- Eureka客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>com.bruce.easygo</groupId>
            <artifactId>easygo-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>
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

2.2.项目中引入依赖

<!--导入Spring Data ES依赖-->
<!--spring-data elasticsearch-->
	<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

2.3.配置Spring Data ES的配置信息

# 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

2.4.测试ES的连接

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;
y
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestES {

    //如果要操作Redis,需要获取一个 RedisTemplate
    //如果要操作ES,需要获取 ElasticsearchTemplate,天下我有!
    @Resource
    ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 01-测试工具类
     */
    @Test
    public void testConn(){
        System.out.println("ES操作的工具类:"+elasticsearchTemplate);
    }

    /**
     *02-创建一个索引库? Goods索引库,实际的项目搜索的是商品数据!
     */
    @Test
    public void testCreateIndex(){
        //创建索库
        elasticsearchTemplate.createIndex("goodsindexs");
        System.out.println("测试创建索引库");
    }

}

3.使用面向对象思想创建索引库和Mapping

3.1.准备一个实体类,索引库对应

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; //卖家ID
    private String goods_name;  //商品的标题
    private Integer default_item_id; //默认上架商品ID
    private String audit_status; //当前状态
    private String is_marketable; //是否上架
    private Integer brand_id;  //商品的品牌ID
    private 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;
}

3.2. 给实体类加一些注解(Spring Data ES注解),配置Mapping规则的注解

<!--导入Spring Data ES依赖 把它导入到实体类中-->
    <!--spring-data elasticsearch-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
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; //默认上架商品ID
    private 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,天下我有!
    @Resource
    ElasticsearchTemplate elasticsearchTemplate;

    @Resource
    GoodsClient goodsClient;

    @Autowired
    GoodsService goodsService;

    /**
     * 01-测试工具类
     */
    @Test
    public void testConn(){
        System.out.println("ES操作的工具类:"+elasticsearchTemplate);
    }

    /**
     *02-创建一个索引库? Goods索引库,实际的项目搜索的是商品数据!
     */
    @Test
    public void testCreateIndex(){
        //创建索库,索引库的名字是??
        elasticsearchTemplate.createIndex(Goods.class);
        System.out.println("测试创建索引库成功~");
        elasticsearchTemplate.putMapping(Goods.class);//因为类上面有注解
        System.out.println("创建Goods的Mapping完成");
    }

    /**
     * 新增数据库中的数据到ES索引库中
     */
    @Test
    public void testAddDocument(){
        List<Goods> goodsList = goodsClient.getGoods(1);
        for (Goods goods : goodsList) {
            System.out.println("正在导入:"+goods);
        }
        goodsService.saveDocuments(goodsList);
        System.out.println("批量新增索引库数据成功......");
    }

    /**
     * 根据ID查询
     */
    @Test
    public void testgetDocumentById(){
        Goods goods = goodsService.getDocumentById(149187842868047L);
        System.out.println("查询的对象是:"+goods);
    }

    /**
     * 根据ID更新
     */
    @Test
    public 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删除
     */
    @Test
    public void testDeleteByid(){
        goodsService.deleteDocumentById(149187842867914L);
        System.out.println("删除成功");
    }

    /**
     * 删除所有
     */
    @Test
    public void testDeleteAll(){
        goodsService.deleteAllDocument();
        System.out.println("全部干光了");
    }

    /**
     * 测试关键词查询01  条件查询分页
     */
    @Test
    public 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<Goods> 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<Goods> goodsList = page.getContent();
        for (Goods goods : goodsList) {
            System.out.println(goods);
        }

    }

    /**
     * 多条件搜索 分页
     */
    @Test
    public 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<Goods> 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<Goods> goodsList = page.getContent();
        for (Goods goods : goodsList) {
            System.out.println(goods);
        }
    }

Docker部署ES

引言

接下来准备讲解商品搜索,商品搜索用到了ES,所以本文先讲解在Docker下搭建ES与ES的可视化工具Kibana

注意:

  1. 虚拟机内存建议在3.5G以上,测试前先关闭虚拟机防火墙!
  2. Elasticsearch版本号、Kibana版本号以及IK分词器版本号必须一致!

PS:跑起ES和ElasticSearch占内存3.4G,如下:

1.docker下安装ElasticSearch

调高JVM线程数限制数量(一定要先设置!)

vim /etc/sysctl.conf
# 添加这个
vm.max_map_count=262144 
# 保存后执行这个命令
sysctl -p

2.下载ES镜像

在使用docker 拉去最新的镜像时,会提示如下错误:

这里错误的意思是docker需要我们指定下载镜像的版本号。

但是我们想下载最新的版本号,该如何得知最新的版本号呢?

我们可以登录docker hub:https://hub.docker/u/library,搜索自己想要下载的镜像名:

点击搜索出来列表里的镜像,进入详情页面,点击Tags,第一个镜像就是最新的,可以看出,最新的镜像版本号为7.6.1:

重新使用docker执行版本号拉取,可以看到正在下载:

3.查看镜像(注意下一步用到版本号):

4.启动ES

docker run --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -d elasticsearch:7.6.1
    
    
 docker 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

5.浏览器访问

浏览器访问http://你的ip:9200/,可以看到访问成功。

6.解决跨域

6.1.修改配置,解决跨域访问问题

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

6.2.保存退出后,重启容器

exit
docker restart elasticsearch

7.安装IK分词器

elasticsearch的版本和ik分词器的版本需要保持一致,不然在重启的时候会失败。ik分词器地址:https://github/medcl/elasticsearch-analysis-ik/releases

7.1.进入容器

docker exec -it elasticsearch /bin/bash

7.2.下载安装ik分词器

cd /usr/share/elasticsearch/plugins/
elasticsearch-plugin install https://github/medcl/elasticsearch-analysis-ik/releases/download/v7.6.1/elasticsearch-analysis-ik-7.6.1.zip
exit
docker restart elasticsearch

7.3.重启

exit
docker restart elasticsearch

ES综合运用

1.传统搜索?

根据一个搜索词,检索出所有包含该词的数据
例如:用户在搜索框输入一个词,客户端软件发送一个请求到后台,后台通过sql语句从数据库中找出相关条目(数据库会一条一条的对比),这就是一个最简单搜索原型

select * from product where title like ‘%关键词%’;

2.传统搜索面临的问题

1, 当数据量很大时,假如500G, 效率低。从用户角度,从点击搜索按钮到看到搜索结果可能要很长时间,1小时?2小时?用户疯掉

2, 当数据量达到1T,一台电脑已经放不下了,这时候就需要多台,这就是分布式。这时候数据就在不同的服务器了,一个客户端不可能去请求每台机器,所以就需要一个管理员角色,负责把客户端请求分发到每台机器,同时汇总结果返回给客户端

ES

3.什么是Elasticsearch?

Elasticsearch简称ES,是一种以JSON格式进行数据存储的分布式搜索引擎

4.为什么不使用MySQL查询,而要用ES呢?

在一个电商平台项目中,会有非常多的商品,如果使用以往的MySQL进行模糊查询,需要比较长的等待时间,对数据库压力比较大,ES中使用倒排索引,查询效率非常高,可以减轻MySQL压力,而且还能进行分词查询和高亮处理,MySQL是做不到的。

5.什么时候使用ES?什么时候使用MySQL呢?

不是所有的数据都会存在ES中,像商品规格表这种不常修改,且不会去模糊查询的放到缓存中就好了。如果要同步多张表到ES中,就要在Logstath中配置多个mysql的配置文件,对应到mysql的每张表

6.什么是倒排索引?什么是分词查询?

倒排索引:将文档中的关键字和文档ID进行记录,在搜索关键词的时候,直接到指定的文档ID中查找数据
分词查询:比如去搜索腾讯课堂,ES有自带的分词器会将腾讯课堂分词为腾讯和课堂,然后通过倒排索引去查找指定的文档id。

7.分词器有哪些?

ES中有一个常用的英文分词器analyze,它只能够识别英文,对英文进行分词,对中文是每个字符都是独立的。如果要对中文进行分词识别,可以安装ik中文分词器插件。

盘古分词、庖丁解牛分词 IK分词…

迪丽热巴 蔡徐坤 IK分词可以自定义分词

8.ES中有哪些常用数据类型?

(1)六个数字类型:byte、short、integer、long、float、double
(2)布尔型:boolean
(3)字符型:string(text分词,keyword不分词)
(4)二进制型:binary

9.ES集群方式

ES集群原理类似Redis分片集群原理,采用分片集群+主从复制+请求转发的方式集群

10.ES如何与MySQL实现数据同步? 一致性问题?

当第一次启动节点的时候,logstath把MySQL中的数据用JSON格式插入到ES中,并修改MySQL中的updatetime字段为系统时间,之后每隔一分钟去MySQL中查updatetime大于上次同步时间的数据,增量同步到ES中。

延迟!!

强一致性:上架的时候商品数据同步过去,下架的时候也同步!

同步操作 操作MySql数据库之后 再同步ES索引库! 一致性~

11.ELK是什么?如何实现日志收集?

ELK是Elasticsearch+Logstath+Kibana,Logstath用来收集日志,并且把日志以Json格式输出到ES中,通过kibana操作数据

12.ES和MySQL专业名词比较

13.常用的ES语句有哪些?

增: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”: “奥迪”
}

14.ES的JavaAPI

Spring Data ES 和SpringBoot整合

ES脑裂

1.什么是Elasticsearch集群脑裂 split brain


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 两个集群。

2.产生的原因

(1)网络抖动

由于是内网通信、网络通信等问题造成部分节点认为master node挂掉, 然后另选master node的情况可能性较小;可以通过检查Ganglia集群监控,没有发现异常的内网流量, 故此原因可以排除。

而外网的网络出现问题的可能性更大,更有可能造成“脑裂”现象。

(2)节点负载

如果主节点同时承担数据节点的工作,可能会因为工作负载大而导致对应的Elasticsearch实例停止响应。此外,由于数据节点上的Elasticsearch进程占用的内存较大, 较大规模的内存回收操作(GC)也能造成Elasticsearch进程失去响应。所以,该原因出现“脑裂”现象的可能性更大。

(3)内存回收
由于数据节点上的Elasticsearch进程占用的内存较大,较大规模的内存回收操作也能造成Elasticsearch进程失去响应。

防止脑裂: 主节点就是只当主节点,不要又当主节点 又当数据节点!

3.解决方法

(1)不要把主节点同时设为数据节点,即node.masternode.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个主节点同意,才能重新选举主节点。

4.方案分析

实际解决办法

最终考虑到资源有限,解决方案如下:

增加一台物理机,这样,一共有了三台物理机。在这三台物理机上,搭建了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,并且造成查询不同机器,查到了结果不同的情况。

5.总结

以上的解决方法只能是减缓这种现象的发生, 并没有从根本上杜绝

如果发生了脑裂, 如何解决?

所以怎么从脑裂中恢复?

第一个建议是给所有数据重新索引。

第二, 如果脑裂发生了, 要十分小心的重启你的集群。 停掉所有节点并决定哪一个节点第一个启动。 如果需要, 单独启动每个节点并分析它保存的数据。 如果不是有效的, 关掉它, 并删除它数据目录的内容( 删前先做个备份) 。 如果你找到了你想要保存数据的节点, 启动它并且检查日志确保它被选为主节点。 这之后你可以安全的启动你集群里的其他节点了。

ES综合

一、基础概念

1.1、什么是Elasticsearch

Elasticsearch 是基于 Lucene 的 Restful 的分布式实时全文搜索引擎,可以快速存储、搜索、分析海量的数据。

1.2、为什么要用Elasticsearch

1、传统数据库模糊查询不走索引,当数据量很大时,查询效率非常低下

2、Es搜索可以对搜索词进行自动分词等处理,更容易查询到相关的内容

二、底层算法相关

2.1、lucene与ES

简单来说,lucene 就是一个 jar 包,里面包含了封装好的各种建立倒排索引的算法代码。

ES是基于Lucene的,提供了更高层次的封装以及分布式等方面的增强与扩展,并且提供了基于JSON的REST API 来更方便地使用Lucene的功能

ELasticsearch中的每个分片都是一个分离的Lucene实例;

2.2、ES算分的算法是什么?

ES 5之前默认的算分采用TF-IDF,ES 5之后采用BM 25

BM 25是在TF-DF基础上做了一个收敛,避免了TF无限增长时得分无限增长的问题

2.3、倒排索引

倒排索引是一种数据结构

Elasticsearch是通过Lucene的倒排索引技术实现比关系型数据库更快的过滤。

  • 创建索引

    (1)把每个文档拆分为独立的词

    (2)创建一个包含所有且不重复的词条列表

    (3)记录每个词条出现在哪些文档中

  • 搜索时

    (1)搜索时,直接根据词,返回对应的文章id

2.4、正排索引

2.4.1、为什么要用正排索引?

正排索引主要用于实现根据指定字段进行排序和聚合的功能。

假如我们需要对数据做一些聚合操作,比如排序时,lucene会将所有匹配到的文档读入到内存,再进行排序,如果排序数据量巨大的话,非常容易就造成内存溢出。

而正排索引类似于数据库key-value-value,可以只读取id和排序字段到内存进行排序。

2.4.2、ES中正排索引有哪几种?

es中的正排索引有两种:FieldData和DocValues

FieldData是存在内存中的,DocValues存在磁盘中,DocValues不支持text类型字段的排序

2.4.3、ES是如何利用正排和倒排索引的?

【生成文档】

ES在生成文档时,会分别生成倒排索引和正排索引

【搜索】

Es先用倒排索引找到文档,然后用正排索引聚合排序等操作

2.4.4、如何自定义排序?

默认是根据算分排序,自定义排序需要用到正排索引

DocValues是默认开启的,所以可以直接使用,但是不支持对text类型字段的排序。

FieldData是默认关闭的,需要设置mapping开启

2.4.5、关闭正排索引

DocValues会占用更多磁盘空间,也会影响搜索速度,确定该字段不需要聚合、排序以及脚本操作可以通过mapping设置doc_values:false,改回true需要重建索引

2.5、高并发下如何保证读写一致?

  • 更新操作

    可以通过版本号使用乐观并发控制

    每个文档都有一个_version 版本号,对其更新会默认携带version=_version+1,如果_version+1小于更新时的_version,说明已经被更新了,会更新失败

  • 写操作

    支持3种一致性级别,默认是:只有大多数分片可用时才允许写操作。

    如果大多数可用,进行了写操作,写入失败的副本分片会在一个不同的节点上重建。

  • 读操作

    默认是主分片和副本分片上更新/写的操作都完成之后的文档才会被查到

    可以修改参数,只要主分片更新/写的操作完成的文档即可被查到

三、使用语法相关

3.1、新增文档create和index的区别

【index】

文档不存在:新增

文档存在:删除旧的、新增

【create】

文档不存在:新增

文档存在:报错

3.2、更新文档update与index的区别

【index】

文档不存在:新增

文档存在:删除旧的、新增

【update】

文档不存在:报错

文档存在:直接更新

3.3、text 和 keyword类型的区别

keyword类型的字段不会分词,只能精确匹配,区分大小写的

text类型会被分词并且转为小写

3.4、query 和 filter 的区别?

query查询会计算相关度分数,进行排序

filter查询不进行分数计算,并且查询结果会被缓存,性能较高

四、集群架构相关

4.1、集群、主分片、副本分片的关系

【集群高可用架构】

es提供了分布式集群(Cluster)的方式,保证高可用

es将索引细分为分片(主分片、副本分片),一个索引被拆分为多个主分片,分布在不同节点上,用于解决存储空间水平扩容问题。副本分片是对主分片的拷贝,用于解决数据高可用问题(保证一个节点挂了,剩余节点主分片+副本分片能组成完整的数据,保证服务可用)。

【master节点】

master节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;

主节点(Master Node):也叫作主节点,主节点负责创建索引、删除索引、分配分片、追踪集群中的节点状态等工作。 Elasticsearch 中的主节点的工作量相对较轻。 用户的请求可以发往任何一个节点,并由该节点负责分发请求、收集结果等操作,而并不需要经过主节点转发。

【分片特性】

主分片在创建索引时指定,后续不允许修改

副本分片数可以动态调整。

4.2、ES是如何实现master选举的?

Elasticsearch 的选主是 ZenDiscovery 模块负责的,主要包含Ping(节点之间通过这个RPC来发现彼此)和 Unicast(单播模块包含一个主机列表以控制哪些节点需要ping通)这两部分;

1、【在elasticsearch.yml 配置文件中确定当master节点最少票数阈值】确认候选主节点的最少投票通过数量,elasticsearch.yml 设置的值 discovery.zen.minimum_master_nodes;

2、【每个节点给能ping通的NodeId最小的节点投票】对所有候选 master 的节点(node.master: true)根据 nodeId 字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。

3、【选举投票达到阈值且自己选举了自己的节点为master节点】如果对某个节点的投票数达到阈值,并且该节点自己也选举自己,那这个节点就是master。否则重新选举一直到满足上述条件。

4.3、如何解决ES集群的脑裂问题

【脑裂问题】

当网络出现问题,Node1是master节点和其他Node无法连接

Node1还是作为Master节点组成一个集群,其他节点重新选举出了Master节点,这样就形成了两个master节点,当网络回复,集群无法恢复

【解决脑裂】

  • 7.0之前

    设置quorum(仲裁)参数,只有集群中可被选举为master的节点数大于该参数,才能选举master

  • 7.0开始

    es做了优化,不会发生脑裂

4.4、集群健康度yellow、red是什么原因

为了保证集群的高可用,分片是要被分配到不同节点上的

Yellow或red原因是分片没有被合理分配,可以通过_cluster/allocation/explain查看具体原因

  • 红:至少有一个主分片没有分配(节点挂了、存储空间不足等)
  • 黄:至少有一个副本没有分配(节点不够)
  • 绿:主副本分片全部正常分配

4.5、为什么主分片数不能修改

因为es在存储文档时,会根据主分片数和文档id,通过hash算法,计算出文档应该分布在哪个分片上,确保了文档可以均匀的分布在各个分片中。

如果修改了主分片数,那计算规则就变了,会导致文档不能均匀的分布在各个分片中。

Ps:保存文档时,可以传_routing参数,自定义存储到指定的分片上

4.6、写数据底层原理

由于倒排索引一旦生成,不可改变,所以每次新建文档(到某个分片),就会生成一个新的Segment,用来存储该分片上新的倒排索引。

  • 【Refresh】

1、新建文档时,数据会分别写入Index Buffer和Transaction Log

2、Index Buffer每1s/存满(默认是jvm的10%),数据会刷新到Segment

  • 【Transaction Log】

3、每次写入请求Transaction Log都会自动落盘

  • 【Flush】

5、ES会自动做flush操作(每30分钟/Transaction log满(默认512MB)),会将Index Buffer刷新到Segment,并将Segment写入磁盘,清空Transaction log

  • 【Merge】

6、Segment会越来越多,ES会自动进行Merge操作,减少Segment数量,也可以手动merge(POST my_index/_forcemerge)。

7、删除的文档数据会存到.log文件中,merge会真正删除.log文件。

4.6.1、ES为什么是近实时的?

【近实时】

ES默认新建文档1s后被搜到

【原因】

创建文档时,会先存储到Index Buffer中,每1s/Index Buffer存满(默认是jvm的10%)刷新存入Segment,查询是查的Segment,所以存入Segment之后才会被搜索到

4.6.2、ES如何保证断电数据不丢失?

答:创建文档时,存储到Index Buffer的同时,会存储到Transaction log中,Transaction log默认落盘,当断电恢复后,会根据Transaction log做数据恢复。

补充:每30分钟/Transaction log满(默认512MB),会进行一次Flush,将Index Buffer刷新到Segment,并将Segment写入磁盘,清空Transaction log。

4.6.3、为什么删除文档,并不会立即释放空间?

被删除的文档,会存到.log文件中,ES在自动进行merge时,会清理.log文件,此时才会释放空间

4.7、ES集群写入和搜索数据过程

4.7.1、写入数据

1、客户端向Node1节点发送新建/删除索引的请求

2、Node1节点根据文档id,通过Hash算法确定属于分片0,会将请求转发到Node3

3、Node3上主分片执行请求(1、对需要分词的字段进行分词、转成小写;2、存储为正排索引和倒排索引),如果成功了,将请求转发到Node1、Node2的副本分片上。一旦所有副本都报告成功,Node3向Node1节点报告成功,Node1节点向客户端报告成功

4.7.2、搜索数据

搜索执行阶段过程分俩个部分,我们称之为 Query Then Fetch。

  • Query查询阶段

    1、客户端向Node3发送了一个search请求,Node3会创建一个大小为from+size的空队列

    2、Node3将查询请求转发到索引的每个主分片/副本分片中。每个分片在本地执行查询(1、对需要分词的字段的搜索时,先分词、再转为小写,根据倒排索引匹配;2、对不需要分词的字段搜索时,直接精确匹配、自定义排序和聚合时使用正排索引)并添加结果到大小为 from + size 的本地有序优先队列中

    3、每个分片返回各自优先队列中所有文档的 ID 和排序值给 Node 3 ,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。

  • fetch - 读取阶段 / 取回阶段

    1、协调节点根据ID,辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。

    2、每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。

    3、一旦所有的文档都被取回了,协调节点返回结果给客户端。

4.8、跨集群搜索的问题

4.8.1、深度分页

比如取100-110的数据,每个节点上的100-110并不是真正的100-110,所以需要把每个节点的前110都拿过来放一起排序,那么:

每个集群需要查询文档数 = from + size

协调节点需要处理文档数 = 分片数 * (from + size)

如果深度分页,from是1万,那协调点处理的数据量就会非常大,ES默认限定最多为1万个文档。

【解决】

1、使用滚动搜索,记录上一次搜索id,往下翻页从id之后开始取,但是只支持往后翻,不支持指定页数。会创建一个有时间限制的快照,新增数据快照不会更新。

2、业务上处理:每次少量翻页,让用户没有翻到很大页数的兴趣

4.8.2、相关性算分

【算分不准】

默认算分都是根据自己分片上的数据进行的算分,会导致各个分片上算分标准不一

【解决】

1、数据量少,主分片设置为1;数据量足够大时,保证文档均匀分布到各分片上

2、搜索时指定参数“_search?search_type=dfs_query_then_fetch”,会收集各个分片上的TF和IDF,合并算分。会耗费跟很多的CPU和内存

五、ELK相关

5.1、什么是ELK

ELK是一套日志采集、监控的解决方案

Elasticsearch:负责日志检索和储存

Logstash:负责日志的收集、处理

Kibana:负责日志的可视化

5.2、配置注意

对于日志类型的,可以基于时间序列的设置索引,通过通配符的方式匹配查找

对于旧的数据处理:直接删除文档是不会立即释放资源的,但是直接删除索引会立即释放索引,时间序列的索引也方便做冷热分离

六、优化手段

6.1、冷热分离架构

ES集群的索引写入及查询速度比较依赖于磁盘的IO速度,冷热数据分离的关键点为使用固态磁盘存储数据。

若全部使用固态,成本过高,且存放冷数据较为浪费,

因此我们可以将实时数据(5天内)存储到热节点(固态磁盘)中,历史数据(5天前)的存储到冷节点(普通机械磁盘)中,并且可以利用ES自身的特性,比如日志类的,可以按天建立索引,这样能更方便的根据时间将热节点的数据迁移到冷节点中,达到冷热分离,提升热点数据的查询速度

6.2、如何设计分片数、节点数、节点的设备要求

6.3.1、服务器节点要求

  • 硬件设施

    选择合理的硬件:数据节点尽量使用SSD

    • 搜索类等性能要求高的场景,建议SSD
      • 按照1:10的比例配置内存和硬盘
    • 日志类和查询并发低的场景,可以考虑使用机械硬盘存储
      • 按照1:50的比例配置内存和硬盘
    • 单节点数据建议控制在2TB以内,最大不建议超过5TB
    • JVM配置机器内存的一般,JVM内存配置不建议超过32G
  • 内存

    最大内存设置,不要超过50%,单节点最大内存不要超过32G

    • 内存大小要根据Node需要存储的数据进行估算

      • 搜索类:1:16
      • 日之类:1:48-1:96
    • 举例:加入需要存储1T的数据,设置一个副本,需要2T的空间

      如果是搜索类项目:最大32G内存那么,一个节点最多存储32*16=512G,所以至少需要4个节点存储数据。

6.3.2、分片

  • 主分片

    7.0之前默认是5个主分片,7.0之后默认是1个主分片

    【主分片少的问题】

    单个主分片可以解决多个主分片算分不准等问题,但是单个主分片即使新增节点,也无法水平扩展

    【主分片多的问题】

    主分片过多,每个分片是一个lucene的索引,会占用更多的资源,并且每次搜索需要从每个分片上获取数据,会增加master节点管理负担,建议控制分片在10w以内。

    数据量较少时,主分片过多也会导致相关性算分不准

    【如何设计主分片】

    根据数据量大小判定

    • 日志类应用,单个分片不要大于50G
    • 搜索类应用,单个分片不要大于20G
  • 副本分片

    【副本分片优点】

    1、有效保证集群高可用

    2、减缓主分片查询压力,可以提升整体查询的QPS

    【缺点】

    1、副本分片是主分片的一个拷贝,需要占与主分片一样的资源

    2、创建索引及文档时,有几个副本就要消耗几倍的cpu

    【如何设计副本分片】

    考虑节点资源,如果资源充足、需要提升查询速度,可以设置多的副本分片

6.3、提升集群写性能

1、多线程

使用线程池,线程数设置为cpu核心数+1,避免过多的上下文切换,来不及处理的放入队列,队列不能太大,否则占用的内存会成为GC的负担

2、使用_bulk批量写入

单次建议在5-15M,使用负载均衡策略尽量将数据打到不同节点

3、分片设定:

(1)适当减少副本数

(2)合理设置主分片数,确保均匀分配到所有数据节点上(eg:5个数据节点的集群,5个主分片5个副本分片,需要限定每个节点上可分配分片数为(5+5)/5=2个,生产中还要适当调大,保证节点下线时,分片无法正常迁移)

4、关闭不需要的功能(mapping中设置)

(1)不需要聚合搜索的字段,index参数设置为fasle(不建立正排索引)

(2)不需要算分的字段,norms设置为fasle

(3)对于不需要分词的字符串使用keyword(不分词)

5、Index Buffer刷新到segment的时间默认是1s,可以设置为-1,即禁止自动refresh(Index Buffer存满再刷新),避免频繁refresh生成过多的segment文件;与此同时增大Index Buffer大小,可以进一步的减少生成segment文件(会导致搜索实时性降低)

6、每次写入数据,都会存入Transaction Log并落盘,把Transaction Log的落盘改为异步、把落盘时间改为60s(每分钟落盘一次)、增大Transaction Log大小(默认512M)(会降低数据的容灾性)

6.4、提升集群读性能

1、使用SSD盘和冷热分离架构,将旧的数据存到普通盘的节点上,把新的常查询的数据分布在SSD盘的节点上(日志类型的按时间序列建立索引)

2、尽量使用Filter Context,利用缓存机制,减少不必要的算分

3、禁止使用*开头通配符查询

4、避免查询时对数据做计算(字符串长度筛选等),可以在存储时计算存入新的字段

可以使用profile,explain api分析慢查询问题

5、【分片优化】

(1)适当减少主分片数,因为主分片数为5,一个查询需要访问5个分片

(2)控制单个分片尺寸,搜索类20G以内、日志类50G以内

6、使用基于时间序列的索引,将旧的只读的索引进行手动merge,减少segment的数量(日志类型)

6.5、提升搜索准确度

1、选用合适的分词器

分词器选用ik分词器,能够较好的对中文分词

创建索引时使用ik_max_word分词,搜索时使用ik_smart分词

即:索引时最大化的将文章内容分词,搜索时更精确的搜索到想要的结果。

重学ElasticSearch (ES) 系列

https://www.modb.pro/db/390323

总结

ES的搜索

ID排序

本文标签: es