Elastic Search

Elastic Search

ELK基础

一、 什么是Elastic Search

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

1 相关概念

1.1 cluster

代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的。es的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来说的,因为从外部来看es集群,在逻辑上是个整体,你与任何一个节点的通信和与整个es集群通信是等价的。

1.2 shards

代表索引分片,es可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改(primary shard)。
水平扩容时,需要重新设置分片数量,重新导入数据。

1.3 replicas

代表索引副本(replica shard),es可以设置多个索引的副本,副本的作用一是提高系统的容错性,当某个节点某个分片损坏或丢失时可以从副本中恢复。二是提高es的查询效率,es会自动对搜索请求进行负载均衡。

1.4 recovery

代表数据恢复或叫数据重新分布,es在有节点加入或退出时会根据机器的负载对索引分片进行重新分配,挂掉的节点重新启动时也会进行数据恢复。

1.5 river

代表es的一个数据源,也是其它存储方式(如:数据库)同步数据到es的一个方法。它是以插件方式存在的一个es服务,通过读取river中的数据并把它索引到es中,官方的river有couchDB的,RabbitMQ的,Twitter的,Wikipedia的。

1.6 gateway

代表es索引快照的存储方式,es默认是先把索引存放到内存中,当内存满了时再持久化到本地硬盘。gateway对索引快照进行存储,当这个es集群关闭再重新启动时就会从gateway中读取索引备份数据。es支持多种类型的gateway,有本地文件系统(默认),分布式文件系统,Hadoop的HDFS和amazon的s3云存储服务。

1.7 discovery.zen

代表es的自动发现节点机制,es是一个基于p2p的系统,它先通过广播寻找存在的节点,再通过多播协议来进行节点之间的通信,同时也支持点对点的交互。

1.8 Transport

代表es内部节点或集群与客户端的交互方式,默认内部是使用tcp协议进行交互,同时它支持http协议(json格式)、thrift、servlet、memcached、zeroMQ等的传输协议(通过插件方式集成)。

2 为什么不用数据库做搜索?

查询语法复杂度高。
如:电商系统中查询商品数据 - select * from products where name like ‘%关键字%’ and price bewteen xxx and yyy and …。不同的用户提供的查询条件不同,需要提供的动态SQL过于复杂。
关键字索引不全面,搜索结果不符合要求。
如:电商系统中查询商品数据,条件为商品名包含’笔记本电脑’。那么对此关键字的分析结果为-笔记本、电脑等。对应的查询语法应该为 - select * from products where name like ‘%笔记本%’ or name like ‘%电脑%’ …
效率问题。
数据量越大,查询反应效率越低。

3 ES功能

3.1 分布式的搜索引擎和数据分析引擎

分布式:使用多节点分部存储海量数据,多节点同时提供服务。
搜索:类似百度搜索引擎的功能。
数据分析:电商中的销量排行,新闻中的点击访问排行等。

3.2 全文检索、结构化检索、数据分析
  • 全文检索 - 搜索商品名包含某关键字的信息。 select * from products where name like ‘%xxx%’
  • 结构化检索 - 搜索某种类的商品信息。 select * from products where category=‘xxx’
  • 数据分析 - 分析每个商品种类中有多少商品。 select category_name, count(*) from category group by category_name
3.3 海量数据近实时处理

通过分布式实现海量数据近实时处理。
近实时处理:在秒级别的时间内对数据进行处理和分析。(如果处理时长过长,如:1小时,则成为离线批处理【batch-processing,如:在电信营业厅登记查询最近3年的通话记录,需要3天后再取结果】)

4 ES常见使用场景

  • 维基百科,全文检索,高亮显示,搜索推荐
  • The Guardian(国外的一个新闻网站),此平台可以对用户的行为(点击、浏览、收藏、评论)、社区网络数据(对新闻的评论等)进行数据分析,为新闻的发布者提供相关的公众反馈。
  • Stack Overflow(国外的程序异常讨论论坛)
  • Github(开源代码管理),在千亿级别的代码行中搜索信息
  • 电商网站
  • 日志数据分析 - ELK技术
  • 商品价格监控网站,在网站中设置商品价格监控,达到监控值的时候,会自动提示用户。
  • BI系统,商业智能,Business Intelligence。如电信企业对不同地点的消费情况,进行分析,得到地方性套餐设定的建议。(ES实现数据的分析和挖掘、Kibana进行数据可视化)
  • 站内搜索,如电商、招聘、门户、企业内部系统等。
  • 数据分析

5 ES的特点

  • 分部集群,单机处理。可大可小
  • ES不是新技术,是将全文检索和数据分析、分布式整合到一起。
  • 开箱即用。对中小型应用(数据量小,操作简单)来说,部署简单(默认部署),3分钟可部署使用。
  • 补充数据库在现在互联网领域中的不足,如:全文检索、同义词处理、相关度排名、复杂数据分析、海量数据近实时处理等。

二、 ES核心概念

1 lucene和ES的关系

1.1 lucene

最先进、功能最强大的搜索库,基于lucene开发很复杂。(API复杂度高,需要深入理解内部原理【索引结构等】)

1.2 ES:

  • 基于lucene开发,提供简单的restful api接口、java api接口、其他语言开发接口等。
  • 实现了分布式的文档存储引擎,可支持PB级别的海量数据。
  • 实现了分布式的搜索引擎和分析引擎
  • 开箱即用,优秀的默认参数,不需要任何额外设置,完全开源。

1.3 lucene->ES的发展

ES核心开发程序员,失业后,陪妻子在伦敦学习厨师课程。学习期间帮妻子写了一个菜谱搜索引擎,觉得lucene太复杂,就开发封装了一个lucene的开源应用-compass。后来重新工作后,做分布式高性能项目,觉得compass功能不足,开发了ES,实现了lucene的分布式系统。可见外国程序员和国内程序员的思维区别。

2 ES的核心概念

2.1 倒排索引

对数据进行分析,抽取出数据中的词条,以词条作为key,对应数据的存储位置作为value,实现索引的存储。这种索引称为倒排索引。

