ElasticSearch

ElasticSearch

ElasticSearch,简称ES ,是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据。本身的扩展性很好,可以扩展到上百台服务器,可以处理PB(1024TB)级别的数据,它的目的是通过简单的Restful API 来隐藏Lucene的复杂性

elasticSearch是一个搜索引擎框架,elastic有弹性的 的意思,所以使用 elasticSearch进行搜索时关键字不准确也一样可以搜到想要的数据

在我们使用Mysql的时候,如果我们想要搜索mysql里的数据,那么需要使用模糊查询,但是搜索效率并不高,所以我们使用elasticSearch来进行我们的搜索操作

ES是一个使用java语言并且基于Lucene编写的搜索引擎框架,提供了分布式的全文搜索功能,提供了统一的基于Restful风格的Web接口,官方客户端也对多种语言都提供了相应的API

Lucene : 本身就是一个搜索引擎的底层,或者是第三方的jar包,但是直接使用比较麻烦,ES作为Lucene的封装,将使用简便化了

分布式: ES突出了高扩展能力,可用于分布式

全文检索:将一段词语进行分词,并且将分出的单个单词统一的放到一个分词库中,在搜索时,根据关键词去分词库中检索,找到匹配的内容(倒排索引)

Restful风格的web接口:操作ES很简单,只需要发送一个HTTP请求,并且根据请求方式的不同,携带方式的不同,执行相应的功能

ES和Slor的区别:

  • 这两个都是基于Lucene编写的搜索引擎框架
  • Solr在查询死数据时,比ES 更快,但是如果数据是实时改变的,Solr的查询速度会降低很多,但是ES的数据则基本没有变化
  • Solr搭建集群需要依赖zookeeper帮助管理,ES本身就支持集群的搭建,不需要第三方的介入
  • ES对云计算和大数据支持的特别好

在这里插入图片描述

ES的结构:

在这里插入图片描述

  • book索引: 相当于数据库
  • type 一张数据表
  • document 一条记录
  • field 字段

索引: 可以创建多个索引,那么就是多个数据库。每个索引默认被分成5片分片存储,每一个分片都会存在至少一个备份分片,备份分片默认不会帮助检索数据,只有当ES检索压力很大的时候,部分分片才会帮助检索数据,备份的分片必须放在不同的ES服务器中

类型Type的概念已经被删除了,也就是一个index只能包含一个type,所以在之后的命令中不要带type了

安装:

需要有JDK1.8的环境

下载地址 : https://repo.huaweicloud.com/elasticsearch/7.8.0/

我这里下载的是 elasticsearch-7.8.0-windows-x86_64.zip

解压后如下:

在这里插入图片描述

bin是二进制命令文件夹

config是配置文件夹 ,里面的 log4j2.properties是日志配置文件, jvm.options是Java虚拟机相关的配置, elasticsearch.yml是elasticsearch的配置文件。

启动:

使用 /bin/elasticsearch.bat即可启动,启动端口默认是9200

启动之后,可以访问 http://localhost:9200/来查看当前ES服务器的信息:

在这里插入图片描述

elasticsearch可能会出现跨域问题,我们需要修改 /config/elasticsearch.yml文件,添加如下内容:

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

在这里插入图片描述

ElasticSearch-head插件安装:

我这里选择将插件安装到浏览器里。

先去GitHub上下载 crx文件:

elasticsearch-head/crx

然后将这个 es-head.crx文件重命名为 es-head.zip

然后解压 es-head.zip 文件,得到如下:

在这里插入图片描述

然后进入浏览器选择我们的扩展文件夹(这里是 crx),插件就会加载成功了:

在这里插入图片描述

我们直接使用插件就行:

在这里插入图片描述

KIBANA安装:

使用kibana实现 ES 的可视化(注意kibana 的版本要和ES的版本相同)

KIBANA是一个免费且开放的用户界面,能够让我们对ES数据进行可视化,并让我们可以做各种操作,从跟踪查询负载到理解请求如何流经整个应用

Kibana可以通过各种图表进行高级数据分析及展示。

去GitHub上下载 kibana的压缩包,并解压。

解压之后使用 /bin/kibana.bat来启动kibana,启动会比较慢

启动完成之后,可以访问 http://localhost:5601/app/kibana#/home查看

在这里插入图片描述

全都是英文的,如果需要汉化:

在这里插入图片描述

可以看到这里提供了中文的json

