目录
一、搜索的介绍
二、全文检索的介绍
三、ELK日志协议栈
1、ELK协议栈基本介绍
四、Elasticsearch介绍
1、什么是ElasticSearch
2、ElasticSearch使用案例
3、ElasticSearch对比Solr
4、ElasticSearch架构图以及基本概念(术语)
1、es概述
2、ES架构模块
3、Elasticsearch核心概念
五、ES的集群部署
六、node01服务器安装elasticsearch-head插件
1 、node01机器安装nodejs
2 、node01机器安装 elasticsearch-head 插件
七、node01服务器安装Kibana
八、使用kibana管理索引
1、使用 Xput创建索引
2、返回值说明
1、Hits
2、took
3、Shard
4、timeout
3、花式查询
4、索引映射(mappings)管理
1、为什么要映射
2、基本命令
3、获取映射字段
5、索引库配置管理(settings)
1、 索引库配置
2、 零停机重新索引数据
九、分页解决方案
1、导入数据
2、size+from浅分页
3、scroll深分页
十、三台机器安装IK分词器
十一、分片交互过程
1、创建索引
2、删除索引
3、查询索引
搜索是指搜寻检索,指代使用一定手段来检索到我们自己需要的信息,包括从文件当中检索,百度当中检索,网站内部搜索等等
首先我们谈几个公司,如雷贯耳的:百度、谷歌、维基百科;这些公司都有一个相似性就是门户网站,可以提供我们通过关键字搜索,然后快速的检索出我们想要的信息;
【网页百度展示】
比如我们检索传智播客,百度后台就会按照这个关键字进行查找(里面有搜索库,以及爬虫库),然后按照权重来进行从上到下的排序,给我们高亮的展示出现
【京东或者淘宝展示】
随便搜索东西,就会高精度的展示我们想要的;就会根据关键词进行海量数据的快速的检索
比如我们查找:”护手霜“ , 那么这期间内部会经过大体的:1、分词(护手,手霜,护等)2、根据这些词去海量的数据中检索 3、然后根据权重把检索出来的信息进行排序展示给我们
可以使用Lucene实现全文检索。Lucene是 apache下的一个开放源代码的全文检索引擎工具包(提供了Jar包,实现全文检索的类库)。它提供了完整的查询引擎和索引引擎,部分文本分析引擎。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便地在目标系统中实现全文检索的功能。
注意:Lucene只是一个引擎,只是一个工具包,如果使用Lucene开发全文检索功能,要记住Lucene是不能单独运行的。
1. 绿色表示索引过程,对要搜索的原始内容进行索引构建一个索引库,索引过程包括:确定原始内容即要搜索的内容→采集文档→创建文档→分析文档→索引文档。
2. 红色表示搜索过程,从索引库中搜索内容,搜索过程包括:用户通过搜索界面→创建查询→执行搜索,从索引库搜索→渲染搜索结果。
从上面了解到的知识点也可看出,索引和搜索流程图也可表示为:
市面上全文检索的框架很多,较早期的一个框架就是lucene,基本上所有的全文检索的工作都交给lucene来实现,但是lucene最大的弊端就是API太原生,没有经过任何封装,不太好使用。所以后来出现一个叫做solr的框架,它也是基于lucene进行改造封装和包装,将服务端单独提取出来,客户端进行请求即可。
另外一个框架就是大名鼎鼎的elasticsearch了,es也是一个基于lucene打造的全文检索的框架,且一经推出就迅速被市场认可,市场占有率越来越多,现在首选的全文检索的框架基本就是ES了。
日志,对于任何系统来说都是及其重要的组成部分。面对如此海量的数据,又是分布在各个不同地方,如果我们需要去查找一些重要的信息,难道还是使用传统的方法,去登陆到一台台机器上查看?看来传统的工具和方法已经显得非常笨拙和低效了。于是,一些聪明人就提出了建立一套集中式的方法,把不同来源的数据集中整合到一个地方。
一个完整的集中式日志系统,是离不开以下几个主要特点的。
ELK 其实并不是一款软件,而是一整套解决方案,是三个软件产品的首字母缩写,Elasticsearch,Logstash 和 Kibana。这三款软件都是开源软件,通常是配合使用,而且又先后归于 Elastic.co 公司名下,故被简称为 ELK 协议栈。
Elasticsearch
ES 是一个实时的分布式搜索和分析引擎,它可以用于全文搜索,结构化搜索以及分析。它是一个建立在全文搜索引擎 Apache Lucene 基础上的搜索引擎,使用 Java 语言编写。
主要特点:
Logstash
Logstash是一个具有实时渠道能力的数据收集引擎(像 flume)。使用 JRuby 语言编写。其作者是世界著名的运维工程师乔丹西塞 (JordanSissel)。
主要特点
它由三个主要部分组成
Kibana
Kibnan是一款基于 Apache 开源协议,使用 JavaScript 语言编写,为 Elasticsearch 提供分析和可视化的 Web 平台。它可以在 Elasticsearch 的索引中查找,交互数据,并生成各种维度的表图。
ELK官网
ELK官网文档
ELK中文手册
ELK中文社区
Elaticsearch,简称为es, es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。es也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
Elasticsearch是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在Elasticsearch中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。
Elasticsearch比传统关系型数据库如下:
Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields
Gateway是 ES 用来存储索引的文件系统,支持多种类型。
Gateway的上层是一个分布式的 lucene 框架。
Lucene之上是ES的模块,包括:索引模块、搜索模块、映射解析模块等
ES模块之上是 Discovery、Scripting 和第三方插件。
Discovery 是 ES 的节点发现模块,不同机器上的ES节点要组成集群需要进行消息通信,集群内部需要选举master节点,这些工作都是由 Discovery 模块完成。支持多种发现机制,如 Zen 、EC2、gce、Azure。
Scripting用来支持在查询语句中插入javascript、python等脚本语言,scripting模块负责解析这些脚本,使用脚本语句性能稍低。ES也支持多种第三方插件。
再上层是ES的传输模块和JMX.传输模块支持多种传输协议,如 Thrift、memecached、http,默认使用http。JMX是java的管理框架,用来管理ES应用。
最上层是ES提供给用户的接口,可以通过RESTful接口和ES集群进行交互。
1、索引 index
一个索引就是一个拥有几分相似特征的文档的集合。比如说,你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。在一个集群中,可以定义任意多的索引。
2、类型 type
在一个索引中,你可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定。通常,会为具有一组共同字段的文档定义一个类型。比如说,我们假设你运营一个博客平台并且将你所有的数据存储到一个索引中。在这个索引中,你可以为用户数据定义一个类型,为博客数据定义另一个类型,当然,也可以为评论数据定义另一个类型。
3、字段Field
相当于是数据表的字段,对文档数据根据不同属性进行的分类标识
4、映射 mapping
mapping是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分析器、是否被索引等等,这些都是映射里面可以设置的,其它就是处理es里面数据的一些使用规则设置也叫做映射,按着最优规则处理数据对性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能对性能更好。
5、文档 document
一个文档是一个可被索引的基础信息单元。比如,你可以拥有某一个客户的文档,某一个产品的一个文档,当然,也可以拥有某个订单的一个文档。文档以JSON(Javascript Object Notation)格式来表示,而JSON是一个到处存在的互联网数据交互格式。
在一个index/type里面,你可以存储任意多的文档。注意,尽管一个文档,物理上存在于一个索引之中,文档必须被赋予一个索引的 type。
6、集群 cluster
一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”。这个名字是重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群
7、节点 node
一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。和集群类似,一个节点也是由一个名字来标识的,默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。
一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。
在一个集群里,只要你想,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。
8、分片和复制 shards&replicas
一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。分片很重要,主要有两方面的原因: 1)允许你水平分割/扩展你的内容容量。 2)允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量。
至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由Elasticsearch管理的,对于作为用户的你来说,这些都是透明的。
在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。
复制之所以重要,有两个主要原因: 在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行。总之,每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制的数量,但是你事后不能改变分片的数量。
默认情况下,Elasticsearch中的每个索引被分片5个主分片和1个复制,这意味着,如果你的集群中至少有两个节点,你的索引将会有5个主分片和另外5个复制分片(1个完全拷贝),这样的话每个索引总共就有10个分片。
注意:ES不能使用root用户来启动,必须使用普通用户来安装启动。这里我们创建一个普通用户以及定义一些常规目录用于存放我们的数据文件以及安装包等
创建一个es专门的用户(必须)
#使用root用户在三台机器执行以下命令
useradd es
mkdir -p /export/servers/es
chown -R es /export/servers/es
passwd es
为了让普通用户有更大的操作权限,我们一般都会给普通用户设置sudo权限,方便普通用户的操作
三台机器使用root用户执行visudo命令然后为es用户添加权限
visudo
es ALL=(ALL) ALL
将es的安v装包下载并上传到node01服务器的/home/es路径下,然后进行解压
使用es用户来执行以下操作(断开连接linux的工具,然后重新使用es用户连接上三台linux服务器)
node01服务器使用es用户执行以下命令
cd /home/es/
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.7.0.tar.gz
tar -zxf elasticsearch-6.7.0.tar.gz -C /export/servers/es/
修改elasticsearch.yml
node01服务器使用es用户来修改配置文件
cd /export/servers/es/elasticsearch-6.7.0/config
mkdir -p /export/servers/es/elasticsearch-6.7.0/logs
mkdir -p /export/servers/es/elasticsearch-6.7.0/datas
rm -rf elasticsearch.yml
vim elasticsearch.yml
cluster.name: myes
node.name: node01
path.data: /export/servers/es/elasticsearch-6.7.0/datas
path.logs: /export/servers/es/elasticsearch-6.7.0/logs
network.host: 192.168.52.100
http.port: 9200
discovery.zen.ping.unicast.hosts: ["node01", "node02", "node03"]i
bootstrap.system_call_filter: false
bootstrap.memory_lock: false
http.cors.enabled: true
http.cors.allow-origin: "*"
修改 jvm.option
修改 jvm.option配置文件,调整 jvm 堆内存大小
node01使用 es 用户执行以下命令调整 jvm 堆内存大小,每个人根据自己服务器的内存大小来进行调整
cd /export/servers/es/elasticsearch-6.7.0/config
vim jvm.options
-Xms2g
-Xmx2g
node01使用 es 用户将安装包分发到其他服务器上面去
cd /export/servers/es/
scp -r elasticsearch-6.7.0/ node02:$PWD
scp -r elasticsearch-6.7.0/ node03:$PWD
node02与node03也需要修改es配置文件
node02使用es用户执行以下命令修改es配置文件
cd /export/servers/es/elasticsearch-6.7.0/config
vim elasticsearch.yml
cluster.name: myes
node.name: node02
path.data: /export/servers/es/elasticsearch-6.7.0/datas
path.logs: /export/servers/es/elasticsearch-6.7.0/logs
network.host: 192.168.52.110
http.port: 9200
discovery.zen.ping.unicast.hosts: ["node01", "node02", "node03"]
bootstrap.system_call_filter: false
bootstrap.memory_lock: false
http.cors.enabled: true
http.cors.allow-origin: "*"
node03使用es用户执行以下命令修改配置文件
cd /export/servers/es/elasticsearch-6.7.0/config/
vim elasticsearch.yml
cluster.name: myes
node.name: node03
path.data: /export/servers/es/elasticsearch-6.7.0/datas
path.logs: /export/servers/es/elasticsearch-6.7.0/logs
network.host: 192.168.52.120
http.port: 9200
discovery.zen.ping.unicast.hosts: ["node01", "node02", "node03"]
bootstrap.system_call_filter: false
bootstrap.memory_lock: false
http.cors.enabled: true
http.cors.allow-origin: "*"
由于现在使用普通用户来安装es服务,且es服务对服务器的资源要求比较多,包括内存大小,线程数等。所以我们需要给普通用户解开资源的束缚。
解决启动问题一:普通用户打开文件的最大数限制
问题错误信息描述:
max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536]
ES因为需要大量的创建索引文件,需要大量的打开系统的文件,所以我们需要解除linux系统当中打开文件最大数目的限制,不然ES启动就会抛错
三台机器使用es用户执行以下命令解除打开文件数据的限制
sudo vi /etc/security/limits.conf
添加如下内容: 注意*不要去掉了
* soft nofile 65536
* hard nofile 131072
* soft nproc 2048
* hard nproc 4096
解决启动问题二:普通用户启动线程数限制
问题错误信息描述
max number of threads [1024] for user [es] likely too low, increase to at least [4096]
修改普通用户可以创建的最大线程数
max number of threads [1024] for user [es] likely too low, increase to at least [4096]
原因:无法创建本地线程问题,用户最大可创建线程数太小
解决方案:修改90-nproc.conf 配置文件。
三台机器使用es用户执行以下命令修改配置文件
sudo vi /etc/security/limits.d/90-nproc.conf
找到如下内容:
* soft nproc 1024
#修改为
* soft nproc 4096
解决启动问题三:普通用户调大虚拟内存
错误信息描述:
max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144]
调大系统的虚拟内存
原因:最大虚拟内存太小
每次启动机器都手动执行下。
三台机器执行以下命令,注意每次启动ES之前都要执行
sudo sysctl -w vm.max_map_count=262144
备注:以上三个问题解决完成之后,重新连接secureCRT或者重新连接xshell生效 ,需要保存、退出、重新登录xshell才可生效。
三台机器使用es用户执行以下命令启动es服务
nohup /export/servers/es/elasticsearch-6.7.0/bin/elasticsearch 2>&1 &
启动成功之后jsp即可看到es的服务进程,并且访问页面
http://node01:9200/?pretty
能够看到es启动之后的一些信息
注意:如果哪一台机器服务启动失败,那么就到哪一台机器的
/export/servers/es/elasticsearch-6.7.0/logs
这个路径下面去查看错误日志
由于es服务启动之后,访问界面比较丑陋,为了更好的查看索引库当中的信息,我们可以通过安装elasticsearch-head这个插件来实现,这个插件可以更方便快捷的看到es的管理界面
Node.js是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
Node.js是一个Javascript运行环境(runtime environment),发布于2009年5月,由Ryan Dahl开发,实质是对Chrome V8引擎进行了封装。Node.js 是一个让 JavaScript 运行在服务端的开发平台,它让 JavaScript 成为与PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。
安装步骤参考:https://www.cnblogs.com/kevingrace/p/8990169.html
node01机器执行以下命令下载安装包,然后进行解压
cd /home/es
wget https://npm.taobao.org/mirrors/node/v8.1.0/node-v8.1.0-linux-x64.tar.gz
tar -zxvf node-v8.1.0-linux-x64.tar.gz -C /export/servers/es/
node01执行以下命令创建软连接
sudo ln -s /export/servers/es/node-v8.1.0-linux-x64/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm
sudo ln -s /export/servers/es/node-v8.1.0-linux-x64/bin/node /usr/local/bin/node
node01服务器添加环境变量
sudo vim /etc/profile
export NODE_HOME=/export/servers/es/node-v8.1.0-linux-x64
export PATH=:$PATH:$NODE_HOME/bin
修改完环境变量使用source生效
source /etc/profile
node01执行以下命令验证安装生效
elasticsearch-head 这个插件是 es 提供的一个用于图形化界面查看的一个插件工具,可以安装上这个插件之后,通过这个插件来实现我们通过浏览器查看es当中的数据
安装 elasticsearch-head 这个插件这里提供两种方式进行安装,第一种方式就是自己下载源码包进行编译,耗时比较长,网络较差的情况下,基本上不可能安装成功
第二种方式就是直接使用我已经编译好的安装包,进行修改配置即可
不做说明
第一步:上传压缩包到 /home/es 路径下去
将压缩包 elasticsearch-head-compile-after.tar.gz 上传到node01机器的 /home/es 路径下面去
第二步:解压安装包
node01执行以下命令解压安装包
第三步、node01机器修改Gruntfile.js
修改 Gruntfile.js 这个文件:
cd /export/servers/es/elasticsearch-head
vim Gruntfile.js
找到以下代码:
添加一行: hostname: '192.168.100.10',
connect: {
server: {
options: {
hostname: '192.168.100.10',
port: 9100,
base: '.',
keepalive: true
}
}
}
第四步、node01 机器修改 app.js
第一台机器修改app.js
cd /export/servers/es/elasticsearch-head/_site
vim app.js
更改前:http://localhost:9200
更改后:http://node01:9200
3、node01机器启动 head 服务
node01启动 elasticsearch-head 插件
cd /export/servers/es/elasticsearch-head/node_modules/grunt/bin/
进程前台启动命令
./grunt server
进程后台启动命令
nohup ./grunt server >/dev/null 2>&1 &
Running "connect:server" (connect) task
Waiting forever...
Started connect web server on http://192.168.52.100:9100
如何停止:elasticsearch-head进程
执行以下命令找到 elasticsearch-head 的插件进程,然后使用kill -9 杀死进程即可
netstat -nltp | grep 9100
kill -9 XXXX
4、访问elasticsearch-head界面
http://192.168.100.10:9100/
kibana的基本介绍
接着使用我们的 es 用户在 node01 服务器上面来实现我们的 kibana 的安装部署
node01服务器使用 es 用户执行以下命令来下载安装包并解压
cd /home/es
在线下载
wget https://artifacts.elastic.co/downloads/kibana/kibana-6.7.0-linux-x86_64.tar.gz
tar -zxf kibana-6.7.0-linux-x86_64.tar.gz -C /export/servers/es/
node01服务器使用 es 用户执行以下命令来修改配置文件
cd /export/servers/es/kibana-6.7.0-linux-x86_64/config/
vi kibana.yml
配置内容如下:
server.host: "node01"
elasticsearch.hosts: ["http://node01:9200"]
node01服务器使用es用户执行以下命令启动kibana服务
cd /export/servers/es/kibana-6.7.0-linux-x86_64
nohup bin/kibana >/dev/null 2>&1 &
如何停止kibana进程:停止kibana服务进程
查看进程号
ps -ef | grep node
然后使用kill -9杀死进程即可
浏览器地址访问kibana服务
http://node01:5601
curl是利用URL语法在命令行方式下工作的开源文件传输工具,使用curl可以简单实现常见的get/post请求。简单的认为是可以在命令行下面访问url的一个工具。在centos的默认库里面是有curl工具的,如果没有请yum安装即可。
curl
-X 指定 htt p的请求方法 有 HEAD GET POST PUT DELETE
-d 指定要传输的数据
-H 指定http请求头信息
在我们的kibana的dev tools当中执行以下语句
curl -XPUT http://node01:9200/blog01/?pretty
前面的命令使用 PUT 动词将一个文档添加到 /article(文档类型),并为该文档分配 ID 为1。URL 路径显示为index/doctype/ID(索引/文档类型/ID)。
curl -XPUT http://node01:9200/blog01/article/1?pretty -d '{"id": "1", "title": "What is lucene"}'
问题:Content-Type header [application/x-www-form-urlencoded] is not supported
解决:
curl -XPUT http://node01:9200/blog01/article/1?pretty -d '{"id": "1", "title": "What is lucene"}' -H "Content-Type: application/json"
原因:
此原因时由于ES增加了安全机制, 进行严格的内容类型检查,严格检查内容类型也可以作为防止跨站点请求伪造攻击的一层保护。
curl -XGET http://node01:9200/blog01/article/1?pretty
问题:Content-Type header [application/x-www-form-urlencoded] is not supported
解决:
curl -XPUT http://node01:9200/blog01/article/1?pretty -d '{"id": "1", "title": "What is lucene"}' -H "Content-Type: application/json"
curl -XGET http://node01:9200/blog01/article/1?pretty -H "Content-Type: application/json"
curl -XPUT http://node01:9200/blog01/article/1?pretty -d '{"id": "1", "title": " What is elasticsearch"}'
问题:Content-Type header [application/x-www-form-urlencoded] is not supported
解决:
curl -XPUT http://node01:9200/blog01/article/1?pretty -d '{"id": "1", "title": " What is elasticsearch"}' -H "Content-Type: application/json"
curl -XGET "http://node01:9200/blog01/article/_search?q=title:elasticsearch"
问题:Content-Type header [application/x-www-form-urlencoded] is not supported
解决:
curl -XGET "http://node01:9200/blog01/article/_search?q=title:'elasticsearch'&pretty" -H "Content-Type: application/json"
curl -XDELETE "http://node01:9200/blog01/article/1?pretty"
curl -XDELETE http://node01:9200/blog01?pretty
返回结果中最重要的部分是 hits ,它包含 total 字段来表示匹配到的文档总数,并且一个 hits 数组包含所查询结果的前十个文档。
在 hits 数组中每个结果包含文档的 _index 、 _type 、 _id ,加上 _source 字段。这意味着我们可以直接从返回的搜索结果中使用整个文档。这不像其他的搜索引擎,仅仅返回文档的ID,需要你单独去获取文档。
每个结果还有一个 _score ,它衡量了文档与查询的匹配程度。默认情况下,首先返回最相关的文档结果,就是说,返回的文档是按照 _score 降序排列的。在这个例子中,我们没有指定任何查询,故所有的文档具有相同的相关性,因此对所有的结果而言 1 是中性的 _score 。
max_score 值是与查询所匹配文档的 _score 的最大值。
took 值告诉我们执行整个搜索请求耗费了多少毫秒
_shards 部分 告诉我们在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个。正常情况下我们不希望分片失败,但是分片失败是可能发生的。
如果我们遭遇到一种灾难级别的故障,在这个故障中丢失了相同分片的原始数据和副本,那么对这个分片将没有可用副本来对搜索请求作出响应。假若这样,Elasticsearch 将报告这个分片是失败的,但是会继续返回剩余分片的结果。
timed_out 值告诉我们查询是否超时。默认情况下,搜索请求不会超时。 如果低响应时间比完成结果更重要,你可以指定 timeout 为 10 或者 10ms(10毫秒),或者 1s(1秒):
GET /_search?timeout=10ms
在请求超时之前,Elasticsearch 将会返回已经成功从每个分片获取的结果。
在 kibana 提供的界面上进行操作。
POST /school/student/_bulk
{ "index": { "_id": 1 }}
{ "name" : "liubei", "age" : 20 , "sex": "boy", "birth": "1996-01-02" , "about": "i like diaocan he girl" }
{ "index": { "_id": 2 }}
{ "name" : "guanyu", "age" : 21 , "sex": "boy", "birth": "1995-01-02" , "about": "i like diaocan" }
{ "index": { "_id": 3 }}
{ "name" : "zhangfei", "age" : 18 , "sex": "boy", "birth": "1998-01-02" , "about": "i like travel" }
{ "index": { "_id": 4 }}
{ "name" : "diaocan", "age" : 20 , "sex": "girl", "birth": "1996-01-02" , "about": "i like travel and sport" }
{ "index": { "_id": 5 }}
{ "name" : "panjinlian", "age" : 25 , "sex": "girl", "birth": "1991-01-02" , "about": "i like travel and wusong" }
{ "index": { "_id": 6 }}
{ "name" : "caocao", "age" : 30 , "sex": "boy", "birth": "1988-01-02" , "about": "i like xiaoqiao" }
{ "index": { "_id": 7 }}
{ "name" : "zhaoyun", "age" : 31 , "sex": "boy", "birth": "1997-01-02" , "about": "i like travel and music" }
{ "index": { "_id": 8 }}
{ "name" : "xiaoqiao", "age" : 18 , "sex": "girl", "birth": "1998-01-02" , "about": "i like caocao" }
{ "index": { "_id": 9 }}
{ "name" : "daqiao", "age" : 20 , "sex": "girl", "birth": "1996-01-02" , "about": "i like travel and history" }
GET /school/student/_search?pretty
{
"query": {
"match_all": {}
}
}
问题:通过 match_all 匹配后,会把所有的数据检索出来,但是往往真正的业务需求并非要找全部的数据,而是检索出自己想要的;并且对于 es 集群来说,直接检索全部的数据,很容易造成GC现象。所以,我们要学会如何进行高效的检索数据
GET /school/student/_search?pretty
{
"query": {
"match": {"about": "travel"}
}
}
如果此时想查询喜欢旅游的,并且不能是男孩的,怎么办?
【这种方式是错误的,因为一个match下,不能出现多个字段值[match] query doesn't support multiple fields】,需要使用复合查询
当出现多个查询语句组合的时候,可以用 bool 来 包含。bool合并聚包含:must,must_not或者should, should表示or的意思
例子:查询非男性中喜欢旅行的人
GET /school/student/_search?pretty
{
"query": {
"bool": {
"must": { "match": {"about": "travel"}},
"must_not": {"match": {"sex": "boy"}}
}
}
}
should 表示可有可无的(如果should匹配到了就展示,否则就不展示)
例子:
查询喜欢旅行的,如果有男性的则显示,否则不显示
GET /school/student/_search?pretty
{
"query": {
"bool": {
"must": { "match": {"about": "travel"}},
"should": {"match": {"sex": "boy"}}
}
}
}
使用 term 进行精确匹配(比如数字,日期,布尔值或 not_analyzed的字符串(未经分析的文本数据类型))
语法
{ "term": { "age": 20 }}
{ "term": { "date": "2018-04-01" }}
{ "term": { "sex": “boy” }}
{ "term": { "about": "trivel" }}
例子:查询喜欢旅行的
GET /school/student/_search?pretty
{
"query": {
"bool": {
"must": { "term": {"about": "travel"}},
"should": {"term": {"sex": "boy"}}
}}
}
GET /school/student/_search?pretty
{
"query": {
"bool": {
"must": { "terms": {"about": ["travel","history"]}}
}
}
}
term主要是用于精确的过滤比如说:”我爱你”
在 match 下面匹配可以为包含:我、爱、你、我爱等等的解析器
在 term 语法下面就精准匹配到:”我爱你”
Range过滤允许我们按照指定的范围查找一些数据:操作范围:gt::大于,gae::大于等于,lt::小于,lte::小于等于
例子:
查找出大于20岁,小于等于25岁的学生
GET /school/student/_search?pretty
{
"query": {
"range": {
"age": {"gt":20,"lte":25}
}
}
}
exists 和 missing 过滤可以找到文档中是否包含某个字段或者是没有某个字段
例子:
查找字段中包含age的文档
GET /school/student/_search?pretty
{
"query": {
"exists": {
"field": "age"
}
}
}
用bool也可以像之前match一样来过滤多行条件:
must :: 多个查询条件的完全匹配,相当于 and 。
must_not :: 多个查询条件的相反匹配,相当于 not 。
should :: 至少有一个查询条件匹配, 相当于 or
例子:过滤出about字段包含travel并且年龄大于20岁小于30岁的同学
GET /school/student/_search?pretty
{
"query": {
"bool": {
"must": [
{"term": {
"about": {
"value": "travel"
}
}},{"range": {
"age": { "gte": 20,"lte": 30
}
}}
]
}
}
}
通常复杂的查询语句,我们也要配合过滤语句来实现缓存,用filter语句就可以来实现
例子:
查询出喜欢旅行的,并且年龄是20岁的文档
GET /school/student/_search?pretty
{
"query": {
"bool": {
"must": {"match": {"about": "travel"}},
"filter": [{"term":{"age": 20}}]
}
}
}
elasticsearch中的文档等价于 java 中的对象,那么在 java 对象中有字段(比如string、int、long等),同理在 elasticsearch 索引中的具体字段也是有类型的。
PUT /document/article/1
{
"title" : "elasticsearchshi是是什么",
"author" : "zhangsan",
"titleScore" : 60
}
这种操作并没有指定字段类型,那么elasticsearch会自动根据数据类型的格式识别字段的类型;查看索引字段类型:GET /document/article/_mapping。可以发现titleScore的类型是long。
然后在插入一条数据:
PUT /document/article/2
{
"title" : "elasticsearchshi是是什么",
"author" : "zhangsan",
"titleScore" : 66.666
}
查询数据:GET /document/article/2
我们会发现es能存入,并没有报错(注意),这其实是一个问题,因为如果后期elaticsearch对接java的时候,我们会写一个类对数据做封装,比如:
class Article{
private String title;
private String author;
private String titleScore //《什么类型合适》?如果使用 long 类型,那么后面肯定会有数据格式转换的异常 doublelong
}
所以,我们如果能提前知道字段类型,那么最好使用mapping的映射管理,提前指定字段的类型,防止后续的程序问题;
DELETE document
PUT document
{
"mappings": {
"article" : {
"properties":
{
"title" : {"type": "text"} ,
"author" : {"type": "text"} ,
"titleScore" : {"type": "double"}
}
}
}
}
get document/article/_mapping
DELETE school
PUT school
{
"mappings": {
"logs" : {
"properties": {"messages" : {"type": "text"}}
}
}
}
添加索引:school,文档类型类logs,索引字段为message ,字段的类型为text
GET /school/_mapping/logs
继续添加字段
POST /school/_mapping/logs
{
"properties": {"number" : {"type": "text"}}
}
GET /school/_mapping/logs
语法:GET /{index}/_mapping/{type}/field/{field}
GET /school/_mapping/logs/field/number
所谓的 settings 就是用来修改索引分片和副本数的;
比如有的重要索引,副本数很少甚至没有副本,那么我们可以通过 setting 来添加副本数
DELETE document
PUT document
{
"mappings": {
"article" : {
"properties":
{
"title" : {"type": "text"} ,
"author" : {"type": "text"} ,
"titleScore" : {"type": "double"}
}
}
}
}
GET /document/_settings
可以看到当前的副本数是1,那么为了提高容错性,我们可以把副本数改成2:
PUT /document/_settings
{
"number_of_replicas": 2
}
副本可以改,分片不能改
PUT /document/_settings
{
"number_of_shards": 3
}
实际生产,对于文档的操作,偶尔会遇到这种问题:
某一个字段的类型不符合后期的业务了,但是当前的索引已经创建了,我们知道 es 在字段的 mapping 建立后就不可再次修改 mapping 的值。
1、新建索引库articles1,并添加数据
DELETE articles1
PUT articles1
{
"settings":{
"number_of_shards":3,
"number_of_replicas":1
},
"mappings":{
"article":{
"dynamic":"strict",
"properties":{
"id":{"type": "text", "store": true},
"title":{"type": "text","store": true},
"readCounts":{"type": "integer","store": true},
"times": {"type": "text", "index": false}
}
}
}
}
PUT articles1/article/1
{
"id" : "1",
"title" : "世界1",
"readCounts" : 2 ,
"times" : "2018-05-01"
}
get articles1/article/1
2、 新建索引库articles2
DELETE articles2
PUT articles2
{
"settings":{
"number_of_shards":5,
"number_of_replicas":1
},
"mappings":{
"article":{
"dynamic":"strict",
"properties":{
"id":{"type": "text", "store": true},
"title":{"type": "text","store": true},
"readCounts":{"type": "integer","store": true},
"times": {"type": "date", "index": false}
}
}
}
}
GET articles2/article/1
3、拷贝数据并验证
POST _reindex
{
"source": {
"index": "articles1"
},
"dest": {
"index": "articles2"
}
}
GET articles2/article/1
DELETE us
POST /_bulk
{ "create": { "_index": "us", "_type": "tweet", "_id": "1" }}
{ "email" : "[email protected]", "name" : "John Smith", "username" : "@john" }
{ "create": { "_index": "us", "_type": "tweet", "_id": "2" }}
{ "email" : "[email protected]", "name" : "Mary Jones", "username" : "@mary" }
{ "create": { "_index": "us", "_type": "tweet", "_id": "3" }}
{ "date" : "2014-09-13", "name" : "Mary Jones", "tweet" : "Elasticsearch means full text search has never been so easy", "user_id" : 2 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "4" }}
{ "date" : "2014-09-14", "name" : "John Smith", "tweet" : "@mary it is not just text, it does everything", "user_id" : 1 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "5" }}
{ "date" : "2014-09-15", "name" : "Mary Jones", "tweet" : "However did I manage before Elasticsearch?", "user_id" : 2 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "6" }}
{ "date" : "2014-09-16", "name" : "John Smith", "tweet" : "The Elasticsearch API is really easy to use", "user_id" : 1 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "7" }}
{ "date" : "2014-09-17", "name" : "Mary Jones", "tweet" : "The Query DSL is really powerful and flexible", "user_id" : 2 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "8" }}
{ "date" : "2014-09-18", "name" : "John Smith", "user_id" : 1 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "9" }}
{ "date" : "2014-09-19", "name" : "Mary Jones", "tweet" : "Geo-location aggregations are really cool", "user_id" : 2 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "10" }}
{ "date" : "2014-09-20", "name" : "John Smith", "tweet" : "Elasticsearch surely is one of the hottest new NoSQL products", "user_id" : 1 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "11" }}
{ "date" : "2014-09-21", "name" : "Mary Jones", "tweet" : "Elasticsearch is built for the cloud, easy to scale", "user_id" : 2 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "12" }}
{ "date" : "2014-09-22", "name" : "John Smith", "tweet" : "Elasticsearch and I have left the honeymoon stage, and I still love her.", "user_id" : 1 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "13" }}
{ "date" : "2014-09-23", "name" : "Mary Jones", "tweet" : "So yes, I am an Elasticsearch fanboy", "user_id" : 2 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "14" }}
{ "date" : "2014-09-24", "name" : "John Smith", "tweet" : "How many more cheesy tweets do I have to write?", "user_id" : 1 }
按照一般的查询流程来说,如果我想查询前10条数据:
from定义了目标数据的偏移值,size定义当前返回的事件数目
GET /us/_search?pretty
{
"from" : 0 , "size" : 5
}
GET /us/_search?pretty
{
"from" : 5 , "size" : 5
}
这种浅分页只适合少量数据,因为随from增大,查询的时间就会越大,而且数据量越大,查询的效率指数下降
优点:from+size在数据量不大的情况下,效率比较高
缺点:在数据量非常大的情况下,from+size分页会把全部记录加载到内存中,这样做不但运行速递特别慢,而且容易让es出现内存不足而挂掉
对于上面介绍的浅分页,当Elasticsearch响应请求时,它必须确定docs的顺序,排列响应结果。
如果请求的页数较少(假设每页20个docs), Elasticsearch不会有什么问题,但是如果页数较大时,比如请求第20页,Elasticsearch不得不取出第1页到第20页的所有docs,再去除第1页到第19页的docs,得到第20页的docs。
解决的方式就是使用scroll,scroll就是维护了当前索引段的一份快照信息--缓存(这个快照信息是你执行这个scroll查询时的快照)。
可以把 scroll 分为初始化和遍历两步: 1、初始化时将所有符合搜索条件的搜索结果缓存起来,可以想象成快照; 2、遍历时,从这个快照里取数据;
初始化
GET us/_search?scroll=3m
{
"query": {"match_all": {}},
"size": 3
}
初始化的时候就像是普通的search一样
其中的scroll=3m代表当前查询的数据缓存3分钟
Size:3 代表当前查询3条数据
遍历
在遍历时候,拿到上一次遍历中的scrollid,然后带scroll参数,重复上一次的遍历步骤,知道返回的数据为空,就表示遍历完成
GET /_search/scroll
{
"scroll" : "1m",
"scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAPXFk0xN1BmSnlVUldhYThEdWVzZ19xbkEAAAAAAAAAIxZuQWVJU0VSZ1JzcVZtMGVYZ3RDaFlBAAAAAAAAA9oWTVZOdHJ2cXBSOU9wN3c1dk5vcWd4QQAAAAAAAAPYFk0xN1BmSnlVUldhYThEdWVzZ19xbkEAAAAAAAAAIhZuQWVJU0VSZ1JzcVZtMGVYZ3RDaFlB"
}
【注意】:每次都要传参数scroll,刷新搜索结果的缓存时间,另外不需要指定index和type(不要把缓存的时时间设置太长,占用内存)
对比
浅分页,每次查询都会去索引库(本地文件夹)中查询pageNum*page条数据,然后截取掉前面的数据,留下最后的数据。 这样的操作在每个分片上都会执行,最后会将多个分片的数据合并到一起,再次排序,截取需要的。
深分页,可以一次性将所有满足查询条件的数据,都放到内存中。分页的时候,在内存中查询。相对浅分页,就可以避免多次读取磁盘。
我们在搜索的时候,都会对数据进行分词,英文的分词很简单,我们可以直接按照空格进行切分即可,但是中文的分词太过复杂,例如:夏天太热,能穿多少穿多少,冬天太冷,能穿多少穿多少。下雨地滑,还好我一把把车把把住了,才没有摔倒。人要是行,干一行行一行,一行行行行行等等的分词都是非常麻烦的,所以针对中文的分词,专门出了一个叫做IK的分词器来解决对中文的分词问题。
每台机器都要配置。配置完成之后,需要重启ES服务
将安装包上传到node01机器的/home/es路径下
cd /home/es
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.7.0/elasticsearch-analysis-ik-6.7.0.zip
# 将ik分词器的插件,上传到/home/es目录下
cd /home/es
mkdir /export/servers/es/elasticsearch-6.7.0/plugins/analysis-ik/
unzip elasticsearch-analysis-ik-6.0.0.zip -d /export/servers/es/elasticsearch-6.0.0/plugins/analysis-ik/
将安装包分发到其他机器上
node01机器执行以下命令进行安装包的分发
cd /export/servers/es/elasticsearch-6.0.0/plugins
scp -r analysis-ik/ node02:$PWD
scp -r analysis-ik/ node03:$PWD
# 三台机器都配置完成
# 配置完成之后,需要重启ES服务。
三台机器重启es服务
三台机器执行以下命令停止es服务
ps -ef|grep elasticsearch|grep bootstravelap |awk '{print $2}' |xargs kill -9
nohup /export/servers/es/elasticsearch-6.0.0/bin/elasticsearch >/dev/null 2>&1 &
2、配置
delete iktest
PUT /iktest?pretty
{
"settings" : {
"analysis" : {
"analyzer" : {
"ik" : {
"tokenizer" : "ik_max_word"
}
}
}
},
"mappings" : {
"article" : {
"dynamic" : true,
"properties" : {
"subject" : {
"type" : "text",
"analyzer" : "ik_max_word"
}
}
}
}
}
说明:ik带有两个分词器:
GET _analyze?pretty
{
"analyzer": "ik_max_word",
"text": "希拉里是个妹子"
}
POST /iktest/article/_bulk?pretty
{ "index" : { "_id" : "1" } }
{"subject" : ""闺蜜"崔顺实被韩检方传唤 韩总统府促彻查真相" }
{ "index" : { "_id" : "2" } }
{"subject" : "韩举行"护国训练" 青瓦台:决不许国家安全出问题" }
{ "index" : { "_id" : "3" } }
{"subject" : "媒体称FBI已经取得搜查令 检视希拉里电邮" }
{ "index" : { "_id" : "4" } }
{"subject" : "村上春树获安徒生奖 演讲中谈及欧洲排外问题" }
{ "index" : { "_id" : "5" } }
{"subject" : "希拉里团队炮轰FBI 参院民主党领袖批其”违法”" }
查看分词器
对"希拉里和韩国"进行分词查询
ikmaxword分词后的效果:希|拉|里|希拉里|和|韩国
POST /iktest/article/_search?pretty
{
"query" : { "match" : { "subject" : "希拉里和韩国" }},
"highlight" : {
"pre_tags" : [""],
"post_tags" : [""],
"fields" : {
"subject" : {}
}
}
}
查看分词效果
GET _analyze?pretty
{
"analyzer": "ik_max_word",
"text": "我在哪里"
}
使用es用户来进行配置tomcat,此处我们将tomcat装在node03机器上面即可
cd /home/es
tar -zxvf apache-tomcat-8.5.34.tar.gz -C /export/servers/es/
tomcat当中添加配置hot.dic
cd /export/servers/es/apache-tomcat-8.5.34/webapps/ROOT/
vi hot.dic
传智播客
# 启动tomcat
/export/servers/es/apache-tomcat-8.5.34/bin/startup.sh
浏览器访问
wget http://node03:8080/hot.dic
三台机器都要修改es的配置文件(使用es用户来进行修改即可)
第一台机器node01修改es的配置
cd /export/servers/es/elasticsearch-6.0.0/plugins/analysis-ik/config
vim IKAnalyzer.cfg.xml
修改完成之后拷贝到node02与node03机器上面去
node01执行以下命令进行拷贝
cd /export/servers/es/elasticsearch-6.0.0/plugins/analysis-ik/config
sudo scp IKAnalyzer.cfg.xml node02:$PWD
sudo scp IKAnalyzer.cfg.xml node03:$PWD
三台机器修改jdk的权限问题
第一台机器执行以下命令修改jdk的权限问题
#修改JDK安全设置
cd /export/servers/jdk1.8.0_141/jre/lib/security
sudo vim java.policy
添加以下四行配置
permission java.net.SocketPermission "192.168.100.10:8080","accept";
permission java.net.SocketPermission "192.168.100.10:8080","listen";
permission java.net.SocketPermission "192.168.100.10:8080","resolve";
permission java.net.SocketPermission "192.168.100.10:8080","connect";
修改完成之后拷贝到第二台和第三台机器
node01执行以下命令拷贝到第二台和第三台
cd /export/servers/jdk1.8.0_141/jre/lib/security
sudo scp java.policy node02:$PWD
sudo scp java.policy node03:$PWD
http://mahilion.blog.163.com/blog/static/1830872952012101225243655/
三台机器重新启动es服务,三台机器先使用kill -9杀死es的服务,然后再执行以下命令进行重启
cd /export/servers/es/elasticsearch-6.0.0/
nohup bin/elasticsearch >/dev/null 2>&1 &
查看我们的分词过程
GET _analyze?pretty
{
"analyzer": "ik_max_word",
"text": "我在哪里"
}
首先:发送一个索引或者删除的请求给node1
其次:node1介绍到请求之后,会根据请求中携带的参数“文档id”判断出该文档应该存储在具体哪一个shard中
shard = hash(routing) % numberofprimary_shards
,比如shard0;其次就是node1通过元数据信息可以知道shard0在具体哪一个节点,于是node1会把请求转发给node3
最后:node3接收到请求之后会将请求并行的分发给shard0的所有replica shard之上,也就是存在于node 1和node 2中的replica shard;如果所有的replica shard都成功地执行了请求,那么将会向node 3回复一个成功确认,当node 3收到了所有replica shard的确认信息后,则最后向用户返回一个Success的消息。
该过程可以分为四个阶段来描述:
阶段1:客户端向node 1发送一个文档删除的请求。
阶段2:同样的node 1通过请求中文档的 _id 值判断出该文档应该被存储在shard 0 这个分片中,并且node 1知道shard 0的primary shard位于node 3这个节点上。因此node 1会把这个请求转发到node 3。
阶段3:node 3接收到请求后,在主分片上面执行删除请求
阶段4:如果node 3成功地删除了文档,node 3将会请求并行地发给其余所有的replica shard所在node中。这些node也同样操作删除,执行后则向node 3确认成功,当node 3接收到所有的成功确认之后,再向客户端发送一个删除成功的信息。
检索文档的时候,我们并不知道文档在集群中的哪个位置,所以一般情况下不得不去询问index中的每一个shard,然后将结果拼接成一个大的已排好序的汇总结果列表;
(1):客户端发送一个检索请求给node3,此时node3会创建一个空的优先级队列并且配置好分页参数from与size。
(2):node3将检所请求发送给index中的每一个shard(primary 和 replica),每一个在本地执行检索,并将结果添加到本地的优先级队列中;
(3):每个shard返回本地优先级序列中所记录的_id与**score值**,并发送node3。Node3将这些值合并到自己的本地的优先级队列中,并做全局的排序(node 3将它们合并成一条汇总的结果),返回给客户端。