2.2 Near Realtime (NRT)

近实时。有两层概念,

  • 从写入数据到可搜索数据有一个延迟(1秒左右,分析写入数据的过程);
  • 基于ES执行搜索和分析可以达到秒级别的响应。

2.3 Cluster&Node

  • Cluster-集群。包含多个节点,每个节点通过配置来决定属于哪一个集群(默认集群命名为“elasticsearch”)。对于中小型应用来说,最初只有一个节点也是很正常的。

  • Node-节点。集群中的一个节点,节点的名字默认是随机分配的。节点名字在运维管理时很重要,节点默认会自动加入一个命名为“elasticsearch”的集群,如果直接启动多个节点,则自动组成一个命名为“elasticsearch”的集群。当然单节点启动也是一个集群。

2.4 Document

文档。ES中的最小数据单元。一个Document就是一条数据,一般使用JSON数据结构表示。每个Index下的Type中都可以存储多个Document。一个Document中有多个field,field就是数据字段。如:

product document
{
"product_id":1,
"product_name":"xxx",
.....
}

注意:不要在Document中描述java对象的双向关联关系。在转换为JSON字符串的时候会出现无限递归问题。

2.5 Index

索引。包含若干相似结构的Document数据。如:客户索引,订单索引,商品索引等。
一个Index包含多个Document,也代表一类相似的或相同的Document。如:订单索引中存放了所有的订单数据(就是所有的订单Document)

2.6 Type

类型。每个索引中都可以有若干Type,Type是Index中的一个逻辑分类,同一个Type中的Document都有相同的field。
示例:订单索引,不同状态的订单包含不同的内容,如:未支付订单(自动取消时间)和已支付订单(支付时间)、已发货订单(发货时间、物流信息)等都有不同的内容。
ES6.x版本之后,type概念被弱化,一个index中只能有唯一的一个type。且在7.x版本之后,会删除type定义。

2.7 Shards

primary shard:代表索引的主分片,ElasticSearch 可以把一个完整的索引分成多个primary shard,这样的好处是可以把一个大的索引拆分成多个分片,分布存储在不同的ElasticSearch 节点上,从而形成分布式存储,并为搜索访问提供分布式服务,提高并发处理能。
primary shard 的数量只能在索引创建时指定,并且索引创建后不能再更改 primaryshard 数量。

2.8 Replicas

replica shard:代表索引主分片的副本,ElasticSearch 可以设置多个 replica shard。
replica shard 的作用:一是提高系统的容错性,当某个节点某个 primary shard 损坏或丢失时可以从副本中恢复。二是提高 ElasticSearch 的查询效率,ElasticSearch 会自动对搜索请求进行负载均衡,将并发的搜索请求发送给合适的节点,增强并发处理能力。

注意: 一个index分配到多个shard上,primary shard和他对应的replica shard不能在同一个节点中存储。


三、 Linux安装ES

使用的ES的版本是6.3.1。ES6.x要求Linux内核必须是3.5+版本以上。
在linux操作系统中,查看内核版本的命令是: uname -a
Linux localhost.localdomain 2.6.32-431.el6.x86_64 #1 SMP Fri Nov 22 03:15:09 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
使用的LInux是CentOS6.5。内核使用的是2.6。内核版本不满足要求。无法安装ES。必须升级内核。

1 升级Linux内核版本

系统内核的提升,对已部署的应用一般没有影响,但是新版本内核,对线程的资源给与了更多的空间。如:低版本内核每个线程分配128K内存,高版本内核可能分配256K内存。
注册内核

rpm –import http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-7

下载内核安装包

rpm -Uvh http://www.elrepo.org/elrepo-release-6-8.el6.elrepo.noarch.rpm

安装内核新版本

yum --enablerepo=elrepo-kernel install kernel-lt -y

2 设置启动内核版本

修改的是Linux系统的引导文件。

vim /etc/grub.conf

修改内容:

default=1  ->  default=0

3 重启系统让内核生效

使用命令: reboot

4 为ES提供完善的系统配置

ES在Linux中安装部署的时候,需要系统为其提供若干系统配置。如:应用可启动的线程数、应用可以在系统中划分的虚拟内存、应用可以最多创建多少文件等。

4.1 修改限制信息

vi /etc/security/limits.conf

是修改系统中允许应用最多创建多少文件等的限制权限。Linux默认来说,一般限制应用最多创建的文件是65535个。但是ES至少需要65536的文件创建权限。修改后的内容为:

* soft nofile 65536

* hard nofile 65536

4.2 修改线程开启限制

vi /etc/security/limits.d/90-nproc.conf

是修改系统中允许用户启动的进程开启多少个线程。默认的Linux限制root用户开启的进程可以开启任意数量的线程,其他用户开启的进程可以开启1024个线程。必须修改限制数为4096+。因为ES至少需要4096的线程池预备。ES在5.x版本之后,强制要求在linux中不能使用root用户启动ES进程。所以必须使用其他用户启动ES进程才可以。

*          soft    nproc     4096
root       soft    nproc     unlimited

注意:Linux低版本内核为线程分配的内存是128K。4.x版本的内核分配的内存更大。如果虚拟机的内存是1G,最多只能开启3000+个线程数。至少为虚拟机分配1.5G以上的内存。

4.3 修改系统控制权限

vi /etc/sysctl.conf 

系统控制文件是管理系统中的各种资源控制的配置文件。ES需要开辟一个65536字节以上空间的虚拟内存。Linux默认不允许任何用户和应用直接开辟虚拟内存。
新增内容为:

vm.max_map_count=655360

使用命令: sysctl -p 。 让系统控制权限配置生效。

4.4 可选配置(建议增加)

修改elasticsearch的配置文件,设置可访问的客户端。0.0.0.0代表任意客户端访问。
vi config/elasticsearch.yml
修改下述内容

network.host: 0.0.0.0

5 安装ES(单机版)