然后去/config/kibana.yml修改这个文件:

在这里插入图片描述

这样就汉化成功

在这里插入图片描述

我们可以进入kibana的 Dev Tools来对 ES操作(发送各种HTTP请求即可,这部分也可以使用POSTMAN)

在这里插入图片描述

分词器:

分词: 就是把一段中文或者别的语句划分成一个个的关键词,我们在搜索的时候会把自己信息进行分词,会把数据库中或索引库中的数据进行分词,然后进行匹配操作,默认的中文分词是将每个字看成一个词,所以我们需要安装中文分词器 IK 来解决

IK提供了两种分词算法, ik_smartik_max_word

  • ik_smart会做最粗粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。
  • ik_max_word会将文本做最细粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。

两种分词器使用的最佳实践是:索引时用ik_max_word,在搜索时用ik_smart。
即:索引时最大化的将文章内容分词,搜索时更精确的搜索到想要的结果。

安装IK分词器

还是去github上下载压缩包,并解压到 ES 的 **/plugins/**目录

然后我们重启ES服务器:

在这里插入图片描述

说明ES启动时加载了IK 分词器

测试两种分词:

发送符合Restful规范的URL来获取分词:

GET _analyze
  {
    "text": "中华人民共和国人民大会堂",
    "analyzer": "ik_smart"
  }

这样得到的分词是:

在这里插入图片描述

请求 ik_max_word 的分词:

GET _analyze
{
  "text": "中华人民共和国人民大会堂",
  "analyzer": "ik_max_word"
}

结果:

在这里插入图片描述

可以发现 ik_max_word这种分词方式将所有能形成的词都进行分词了

IK分词器的分词如果不能满足我们的话,我们也可以自己扩展IK的分词:

看到 ik分词器的目录下的 config/IKAnalyzer.cfg.xml 文件

在这里插入图片描述

我们需要在 ext_dict里写入我们配置的 dic 文件,

<entry key="ext_dict">custom.dic</entry>

custom.dic :

在这里插入图片描述

然后重启ES服务器,我们会发现,可以分词出我们配置的分词了

ES的请求URL:

创建索引:

要对数据做操作,必须要有索引(也就是先有数据库)

kibana的环境下: PUT /shopping : 创建一个名为 shopping 的索引:

在这里插入图片描述

做同样的操作是不允许的,也就是说连续第二次PUT时会报错已存在索引

查询索引信息 :

GET /shopping 取得索引的各种信息

在这里插入图片描述

删除索引:

DELETE /shopping即可删除索引

在这里插入图片描述

document的创建(只能使用POST):

在MySQL中添加数据还需要数据表,但是现在的ES去掉了type这个数据表定义,所以是直接在索引里创建文档 document

当我们数据创建时会返回id ,这个id是随机生成的,所以对于同样的请求每次返回的结果是不同的,所以POST不是幂等性的,但是PUT必须是幂等性,也就是两次同样的请求结果也必须一样

使用 :

POST /shopping/_doc
{
  "name": "onlineshops",
  "cate": "小米手机"
}

在这里插入图片描述

我们还可以自己指定 id,那么就不会产生随机id了, POST /shopping/_doc/1001

里面的 result就表示是否已创建

查询文档document:

使用 GET /shopping/_doc/ID,这就是用主键查询文档

在这里插入图片描述

如果要查询所有数据,那么使用 GET /shopping/_doc/_search来进行查询

修改文档数据:

在修改之前的数据是 :

  "_source" : {
    "name" : "onlineshops",
    "cate" : "小米手机"
  }

完全覆盖数据:

PUT  /shopping/_doc/ID
{
   "name" : "onlineshops",
    "cate" : "华为手机"
}

在这里插入图片描述

看到 "result": "updated"可以知道我们的修改提交成功了,再次使用 GET /shopping/_doc/LXs0s3kBya_CuchCNFyl查询的时候,就会发现数据变成了 华为手机

注意:这里有一个 version,你每提交一次 version就会加一

局部更新:

POST /shopping/_update/LXs0s3kBya_CuchCNFyl
{
  "doc": {
      "name": "onlinesshops"
  }
}

可以看到,局部更新里面我们需要指定 doc,配置需要修改的值,那么就只会修改我们配置的值,其他的不会变

删除document数据:

使用DELETE /shopping/_doc/LXs0s3kBya_CuchCNFyl即可,

在这里插入图片描述

查询

搜索查询才是ES最主要的内容

在请求体中增加查询

命令就是前面的查询全部数据GET /shopping/_search然后加上匹配信息的配置

GET /shopping/_search
{
  "query": {
    "match": {
      "FIELD": "TEXT"
    }
  }
}

这就是配置某个Field的值能匹配TEXT的值(匹配是包含,而不只是等于)

配置分页(与query同级):

  • from 从命中的第几个开始显示,从 0开始
  • size 分页的大小 , fromsize一起使用的时候,from就表示分页的第几页了,如 from=1,size=2,那么显示的就是所有查询里的第三四条

只需要特定的字段值,可以配置 _source 属性

GET /shopping/_search
{
  "query": {
    "match_all": {
    }
  },
  "_source": ["name","cate"]
}

这里的 _source配置的是两个字段,那么查询的时候就查询出这两个字段,抛弃其他的字段

按照某个字段进行排序:

配置 sort 属性就行了

 "sort": [
    {
      "FIELD": {
        "order": "desc"
      }
    }
  ]
查看所有的索引及其信息:

使用GET发送 http://127.0.0.1:9200/_cat/indices

设置某些条件查询

配置 query里的 bool即可

GET /shopping/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "onlineshops"
          }
        }
      ]
    }
  }
}

如图就是配置name要匹配 "onlineshops"这个值,由于 must是一个数组,所以可以配置多个条件

match是全文检索匹配,会将关键词进行分词,索引内容也分词进行匹配,

我们可以使用 match_phrase进行完全匹配,不会进行分词,就是对当前的词进行匹配

使用match和match_phrase分别进行匹配:

在这里插入图片描述

在这里插入图片描述

如图,同样关键词是 小华match能够查出所有内容, match_phrase就查不出,因为 match_phrase是直接使用 小华两个字去匹配的,自然匹配不到

must的意思

should

filter范围筛选

		"filter": [
        {
          "range": {
            "FIELD": {
              "gte": 10,  
              "lte": 20
            }
          }
        }
      ]
  • gtegreater than ,也就是大于
  • ltelower than ,也就是小于
配置高亮:
GET /shopping/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "cate": "小华"
          }
        }
      ]
    }
  },
  "highlight": {
    "fields": {
      "cate": {}
    }
  }
}

这就是将查询出来的 cate 字段值进行高亮显示:

在这里插入图片描述

进行聚合操作:

配置 aggs属性

GET /shopping/_search
{
  "aggs": {
    "price_term": {
      "terms": {
        "field": "price"
      }
    }
  },
  "size": 0
}

这里的 price_term 可以是任意名字 , term表示的是这是分组操作,根据 price字段进行分组(price字段必须是整数型的,如果是字符串会 报错), size为0表示我只需要聚合的信息,原内容不需要

在这里插入图片描述

如图,将我们的统计信息查询出来了

对数据进行约束

我们可以对索引里的数据进行约束,如类型约束等等

GET /shopping/_mapping
{
  "properties": {
    "name": {
      "type": "text",
      "index": true
    },
    "cate": {
      "type": "keyword",
      "index": false
    }
  }
}

配置约束要发送到 _mapping ,其中name字段限制的是 类型为文本,可以作为索引,cate字段限制的是类型是关键词(也就是不能被分词,默认作为完全匹配),不能被索引,不能被索引也就是不能查询,在match中指定了 cate会报错

搜索原理:

我们在搜索的时候可以发现,即使只输入一部分字,也可以查出内容,因为ES会将关键词进行分词,然后对索引中的内容也进行分词,使用倒排索引进行匹配

集群:

前面所使用的ES服务器是单机的。

但是单台ES服务器提供服务往往都有最大负载能力,超过这个阈值,服务器性能就会大大降低甚至不可用,所以在生产环境中,一般都是运行在指定服务器集群中。

集群Cluster:

一个集群就是由一个或多个服务器节点组织在一起,共同持有整个数据,并一起提供索引和搜索功能。一个ES集群有一个唯一的名字标识,这个名字默认是 elasticsearch

节点NODE:

一个节点就是其中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。

windows集群配置:

先将datalogs文件夹删除,打开config/elasticsearch.yml 文件

配置这两个内容:

在这里插入图片描述

必须将集群内的配置文件的 cluster.name改成一致的,node.name必须不一致

network.host改成 localhost

在这里插入图片描述

  • 启动端口号 http.port: 9201

  • 配置监听端口号:transport.tcp.port:9301

  • 配置跨域配置:http.cors.enabled: true http.cors.allow-origin: "*"

  • 配置当前节点的配置 : node.master: true node.data: true

然后将ES文件目录复制三份:

在这里插入图片描述

然后修改其中的 启动端口号、监听端口号和节点名字,其他的不需要修改

启动之后可以使用 http://127.0.0.1:9201/_cluster/health来查询集群信息:

在这里插入图片描述

现在 number_of_nodes的值为1,说明集群中只有一个节点

现在启动的第一个节点是 9201的,监听端口号是9301

那么启动第二个节点 端口号是9202,监听端口号是9302(记得先删除data和log文件夹)时候要去寻找第一个节点,要在配置文件 /conf/elasticsearch.yml里额外配置寻找信息:

discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301"]
discovery.zen.fd.ping_timeout: 1m
discovery.zen.fd.ping_retries: 5

再启动第三个(端口号是9203,监听端口号是9303

启动第三个时可以寻找第二个节点的监听端口号来加入集群:

discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302"]

现在再使用http://127.0.0.1:9201/_cluster/health查看集群信息时,可以发现集群节点变成了3个

在这里插入图片描述

到此,三个节点形成的ES集群就创建成功了

Linux配置ES:

先下载安装包:

https://repo.huaweicloud.com/elasticsearch/7.8.0/elasticsearch-7.8.0-linux-x86_64.tar.gz上传到阿里云服务器上之后解压

  • tar -zxvf elasticsearch-7.8.0-linux-x86_64.tar.gz
  • mv elasticsearch-7.8.0/ es

因为安全问题,ElasticSearch不允许root用户直接运行,所以需要创建新用户:

  • useradd es
  • passwd es
  • 密码输入 es

那么现在是使用 es-es 这个用户来操作ES

配置集群:

config/elasticsearch.yml这个配置文件里面加入

cluster.name: elasticsearch
node.name: node-1
network.host: 0.0.0.0
http.port: 9200
cluster.initial_master_nodes: ["node-1"]

然后修改 /etc/security/limits.conf ,配置es这个用户的进程可以打开的文件数的限制

es soft nofile 65536
es hard nofile 65536

修改 /etc/security/limits.d/20-nproc.conf,添加如下内容:

es soft nofile 65536
es hard nofile 65536

在这里插入图片描述

然后修改 /etc/sysctl.conf

vm.max_map_count = 655360

然后重新加载: sysctl -p

启动:

首先进入 elasticsearch 的目录,然后登录es 用户

  • chown -R es:es /envir/elasticsearch/es 把elasticsearch文件目录授权给es用户

  • su es

我这里的问题使用的是轻量级服务器,内存不够,完全启动不了ES,等啥时候有 2C2G的服务器再说吧

ES进阶:

索引 index

一个索引就是一个拥有几分相似特征的文档的集合。你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。一个索引用一个名字标识(必须是小写字母)。当我们要对这个索引中的文档进行增删改查的时候都需要使用这个名字。在一个集群中,可以定义任意多个索引

能搜索的数据必须要索引,这样的好处是可以提高查询速度

一切设计都是为了提高搜索的性能

类型 Type

已被抛弃,可以不理解

索引可以直接关联数据,所以多一个类型是没有必要的

文档 document

文档就是 json格式的数据。在一个索引内可以存储任意多个文档

字段 field

相当于数据库内的字段、列

映射 mapping

mapping是对处理数据的方式和规则方面做一些限制,如: 某个字段的数据类型、默认值、分析器、是否被索引等等。

分片:

一个索引可以存储超出单个节点硬件限制的大量数据。如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点可能都没有这样大的磁盘空间,或者单个节点处理搜索请求,响应太慢。所以ES提高了分片功能,将索引划分成多份的能力,每一份都称之为分片,当你创建一个索引的时候,你可以指定你想要的分片的数量,每个分片本身也是一个功能完善的并且独立的 索引,这个 索引可以放置到集群中的任何节点上

  • 分片可以允许我们水平分割
  • 可以在分片上进行分布式的、并行的操作,进而提高性能和吞吐量

分片就是ES里的最小单位

副本 replicas:

副本就是故障转移机制,ES允许创建分片的一份或多份拷贝,这些拷贝叫做复制分片(副本)

  • 在节点、分片失败的时候,提高了高可用性,要注意 复制分片 从不与 原分片置于同一节点
  • 扩展搜索量和吞吐量,因为搜索可以在所有的副本上并行允许

分配 allocation:

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

架构:

在这里插入图片描述

ES的分布式集群:

单节点集群:

我们启动第一个9201节点(先清空data和log文件夹,要不然可能会自动寻找集群),在的集群内创建 users索引,我们为其分配三个主分片和一个副本

PUT  http://127.0.0.1:9201/users
{
    "settings": {
        "number_of_shards": 3,
        "number_of_replicas": 1
    }
}

创建这个 users索引之后,查看一下发现:

在这里插入图片描述

分片个数和副本个数都是我们设置的

现在我们有三个分片,但是当前只有一个节点,所以三个节点都被分配到了 node-1这个节点上

在这里插入图片描述

要查看ES的集群信息,我们可以使用 edge自带的扩展程序(其实不如 elasticsearch-head好用,前可以使用前面已经说过的浏览器安装 elasticsearch-head插件):

在这里插入图片描述

启动扩展程序之后,如下:

在这里插入图片描述

可以看到Cluster Health 里面的 statusyellow ,说明集群运行正常,但是分片没有全分配好。

在这里插入图片描述

但是 unassigned_shards的值是3,说明有三个副本分片都是 unassigned,它们都没有被分配到任何节点,在同一个节点上既保存原始数据又保存副本是没有意义的,因为一旦失去了那个节点,我们也将丢失该节点上的所有副本数据

故障转移

当集群中只有一个节点在运行,就意味着会有单点故障问题—没有冗余。

我们可以启动第二个节点来防止数据丢失。现在启动9202节点(记得先删除data和log)。

第二个节点加入的时候会进行副本的重新分配

在这里插入图片描述

如图,就是我们添加了第二个节点进入集群,在这个集群中我们可以发现 status变成了 green,说明集群运行是完全正常的。未分配的分片也变成了 0。

绿色 green表示所有的分片都在正常运行

水平扩容:

当我们启动了第三个节点,我们的集群会拥有三个节点 的集群,此时分片会继续重新分配

分配原则: 1.副本分片和原分片不可以在同一个节点, 2. 要尽量提高均衡,也就是如果有三个节点,不可能一个有三个副本分片,一个只有一个副本分片这种情况

启动第三个节点那么副本分片就会变成:

在这里插入图片描述

可以发现node-1和node-2和node-3都存在了两个分片。

原先的分配是 node-1和node-2各有三个分片,现在每个节点都拥有两个分片,而不是之前的三个,这表示每个节点的硬件资源将被更少的分片共享,每个分片的性能将会提高

分片是一个功能完整的搜索引擎,它拥有使用一个节点上的所有资源的能力。我们这个拥有六个分片(三个主分片和三个副本分片)的users索引可以最大扩容到6个节点,每个节点上存在一个分片,并且每个分片拥有节点的全部资源

在这里插入图片描述

主分片的数量在索引创建时就已经确定了,这个数目定义了这个索引能够存储的最大数据量。但是读操作(搜索和返回数据)可以同时被主分片或副本分片所处理,所以当你有越多的副本分片时,也将拥有越高的吞吐量

如果需要扩容超过6个分片怎么做?

首先,现在是两个节点,三个不同的分片,如果我们要变成九个分片,那么每个节点至少放4个(4大于3),那么说明一个节点上是必定会有一个重复的分片,这样就和分配原则相违背,会报错。那么必须增加节点的数量

主分片的数量是不能修改的,但是我们可以通过修改副本分片的数量来进行扩容:

PUT http://127.0.0.1:9201/users/_settings
{
    "settings": {
        "number_of_replicas": 2
    }
}

应对故障:

当我们将 主节点 node_1下线,那么现在副本分片就会变成主分片,然后非主节点会选举成主节点,但是因为node_1的分片当前无法使用了,所以 集群状态会从 green–>yellow。当node_1节点重新连接时(需要配置 discovery.seed_hosts为其他在集群中的监听IP地址 来加入集群),node_1就会变成普通节点

现在的集群如图所示:

在这里插入图片描述

集群是正常的,但是当我们将master节点node-3关闭。

在这里插入图片描述

此时,master节点就变成了node-1,健康值为变成了yellow(因为有三个分片无法分配)

路由计算:

我们放入的数据可能在三个分片的某一个,对于唯一的数据这个分片必须要固定,要不然我们查找的分片和存储的分片可能不一致。

路由计算是通过hash计算的,也就是通过hash算法来计算出具体放在索引的哪个分片

分片控制:用户可以访问任何一个节点获取数据,那么会通过轮询来进行负载均衡

数据写的流程:

在这里插入图片描述

  1. 用户连接时可能连接到node-1002这个节点,但是hash计算出来应该将 zhangsan这个数据放到p0这个分片上,所以会转移到node-1001节点进行写
  2. 写主分片写完之后,node -1节点会发送请求给副本分片所在的node-1,将R0这个副本分片更新
  3. 副本分片R0更新完之后,主分片发送更新完成的响应
  4. 客户端获取响应

写数据时,当前节点要判断副本分片要达到足够的数量才会进行写数据(数量可以修改),如果没达到指定数量,那么就会等待,默认一分钟,然后作为超时处理

持久化如下:

在这里插入图片描述

  • ES MEMORY 是指内存

  • ES 持久化是将index先变为 段(segment)再进行持久化的

  • ES是先进行 index->segment 的操作再进行 index->translog的,因为index->segment很有可能会出错,如果先写log文件那么可能会产生很多无效记录

  • OS CACHE就是操作系统的文件缓冲区

数据读流程

客户端第一次连接的节点是随机的。连接到之后会找到你要找的所有主分片和副本分片,然后根据轮询算法访问不同的节点

在这里插入图片描述

  1. 客户端发送查询请求到协调节点(第一次连接的是哪个节点,那个节点就作为协调节点)
  2. 协调节点计算数据所在分片及其所有副本的位置
  3. 为了能够负载均衡,可以轮询所有节点
  4. 将节点转发给具体的节点
  5. 节点返回查询结果,将结果反馈给客户端

数据更新的流程:

在这里插入图片描述

  1. 客户端发送请求
  2. 协调节点将请求发送到主分片所在的节点
  3. 主分片修改
  4. 主分片修改完之后通知副本分片修改
  5. 主分片及所有副本分片修改完之后返回响应

倒排索引

倒排索引是全文检索的主要方式

通过分词将 内容分成一个个的单词,将每个单词拿出来做key,value则是这个单词所在的文章ID

然后根据分词的不同又有 max_wordsmart,这在前面的ik分词器中有说

  • 词条:就是分出的各个单词
  • 词典: 字典,词条的集合
  • 倒排表: 也就是 单词-文章ID 的表

文档分析:

  1. 将一块文本分成适合于倒排索引的独立的词条
  2. 将这些词条统一化为标准格式以提高他们的 可搜索性

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

  • 字符过滤器:

    ​ 首先,字符串按顺序通过每个字符过滤器,用处是在分词前整理字符串,一个字符过滤器可以用来去掉HTML,或者将 & 转换成 and

  • 分词器:

    ​ 字符串被 分词器分为 单个的词条。一个简单的分词器遇到空格和标点的时候,可能会将文本拆分成词条

  • Token 过滤器:

    ​ 词条按顺序通过每个 token过滤器。这个过程可能会改变词条(例如,小写化单词),删除词条(如 a ,and ,the 这种无用词),或者增加 词条、、

ES自带一些分析器

测试ES的分析器:

发送如下URL:

GET http://127.0.0.1:9200/_analyze
{
    "analyzer": "standard",
    "text": "text to analyze"
}

使用 standard的分析器进行文本分析

结果如下:

{
    "tokens": [
        {
            "token": "text",
            "start_offset": 0,
            "end_offset": 4,
            "type": "<ALPHANUM>",
            "position": 0
        },
        {
            "token": "to",
            "start_offset": 5,
            "end_offset": 7,
            "type": "<ALPHANUM>",
            "position": 1
        },
        {
            "token": "analyze",
            "start_offset": 8,
            "end_offset": 15,
            "type": "<ALPHANUM>",
            "position": 2
        }
    ]
}
  • token 是指词条
  • start_offset是指在文本中的开始偏移量

ES的默认的分析器是做不了 中文的分词的,所以我们需要使用 IK分词器,前面已经介绍了

ES的并发控制

es中使用乐观并发控制,它假定冲突是不可能发生的,并且不会阻塞正在尝试的操作。然而,如果源数据在读写中被修改,更新将失败。

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

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

我们先创建一个索引:

POST http://127.0.0.1:9200/users/_doc/1001
{
    "name": "hello111",
    "content": "concurrency111"
}

结果如下:

{
    "_index": "users",
    "_type": "_doc",
    "_id": "1001",
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 1,
    "_primary_term": 1
}

这里面的 _seq_no_primary_term 就是用于并发控制的属性

我们进行修改:

POST http://127.0.0.1:9200/users/_doc/1001?if_seq_no=1&if_primary_term=1
{
    "name": "hello222",
    "content": "concurrency222"
}

可以发现属性值被修改成功, _seq_no这个属性值也自增了 1

但是当我们再一次发送同样的请求时,就会报错,因为当前需要的 seq_no值变成了 2,并不等于1,所以把 if_seq_no=1改成 if_seq_no=2即可修改

ES集成Spring Data

Spring Data 是一个由于简化关系数据库非关系型数据库索引库的访问的框架。主要目标是使得对数据的访问变得方便快捷

spring Data可以极大地简化JPA(Java Persistence API)的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作,除了CRUD,还包括如分页、排序等一些常用的功能

Spring Data集成ES则是使用springdata-elasticsearch

创建spring boot项目:

在这里插入图片描述

版本对应:版本查看

在这里插入图片描述

因为我们使用的是7.8.0的ES,那么使用2.3.*的spring boot即可

我这使用2.3.6的spring boot:

<parent>
 	  <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
          <version>2.3.6.RELEASE</version>
</parent>

<dependency> 		<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<optional>true</optional>
</dependency>

创建实体类(创建这个实体类之后,在启动spring boot时会自动去ES创建indexName为 shopping的索引):

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Document(indexName = "shopping",shards = 3,replicas = 1)
public class Product {
    @Id
    private Long id;
    private String title;
    @Field(type = FieldType.Keyword)//keyword表示不能被分词
    private String category;
    @Field(type = FieldType.Double)//表示为浮点类型,非字符串类型都必须要使用这个
    private Double price;
    @Field(type = FieldType.Keyword,index = false) //不能被索引
    private String images;

}

创建Dao对象:

@Repository
public interface ProductDao extends ElasticsearchRepository<Product,Long> {

}

创建配置类:

@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {

    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {

        RestHighLevelClient client=new RestHighLevelClient(RestClient.builder(new HttpHost("localhost",9200)));
        return client;
    }
}

创建索引:

索引是在我们初始化模板时自动进行创建的,如下:

   //初始化模板时会自动创建索引
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

所以我们在启动测试类时要在测试类里写上,如果启动spring boot,那么可以在spring boot启动类里写上

操作文档:

创建文档:
    //初始化模板时会自动创建索引
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
        @Autowired
    ProductDao productDao;

  @Test
    void create_document(){
        System.out.println("插入document");
        Product product=new Product();
        product.setId(1001L);
        product.setTitle("华为");
        product.setCategory("手机");
        product.setImages("/111.jpg");
        product.setPrice(500.0);
        productDao.save(product);
    }

要记得在每个类中都必须写模板类,使用 ElasticsearchRepository的save() 方法插入文档document

批量插入:

 List<Product> list=new ArrayList<>();
productDao.saveAll(list);
修改文档:

也是使用save() 方法,ID相同的时候就作为修改

查询文档:
@Test
    void find_document(){
        Product product = productDao.findById(1001L).get();
        System.out.println(product);

}
    @Test
    void find_all_document(){
        Iterator<Product> iterator = productDao.findAll().iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
}
删除文档:
 productDao.deleteById(1001L);
分页查询:
 PageRequest pageRequest=PageRequest.of(0,5, Sort.by(Sort.Direction.DESC,"id"));
        productDao.findAll(pageRequest);

Sort.by(Sort.Direction.DESC,"id")说明是按照id进行降序排序

0,5说明当前是第一页的前五个文档

文档搜索:

条件搜索:

使用 TermQueryBuilder来创建搜索条件

  @Test
    void term_Query(){
        Product p=new Product();
        p.setTitle("华为");
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("price", "500.0");
        Iterable<Product> products = productDao.search(termQueryBuilder, PageRequest.of(0,2));
        for (Product product : products) {
            System.out.println(product);
        }
    }

分片策略:

分片和副本的设计为ES提供了分布式和故障转移的特性,但并不意味着分片和副本是可以无限分配的,而且索引的分配完成分配后我们是不能重新修改分分片数的

一个分片也是有代价的:

  • 一个分片的底层即为一个Lucene索引,会消耗一i党的文件句柄,内存以及CPU运转。
  • 每一个搜索请求都需要命中索引的每一个分片,如果每一个分片都处于不同的节点还好,如果存在一个节点,那么会造成很大压力。
  • 计算相关度是基于分片的,如果有许多分片,每一个都只有很少的数据会导致很低的相关度

所以应该按着下面的原则进行分配分片:

  1. 分片数不超过节点数的3倍
  2. 节点数 <=主分片数*(副本数+1)

断线重连机制:

当一个节点断线了,那么集群会等待一分钟,超过一分钟就会重新分配分片(会造成极大开销),

我们可以推迟等待时间:

PUT /_all/_settings 
{
 "settings": {
 "index.unassigned.node_left.delayed_timeout": "5m" 
 }

路由选择:

当我们查询文档的时候,ES可以带路由查询,也可以不带路由查询:

不带 routing查询:

  1. 分发:请求到达协调节点后,协调节点将查询请求分发到每个分片上。
  2. 聚合: 协调节点搜集到每个分片上查询结果,在将查询的结果进行排序,之后给用户返回结果。

使用routing查询:

直接根据 routing 信息定位到某个分配查询,如果 routing 设置为 id 的话,可以直接查询出数据来, 效率提升很多

减少副本数量:

ES为了保证集群的可用性,提供了 Replicas支持,但是每个副本也会执行分析、索引、及可能的合并过程。所以Replicas的数量会严重影响写索引的效率

当写索引时,需要把数据同步到副本节点,所以副本节点越多,写索引的效率就越慢。

当我们需要大批量写入操作时,可以先禁止Replica复制,

设置index.number_of_replicas: 0 关闭副本,写入完成之后,Replica修改回正常的状态

productDao.findAll(pageRequest);

`Sort.by(Sort.Direction.DESC,"id")`说明是按照id进行降序排序

`0,5`说明当前是第一页的前五个文档



### 文档搜索:

##### 条件搜索:

使用 `TermQueryBuilder`来创建搜索条件

```java
  @Test
    void term_Query(){
        Product p=new Product();
        p.setTitle("华为");
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("price", "500.0");
        Iterable<Product> products = productDao.search(termQueryBuilder, PageRequest.of(0,2));
        for (Product product : products) {
            System.out.println(product);
        }
    }

分片策略:

分片和副本的设计为ES提供了分布式和故障转移的特性,但并不意味着分片和副本是可以无限分配的,而且索引的分配完成分配后我们是不能重新修改分分片数的

一个分片也是有代价的:

  • 一个分片的底层即为一个Lucene索引,会消耗一i党的文件句柄,内存以及CPU运转。
  • 每一个搜索请求都需要命中索引的每一个分片,如果每一个分片都处于不同的节点还好,如果存在一个节点,那么会造成很大压力。
  • 计算相关度是基于分片的,如果有许多分片,每一个都只有很少的数据会导致很低的相关度

所以应该按着下面的原则进行分配分片:

  1. 分片数不超过节点数的3倍
  2. 节点数 <=主分片数*(副本数+1)

断线重连机制:

当一个节点断线了,那么集群会等待一分钟,超过一分钟就会重新分配分片(会造成极大开销),

我们可以推迟等待时间:

PUT /_all/_settings 
{
 "settings": {
 "index.unassigned.node_left.delayed_timeout": "5m" 
 }

路由选择:

当我们查询文档的时候,ES可以带路由查询,也可以不带路由查询:

不带 routing查询:

  1. 分发:请求到达协调节点后,协调节点将查询请求分发到每个分片上。
  2. 聚合: 协调节点搜集到每个分片上查询结果,在将查询的结果进行排序,之后给用户返回结果。

使用routing查询:

直接根据 routing 信息定位到某个分配查询,如果 routing 设置为 id 的话,可以直接查询出数据来, 效率提升很多

减少副本数量:

ES为了保证集群的可用性,提供了 Replicas支持,但是每个副本也会执行分析、索引、及可能的合并过程。所以Replicas的数量会严重影响写索引的效率

当写索引时,需要把数据同步到副本节点,所以副本节点越多,写索引的效率就越慢。

当我们需要大批量写入操作时,可以先禁止Replica复制,

设置index.number_of_replicas: 0 关闭副本,写入完成之后,Replica修改回正常的状态

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值