ES是java开发的应用。在6.3.1版本中,要求JDK至少是1.8.0_131版本以上。
ES的安装过程非常简单。解压立刻可以使用。

tar -zxf elasticsearch-6.3.1.tar.gz

如果想使用docker 安装elk可以通过这个博客进行安装, 步骤很详细!!!

5.1 修改ES应用的所有者

因为ES不允许root用户启动,而案例中,ES是root用户解压缩的。所以解压后的ES应用属于root用户。所以我们需要将ES应用的所有者修改为其他用户。当前案例中虚拟机Linux内有bjsxt这个用户。

chown -R bjsxt.bjsxt elasticsearch

5.2 切换用户并启动

su bjsxt
$es_home/bin/elasticsearch

5.3 测试连接

curl http://192.168.2.119:9200

返回如下结果:

{
  "name" : "L6WdN7y",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "s7_GSd9YQnaH10VQBKCQ5w",
  "version" : {
    "number" : "6.3.1",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "eb782d0",
    "build_date" : "2018-06-29T21:59:26.107521Z",
    "build_snapshot" : false,
    "lucene_version" : "7.3.1",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

6 安装ES(集群版)

共享模式下:

useradd sxt
echo sxt | passwd --stdin sxt

su sxt

root 用户创建 /opt/sxt/es(普通用户无法创建)

mkdir -p /opt/sxt/es (注意:此时的目录权限属于root)

在附近目录尚学堂下执行: chown sxt:sxt es


单节点模式下root用户:
安装解压程序

ftp拷贝至根目录下,或者software目录下(备用,可选)


切换回sxt用户(共享模式,可以看到software内容)

单节点模式:

使用sxt用户解压es2.2.1zip包至es目录,保证es文件属于用户sxt:

unzip elasticsearch-2.2.1.zip -d /opt/sxt/es

进入es/conf, 修改elastic配置文件:
修改:
---------------cluster-------------------------

cluster.name: bjsxt-es
----------------node-------------------------------
node.name: node06 (分发后各节点修改)

----------------network--------------------------------
network.host: 192.168.133.6 (分发后修改)

http.port:9200 (放开)

末尾增加防脑裂:
discovery.zen.ping.multicast.enabled: false 
discovery.zen.ping.unicast.hosts: ["192.168.133.6","192.168.133.7", "192.168.133.8"]
discovery.zen.ping_timeout: 120s
client.transport.ping_timeout: 60s


增加图形可视化插件:

[root@node06 software]# cp -abr plugins/ /opt/sxt/es/elasticsearch-2.2.1/

分发其他节点:
scp -r ./elasticsearch-2.2.1/ sxt@node07:`pwd`
scp -r ./elasticsearch-2.2.1/ sxt@node08:`pwd`

修改配置文件


进入bin目录,启动脚本:
[root@node06 bin]# ./elasticsearch

6.2 在集群中配置分词器

1.关闭es集群

2.共享模式sxt用户plugins下创建ik目录

3.ftp上传1个节点ik分词器到software目录下(便于拷贝)

4.plugins下sxt用户同步创建 ik目录

3.拷贝ik压缩包到software目录下,使用sxt用户解压 unzip -d (或者root用户拷贝压缩包到ik目录下,使用sxt解压)

4.修改ik的 vi plugin-descriptor.properties 配置文件,版本变为2.2.1/xxx 搜索)
# plugins with the incorrect elasticsearch.version.
elasticsearch.version=2.2.1

5.将 ik分发给别的节点:scp -r ik/ sxt@node07:`pwd`

6.重启动集群

7 安装Kibana

是ES提供的一个基于WEB的管理控制台。现阶段安装Kibana主要是为了方便学习。
在Linux中安装Kibana很方便。解压,启动即可。Kibana要求的环境配置是小于ES的要求的。

tar -zxf kibana-6.3.1-linux-x86_64.tar.gz

修改config/kibana.yml
新增内容:

server.host: "0.0.0.0"
$kibana_home/bin/kibana

访问时,使用浏览器访问http://ip:5601/

8 简单Kibana命令

8.1 查看健康状态

GET _cat/health?v 

在这里插入图片描述

8.2 检查分片信息

查看索引的shard信息。

GET _cat/shards?v

在这里插入图片描述

PUT _cluster/settings
{
  "transient": {
    "cluster.routing.allocation.disk.watermark.low": "95%",
    "cluster.routing.allocation.disk.watermark.high": "5gb"
  }
}

注意:
配置磁盘空间限制的时候,要求low必须比high大。可以使用百分比或gb的方式设置。且ES要求low至少满足磁盘95%的容量。
此处配置的百分比都是磁盘的使用百分比,如85%,代表磁盘使用了85%后如何限制。配置的GB绝对值都是剩余空间多少。
low - 对磁盘空闲容量的最低限制。默认85%。
high - 对磁盘空闲容量的最高限制。默认90%。
如:low为50gb。high为10gb。则当磁盘空闲容量不足50gb时停止分配replica shard。
当磁盘空闲容量不足10gb时,停止分配shard,并将应该在当前结点中分配的shard分配到其他结点中。
强调:red问题。因为ES中primary shard是主分片,要求必须全部活动才能正常使用。

8.3 设置磁盘限制

ES默认当磁盘空间不足15%时,会禁止分配replica shard。可以动态调整ES对磁盘空间的要求限制,命令如下:

在这里插入图片描述

8.4 查看索引信息

GET _cat/indices?v

8.5 新增索引

PUT /test_index

8.5 新增索引

PUT /test_index

在ES中,默认的创建索引的时候,会分配5个primary shard,并为每个primary shard分配一个replica shard。在ES中,默认的限制是:如果磁盘空间不足15%的时候,不分配replica shard。如果磁盘空间不足5%的时候,不再分配任何的primary shard。
创建索引时指定分片。

PUT /test_index
{
  "settings":{
    "number_of_shards" : 2,
    "number_of_replicas" : 1
  }
}

8.6 修改索引

注意:索引一旦创建,primary shard数量不可变化,可以改变replica shard数量。

PUT /test_index/_settings
{
  "number_of_replicas" : 2
}

ES中对shard的分布是有要求的。有其内置的特殊算法。ES尽可能保证primary shard平均分布在多个节点上。Replica shard会保证不和他备份的那个primary shard分配在同一个节点上。

8.7 删除索引

DELETE /test_index [, other_index]

8.8 总结:索引不可变

索引不可变的原因是倒排索引不可变。

8.8.1 倒排索引不可变的好处

不需要锁,提升并发能力,避免锁问题。数据不变,可以缓存在OS cache中(前提是cache足够大)。filter cache始终在内存中,因为数据是不可变的。可以通过压缩技术来节省CPU和IO的开销。

8.8.2 倒排索引不可变的坏处

倒排索引不可变,导致了ES中的index不可变。只要index的结构发生任何变化,都必须重建索引。重建索引的具体实现方式在后续课程中讲解。

8.9 新增Document

在索引中增加文档。在index中增加document。
ES有自动识别机制。如果增加的document对应的index不存在。自动创建,如果index存在,type不存在自动创建。如果index和type都存在,则使用现有的。

8.9.1 PUT语法
此操作为手工指定id的Document新增方式。

PUT /index_name/type_name/id{field_name:field_value}

如:

PUT /test_index/my_type/1
{
   "name":"test_doc_01",
   "remark":"first test elastic search",
   "order_no":1
}
PUT /test_index/my_type/2
{
   "name":"test_doc_02",
   "remark":"second test elastic search",
   "order_no":2
}
PUT /test_index/my_type/3
{
   "name":"test_doc_03",
   "remark":"third test elastic search",
   "order_no":3
}

结果

{
  "_index": "test_index", 新增的document在什么index中,
  "_type": "my_type", 新增的document在index中的哪一个type中。
  "_id": "1", 指定的id是多少
  "_version": 1, document的版本是多少,版本从1开始递增,每次写操作都会+1
  "result": "created", 本次操作的结果,created创建,updated修改,deleted删除
  "_shards": { 分片信息
    "total": 2, 分片数量只提示primary shard
    "successful": 1, 数据document一定只存放在index中的某一个primary shard中
    "failed": 0
  },
  "_seq_no": 0, 执行的序列号
  "_primary_term": 1 词条比对。
}

8.9.2 POST语法
此操作为ES自动生成id的新增Document方式。
语法:POST /index_name/type_name{fieldname:fieldvalue}
如:

POST /test_index/my_type
{
   "name":"test_doc_04",
   "remark":"forth test elastic search",
   "order_no":4
}

注意:在ES中,一个index中的所有type类型的Document是存储在一起的,如果index中的不同的type之间的field差别太大,也会影响到磁盘的存储结构和存储空间的占用。如:test_index中有test_type1和test_type2两个不同的类型,type1中的document结构为:{"_id":“1”,“f1”:“v1”,“f2”:“v2”},type2中的document结构为:{"_id":“2”,“f3”:“v3”,“f4”:“v4”},那么ES在存储的时候,统一的存储方式是{"_id":“1”,“f1”:“v1”,“f2”:“v2”,“f3”:"",“f4”:""}, {"_id":“2”,“f1”:"",“f2”:"",“f3”:“v3”,“f4”,“v4”}、建议,每个index中存储的document结构不要有太大的差别。尽量控制在总计字段数据的10%以内。

8.10 查询Document

8.10.1 GET查询
GET /index_name/type_name/id

如:

GET /test_index/my_type/1

结果

{
  "_index": "test_index",
  "_type": "my_type",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": { 找到的document数据内容。
    "name": "test_doc_01",
    "remark": "first test elastic search",
    "order_no":1
  }
}

8.10.2 GET _mget批量查询

批量查询可以提高查询效率。推荐使用(相对于单数据查询来说)。
语法:

GET /_mget
{
  "docs" : [
    {
      "_index" : "value",
      "_type" : "value",
      "_id" : "value"
    }, {}, {}
  ]
}


GET /index_name/_mget
{
  "docs" : [
    {
      "_type" : "value",
      "_id" : "value"
    }, {}, {}
  ]
}


GET /index_name/type_name/_mget
{
  "docs" : [
    {
     "_id" : "value1"
    },
    {
     "_id" : "value2"
    }
  ]
}

--------------------------------------2021.1.20更新--------------------------------------

8.10.3 全量替换文档

和新增的PUT or POST语法一致.
但是, 该操作的实质是将es中原有的Document改为delete状态, 再创建一个新的document来存储文档, 当es中数据量过大是才会回收delete状态的document

//先创建
POST my_index6/_doc/2
{
  "name":"chy3",
  "age":26,
  "sex":"male"
}
POST my_index6/_doc/3
{
  "name":"chy3",
  "age":26,
  "sex":"male"
}

//再替换
PUT my_index6/_doc/2
{
  "name":"test_recover",
  "age":23,
  "sex":"female"
}
PUT my_index6/_doc/2
{
  "name":"jinchunguang",
  "age":32,
  "sex":"female"
}
8.10.4 部分替换文档

语法: POST 索引名/类型名/主键id/_update{“doc”:字段值 }
该操作的实质也是将es中原有的Document改为delete状态, 再创建一个新的document来存储文档, 将新的字段和未更新的字段组成一个新的document

//部分更新,和全量替换实质上是一样的, 因此无需对比二者的优劣
POST my_index6/_doc/2/_update
{
  "doc":{
    "name":"jinchunguang11"
  }
}

8.10.5 删除文档

语法: Delete 索引名/类型名/主键id
实质上是标记删除, 和替换一样, 待空闲时再物理删除

DELETE my_index6/_doc/3

//再次查询 GET my_index6/_doc/3 时会返回下面数据
{
  "_index" : "my_index6",
  "_type" : "_doc",
  "_id" : "3",
  "found" : false   //表示已经删除
}
8.10.6 bulk批量操作文档

语法 POST bulk {json}
执行批量操作bulk,他的所有操作都会处理, 并且其中一个操作执行错误并不会影响后面的操作

注意:

  1. bulk语法要求json不换行,但不同的json必须使用换行符分隔
  2. 多个操作中, 如果有错误情况不会影响其他操作, 只会在批量操作返回结果中标记失败
  3. bulk语法要求json格式是为了对内存的方便管理, 尽可能降低内存压力
POST _bulk
{"create":{"_index" : "my_index6","_type" : "_doc","_id" : "4"}}
{"name":"创建bulk(不要换行)"}
{"index":{"_index" : "my_index6","_type" : "_doc","_id" : "5"}}
{"name": "创建索引bulk"}
{"index":{"_index" : "my_index6","_type" : "_doc","_id" : "2"}}
{"name":"创建更新bulk,覆盖之前的name属性"}
POST _bulk
{"update":{"_index" : "my_index6","_type" : "_doc","_id" : "2"}}
{"doc":{"name":"执行更新操作"}}
{"delete":{"_index" : "my_index6","_type" : "_doc","_id" : "4"}}

四. 分词器和标准化处理

1. 什么是分词器

分词器是一个字符串拆分的工具, 其作用是写入Document中的文本数据field, 并将field数据拆分成一个个有完整含义的, 不可拆分的单词

2. 什么是标准化处理

标准化处理是用来完善分词器结果的
分词器处理的结果通常是有一些不需要的, 有差异的, 包含时态转化等情况的数据. 如英文的大小写, 单复数, 时态等. ElasticSearch维护这些单词是没有太大必要的, 这时候就需要标准化处理了.

  • 如:china 搜索时,如果条件为 cn 是否可搜索到。如:dogs,搜索时,条件为 dog是否可搜索到数据。如果可以使用简写(cn)或者单复数(dog&dogs)搜索到想要的结果,那么称为搜索引擎人性化。
  • normalization 是为了提升召回率的(recall),就是提升搜索能力的。
  • normalization 是配合分词器(analyzer)完成其功能的。

3 ElasticSearch 默认提供的常见分词器

要切分的语句:Set the shape to semi-transparent by calling set_trans(5)
  • standard analyzer - 是 ElasticSearch 中的默认分词器。标准分词器,处理英语语法的分词器。切分后的 key_words:set, the, shape, to, semi, transparent, by, calling,set_trans, 5。这种分词器也是 ElasticSearch 中默认的分词器。切分过程中不会忽略停止词(如:the、a、an 等)。会进行单词的大小写转换、过滤连接符(-)或括号等常见符号

    GET _analyze
    {
    "text": "Set the shape to semi-transparent by calling set_trans(5)",
    "analyzer": "standard"
    }
    
  • simple analyzer - 简单分词器。切分后的 key_words:set, the, shape, to, semi,
    transparent, by, calling, set, trans。就是将数据切分成一个个的单词。使用较少,经常会
    破坏英语语法。

    GET _analyze
    {
    "text": "Set the shape to semi-transparent by calling set_trans(5)",
    "analyzer": "simple"
    }
    
  • whitespace analyzer - 空白符分词器。切分后的 key_words:Set, the, shape, to,
    semi-transparent, by, calling, set_trans(5)。就是根据空白符号切分数据。如:空格、制
    表符等。使用较少,经常会破坏英语语法。

    GET _analyze
    {
    "text": "Set the shape to semi-transparent by calling set_trans(5)",
    "analyzer": "whitespace"
    }
    
  • language analyzer - 语言分词器,如英语分词器(english)等。切分后的 key_words:
    set, shape, semi, transpar, call, set_tran, 5。根据英语语法分词,会忽略停止词、转换大
    小写、单复数转换、时态转换等,应用分词器分词功能类似 standard analyzer。

    GET _analyze
    {
    "text": "Set the shape to semi-transparent by calling set_trans(5)",
    "analyzer": "english"
    }
    

注意:ElasticSearch 中提供的常用分词器都是英语相关的分词器,对中文的分词都是
一字一词。

4. 安装中文分词器

IK 中文分词器,很少有直接下载使用的,都需要通过 github 下载源码,本地编译打包。
就是 maven 工程中的 package 能力。
github 上提供的源码不是伴随 ES 的每个版本提供,一般只有分词器无效后,才提供新
的版本。通常都是伴随 ES 的次版本号提供 IK 分词器版本。下载对应的 IK 分词器源码,本
地 package 打包,生成 zip 压缩包,既是 IK 在 ES 中的分词器安装包。
git clone https://github.com/medcl/elasticsearch-analysis-ik.git
git checkout tags/v6.5.0

如何安装和ES版本匹配的中文分词器?
  1. 查看当前es版本curl -XGET localhost:9200, number中就是es版本

    [root@docker01 ~]# curl -XGET localhost:9200
    {
      "name" : "elk",
      "cluster_name" : "elasticsearch",
      "cluster_uuid" : "hjMgyYH2SWOh725jwBAEMA",
      "version" : {
        "number" : "7.10.0",
        "build_flavor" : "oss",
        "build_type" : "tar",
        "build_hash" : "51e9d6f22758d0374a0f3f5c6e8f3a7997850f96",
        "build_date" : "2020-11-09T21:30:33.964949Z",
        "build_snapshot" : false,
        "lucene_version" : "8.7.0",
        "minimum_wire_compatibility_version" : "6.8.0",
        "minimum_index_compatibility_version" : "6.0.0-beta1"
      },
      "tagline" : "You Know, for Search"
    }
    
  2. 安装和es版本匹配的ik中文分词器
    https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.10.0
    在这里插入图片描述
    上传到服务器后解压(), 然后通过docker cp将其从本地复制到docker es上

    # 创建目录
    mkdir elasticsearch-analysis-ik-7.10.0
    # 复制插件
    cp elasticsearch-analysis-ik-7.10.0.zip elasticsearch-analysis-ik-7.10.0
    cd elasticsearch-analysis-ik-7.10.0/
    #解压
    unzip elasticsearch-analysis-ik-7.10.0.zip
    # 复制到docker elasticsearch上,elk为容器的名称
    docker cp elasticsearch-analysis-ik-7.10.0  elk:/opt/elasticsearch/plugins
    # 重启elk容器
    docker restart elk
    
  3. 测试中文分词器
    访问kibana的dev tools工具栏 http://kibanaip:kibana端口/app/dev_tools#/console, 输入下面内容测试

    GET _analyze
    {
    "text" : "中华人民共和国国歌",
    "analyzer": "ik_max_word"
    }
    
    GET _analyze
    {
    "text" : "中华人民共和国国歌",
    "analyzer": "ik_smart"
    }
    

    如果对中文出现了分词则说明安装成功了
    在这里插入图片描述
    在这里插入图片描述

IK 配置文件解读

IK 的配置文件在 ElasticSearch 安装目录/plugins/ik/config/中。

配置文件有:

  • main.dic : IK 中内置的词典。 main dictionary。记录了 IK 统计的所有中文单词。一行一词。文件中未记录的单词,IK 无法实现有效分词。如:雨女无瓜。不建议修改当前文件中的单词。这个是最核心的中文单词库。就好像,很多的网络词不会收集到辞海中一样。
  • quantifier.dic : IK 内置的数据单位词典
  • suffix.dic :IK 内置的后缀词典
  • surname.dic :IK 内置的姓氏词典
  • stopword.dic :IK 内置的英文停用词
  • preposition.dic :IK 内置的中文停用词(介词)
  • IKAnalyzer.cfg.xml : 用于配置自定义词库的
    自定义词库是用户手工提供的特殊词典,类似网络热词,特定业务用词等。
    ext_dict - 自定义词库,配置方式为相对于 IKAnalyzer.cfg.xml 文件所在位置的相对路径寻址方式。相当于是用户自定义的一个 main.dic 文件。是对 main.dic 文件的扩展。
    ext_stopwords - 自定义停用词,配置方式为相对于 IKAnalyzer.cfg.xml 文件所在位置的相对路径寻址方式。相当于是 preposition.dic 的扩展。
    在这里插入图片描述
    如果在这里不进行配置的话, 相当于只使用了默认的词典(下图圈起来的)
    如果需要进行配置的话, 只需要输入当前的词典名, 无需输入 .dic后缀
    在这里插入图片描述

注意:IK 的所有的 dic 词库文件,必须使用 UTF-8 字符集。不建议使用 windows 自
带的文本编辑器编辑。Windows 中自带的文本编辑器是使用 GBK 字符集。IK 不识别,是
乱码。

五. ElasticSearch 中的 mapping 问题

Mapping 在 ElasticSearch 中是非常重要的一个概念。决定了一个 index 中的 field 使用什么数据格式存储,使用什么分词器解析,是否有子字段等。Mapping 决定了 index 中的 field 的特征。

  1. mapping 核心数据类型
    ElasticSearch 中的数据类型有很多,在这里只介绍常用的数据类型。

    文本(字符串):text
    整数:byteshort、integer、long
    浮点型:floatdouble
    布尔类型:boolean
    日期类型:date
    数组类型:array {a:[]}
    对象类型:object {a:{}}
    不分词的字符串(关键字): keyword
    
  2. dynamic mapping 对字段的类型分配

    true or false -> boolean
    20 -> long
    888.88 -> double
    2021-01-26 -> date
    zhangsan -> text
    [] -> array
    {} -> object
    
    =>在kibana的dev tools界面插入一个含有多种数据类型的索引, 然后查看其mapping
    	PUT test_index/_doc/1
    {
      "name":"zhangsan",
      "age":20,
      "salary":"888.88",
      "isMarried":false,
      "hobbies":["eat","sleep","game"],
      "addr":{
        "province":"Jiangsu",
        "city":"Nanjing"
      },
      "birth":"2021-01-26"
    }
    
    =>查看索引mapping
    GET test_index/_mapping
    

在上述的自动 mapping 字段类型分配的时候,只有 text 类型的字段需要分词器。默认分词器是 standard 分词器。

  1. 创建自定义索引
    因为索引(index)的映射(mapping)一旦创建就允许更改了, 因此需要在创建索引时创建映射

    	==>查看映射
    	GET test_mapping_index5/_mapping
    	==>创建索引和映射
    	PUT test_mapping_index5
    	{
    	  "settings": {
    	   "number_of_shards": 2,
    	   "number_of_replicas": 1
    	  },
    	  "mappings": {
    	     "properties": {
    	           "author_id" : {
    	           "type": "byte",
    	           "index": false
    	           },
    	          "title" : {
    	          "type": "text",
    	          "analyzer": "ik_max_word",
    	                "fields": {
    	                "keyword" : {
    	                "type": "keyword",
    	                "ignore_above": 256
    	            }
    	            }
    	        },
    	          "content" : {
    	            "type": "text",
    	            "analyzer": "ik_max_word"
    	          },
    	          "post_date" : {
    	          "type": "date"
    	          }
    	        }
    	   }
    	}
    
  2. 为已有索引添加新的字段 mapping

    PUT /test_mapping_index5/_mapping/
    {
      "properties" : {
      "new_field" : { 
        "type" : "text" , "analyzer" : "standard" 
        }
      }
    }
    
  3. 测试不同的字段的分词器

    ==>验证分词器
    GET /test_mapping_index5/_analyze
    {
      "field": "new_field",
      "text": "中华人民共和国国歌"
    }
    GET /test_mapping_index5/_analyze
    {
      "field": "content",
      "text": "中华人民共和国国歌"
    }
    

六. Elastic Search 搜索详解

1. 搜索学习测试数据

PUT test_search
{
  "mappings": {
      "properties": {
        "dname" : {
        "type" : "text",
        "analyzer": "standard"
        },
        "ename" : {
        "type" : "text",
        "analyzer": "standard"
        },
        "eage" : {
        "type": "long"
        },
        "hiredate" : {
        "type": "date"
        },
        "gender" : {
        "type" : "keyword"
        }
      }
    }
}

==>批量插入测试数据
POST test_search/_bulk
{ "index": {}}
{ "dname" : "Sales Department", "ename" : " 张 三 ", "eage":20, "hiredate" : "2019-01-01","gender" : "男性" }
{ "index": {}}
{ "dname" : "Sales Department", "ename" : " 李 四 ", "eage":21, "hiredate" : "2019-02-01","gender" : "男性" }
{ "index": {}}
{ "dname" : "Development Department", "ename" : " 王 五 ", "eage":23, "hiredate" :"2019-01-03", "gender" : "男性" }
{ "index": {}}
{ "dname" : "Development Department", "ename" : " 赵 六 ", "eage":26, "hiredate" :"2018-01-01", "gender" : "男性" }
{ "index": {}}
{ "dname" : "Development Department", "ename" : " 韩 梅 梅 ", "eage":24, "hiredate" :"2019-03-01", "gender" : "女性"}
{ "index": {}}
{ "dname" : "Development Department", "ename" : " 钱 虹 ", "eage":29, "hiredate" :"2018-03-01", "gender" : "女性" }

2. query string search

search 的参数都是类似 http 请求头中的字符串参数提供搜索条件的。

语法

GET  [/index_name/type_name/]_search[?parameter_name=parameter_value&...]
2.1 全搜索

timeout 参数:是超时时长定义。代表每个节点上的每个 shard 执行搜索时最多耗时
多久。不会影响响应的正常返回。只会影响返回响应中的数据数量。
如:索引 a 中,有 10 亿数据。存储在 5 个 shard 中,假设每个 shard 中 2 亿数据,执行全数据搜索的时候,需要耗时 1000 毫秒。定义 timeout 为 10 毫秒,代表的是 shard执行 10 毫秒,搜索出多少数据,直接返回。
在商业项目中,是禁止全数据搜索的。必须指定搜索的索引,类型和关键字。如果没有指定索引或类型,则代表开发目的不明确,需要重新做用例分析。如果没有关键字,称为索引内全搜索,也叫魔鬼搜索

语法:

GET [索引名/类型名/]_search?timeout=10ms
eg:GET test_search/_search?timeout=10ms
{
	"took": 144, #请求耗时多少毫秒
	"timed_out": false, #是否超时。默认情况下没有超时机制,也就是客户端等待 ElasticSearch 搜索
	结束(无论执行多久),提供超时机制的话,ElasticSearch 则在指定时长内处理搜索,在指定时长结束的
	时候,将搜索的结果直接返回(无论是否搜索结束)。指定超时的方式是传递参数,参数单位是:毫秒-ms。
	秒-s。分钟-m。
	"_shards": {
		"total": 1, #请求发送到多少个 shard 上
		"successful": 1,#成功返回搜索结果的 shard
		"skipped": 0, #停止服务的 shard
		"failed": 0 #失败的 shard
		},
		"hits": {
		"total": 1, #返回了多少结果
		"max_score": 1, #搜索结果中,最大的相关度分数,相关度越大分数越高,_score 越大,排位越
		靠前。
		"hits": [ #搜索到的结果集合,默认查询前 10 条数据。
		{
		"_index": "test_index", #数据所在索引
		"_type": "my_type", #数据所在类型
		"_id": "1", #数据的 id
		"_score": 1, #数据的搜索相关度分数
		"_source": { # 数据的具体内容。
		"field": "value"
		}
		}
		]
	}
}
2.2 multi index 搜索

所谓的 multi-index 就是从多个 index 中搜索数据。相对使用较少,只有在复合数据
搜索的时候,可能出现。一般来说,如果真使用复合数据搜索,都会使用_all。

GET _search
GET 索引名 1,索引名 2/_search # 搜索多个 index 中的数据
GET 索引名/类型名/_search # 所属一个 index 中 type 的数据
GET prefix_*/_search # 通配符搜索
GET *_suffix/_search
GET 索引名 1,索引名 2/类型名/_search # 搜索多个 index 中 type 的数据
GET _all/_search # _all 代表所有的索引
2.3 条件搜索

query string search 搜索是通过 HTTP 请求的请求头传递参数的,默认的 HTTP 请求
头字符集是 ISO-8859-1,请求头传递中文会有乱码。

GET 索引名/_search?q=字段名:搜索条件
2.4 分页搜索

默认情况下,ElasticSearch 搜索返回结果是 10 条数据。从第 0 条开始查询。

GET 索引名/_search?size=10 # size 查询数据的行数
GET 索引名/_search?from=0&size=10 # from 从第几行开始查询,行号从 0 开始。
2.5 +/-搜索

+ :和不定义符号含义一样,就是搜索指定的字段中包含 key words 的数据
- : 与+符号含义相反,就是搜索指定的字段中不包含 key words 的数据

GET 索引名/_search?q=字段名:条件
GET 索引名/_search?q=+字段名:条件
GET 索引名/_search?q=-字段名:条件
2.6 排序

语法:GET 索引名/_search?sort=字段名:排序规则
排序规则: asc(升序) | desc(降序)

GET test_search/_search?sort=eage:asc
GET test_search/_search?sort=eage:desc

3 query DSL

DSL - Domain Specified Language , 特殊领域的语言。
请求参数是请求体传递的。在 ElasticSearch 中,请求体的字符集默认为 UTF-8。

语法格式:

GET 索引名/_search
{
"command":{ "parameter_name" : "parameter_value"}
}
3.1 查询所有数据

语法格式

GET 索引名/_search
{
"query" : { "match_all" : {} }
}

举例

GET test_search/_search
{
 "query":{
   "match_all": {}
 } 
}
3.2 match search

全文检索。要求查询条件拆分后的任意词条与具体数据匹配就算搜索结果

语法格式

GET 索引名/_search
{
	"query": {
		"match": {
		"字段名": "搜索条件"
		}
	}
}

举例:

GET test_search/_search
{
  "query":{
    "match":{
      "ename" : " 张三"
    }
  }
}
3.3 phrase search

短语检索。要求查询条件必须和具体数据完全匹配才算搜索结果。其特征是:1-搜索条
件不做任何分词解析;2-在搜索字段对应的倒排索引(正排索引)中进行精确匹配,不再是简单的全文检索。

语法格式

GET 索引名/_search
{
	"query": {
		"match_phrase": {
		"字段名": "搜索条件"
		}
	}
}

举例

GET test_search/_search
{
  "query":{
    "match_phrase":{
      "ename" : " 张三"
    }
  }
}
3.4 range

范围比较搜索

语法格式

	GET 索引名/类型名/_search
		{
			"query" : {
				"range" : {
				"字段名" : {
				"gt" : 搜索条件 1,
				"lte" : 搜索条件 2
			}
		}
	}
}

举例:

GET test_search/_search
{
  "query" : 
  {
    "range" : {
      "eage" : {
      "gt" :20,
      "lte" :30
      }
    }
  }
}
3.5 term

词组比较,词组搜索。
忽略搜索条件分词,在 ElasticSearch 倒排索引中进行精确匹配

语法格式

GET 索引名/类型名/_search
{
	"query" : {
		"term" : {
		"字段名": "搜索条件"
		}
	}
}
GET 索引名/类型名/_search
{
	"query" : {
		"terms" : {
		"字段名": ["搜索条件 1", "搜索条件 2"]
		}
	}
}

举例:

GET test_search/_search
{
  "query" : {
    "term" : {
      "dname": {
        "value": "sales"
      }
    }
  }
}

GET test_search/_search
{
  "query": {
    "term": {
      "ename": {
        "value": "张三"
      }
    }
  }
}
GET _analyze
{
  "text": ["张三"],
  "analyzer": "ik_max_word"
}

3.6 多条件复合搜索

在一个请求体中,有多个搜索条件,就是复合搜索。
如:搜索数据,条件为部门名称是Sales Department,员工年龄在 20 到 26 之间,部门员工姓名叫张三。上述条件中,部门名称为可选条件,员工年龄必须满足要求,部门员工姓名为可选要求。这种多条件搜索就是符合搜索。

举例:

GET test_search/_search
{
  "query":{
    "bool":{
      "must": [
        {
          "match": {
            "ename": "张"
          }
        },
        {
          "range": {
            "eage": {
              "gte": 20,
              "lte": 30
            }
          }
        }
      ]
    }
  }
}

GET test_search/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "dname": "sales"
          }
        },
        {
          "range": {
            "eage": {
              "gte": 20,
              "lte": 25
            }
          }
        }
      ]
    }
  }
}

GET test_search/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "match": {
            "dname": "sales"
          }
        },
        {
          "range": {
            "eage": {
              "gte": 20,
              "lte": 25
            }
          }
        }
      ]
    }
  }
}
3.7 排序

在 ElasticSearch 的搜索中,默认是使用相关度分数实现排序的。可以通过搜索语法实现定制化排序。

举例

GET test_search/_search
{
  "from": 0,
  "size": 5,
  "sort": [
    {
      "eage": {
        "order": "desc"
      }
    }
  ]
}

注意:在 ElasticSearch 中,如果使用 text 类型的字段作为排序依据,会有问题。
ElasticSearch 需要对 text 类型字段数据做分词处理。如果使用 text 类型字段做排序,
ElasticSearch 给出的排序结果未必友好,毕竟分词后,先使用哪一个单词做排序都是不合理的。所以 ElasticSearch 中默认情况下不允许使用 text 类型的字段做排序,如果需要使用字符串做结果排序,则可使用 keyword 类型字段作为排序依据,因为 keyword 字段不做分词处理。

3.8 分页

DSL 分页也是使用 from 和 size 实现的。

语法格式

GET 索引名称/_search
{
	"query":{
	"match_all":{}
	},
	"from": 起始下标,
	"size": 查询记录数
}

举例:

GET test_search/_search
{
  "from": 0,
  "size": 5,
}
3.9 highlight display

在搜索中,经常需要对搜索关键字做高亮显示,这个时候就可以使用 highlight 语法。

语法格式:

GET 索引名/_search
{
	"query": {
		"match": {
			"字段名": "条件"
			}
				},
				"highlight": {
				"fields": {
				"要高亮显示的字段名": {
				"fragment_size": 5, #每个分段长度,默认 20
				"number_of_fragments": 1 #返回多少个分段,默认 3
				}
		},
		"pre_tags": ["前缀"],
		"post_tags": ["后缀"]
	}
}

举例

GET test_search/_search
{
  "query": {
    "match": {
      "dname": "sales"
    }
  },
  "highlight": {
    "fields": {
      "dname": {
        "fragment_size": 2,
        "number_of_fragments": 1
      }
    },
    "pre_tags": "<em>",
    "post_tags": "</em>"
  }
}

fragment_size:代表字段数据如果过长,则分段,每个片段数据长度为多少。长度不是字符数量,是 ElasticSearch 内部的数据长度计算方式。默认不对字段做分段。number_of_fragments:代表搜索返回的高亮片段数量,默认情况下会将拆分后的所有片段都返回。
pre_tags:高亮前缀
post_tags:高亮后缀
很多搜索结果显示页面中都不会显示完整的数据,这样在数据过长的时候会导致页面效果不佳 , 都 会 按 照 某 一 个 固 定 长 度 来 显 示 搜 索 结 果 , 所 以 fragment_size 和number_of_fragments 参数还是很常用的。


附上中文分词器IK 6.8.4插件以及源码和IK 7.10.0插件(觉得有用的话可以点赞哦~~~)
链接:https://pan.baidu.com/s/1booUQarHTEmj2WOQ1raM-A
提取码:n4t5
复制这段内容后打开百度网盘手机App,操作更方便哦–来自百度网盘超级会员V3的分享

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时间静止不是简史

感谢你的肯定, 我将继续努力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值