ELK介绍
本章节目的
了解什么是ELK
为什么要使用和学习ELK
1.什么是ELK
ELK 是elastic公司提供的一套完整的日志收集以及展示的解决方案,是三个产品的首字母缩写,分别是ElasticSearch、Logstash 和 Kibana。
1.1E-ELASTICSEARCH
ElasticSearch简称ES,它是一个实时的分布式搜索和分析引擎,它可以用于全文搜索,结构化搜索以及分析。它是一个建立在全文搜索引擎 Apache Lucene 基础上的搜索引擎,使用 Java 语言编写。
1.2L-LOGSTASH
Logstash是一个具有实时传输能力的数据收集引擎,用来进行数据收集(如:读取文本文件)、解析,并将数据发送给ES。
1.3K-KIBANA
Kibana为 Elasticsearch 提供了分析和可视化的 Web 平台。它可以在 Elasticsearch 的索引中查找,交互数据,并生成各种维度表格、图形。
2.为什么要使用ELK
2.1分布式集群
o数据量庞大:PB级别甚至更高
o搜索需求要求高:快,准,多维度全文搜索
ELASTICSEARCH就支持这种场景,也是目前全文搜索功能使用最多的一种技术.
2.2数据源丰富
o数据库数据
o日志数据
o其他分散存储的数据.
LOGSTASH可以从多数据源采集数据
2.3数据的价值
所谓数据保存的越多,数据提现的价值就越多.前提是需要分析展示数据.
KIBANA就可以提供数据的仪表分析等功能.
所以基于上述的一些问题,开源实时日志分析 ELK 平台能够完美的解决, ELK 由 ElasticSearch 、 Logstash 和 Kiabana 三个开源工具组成。
ELASTICSEARCH概括
本章节目的
掌握ELASTICSEARCH安装启动和集群配置
完成head插件的安装和启动
1.ES安装和启动
注意:如果使用达内云主机,无需安装es,直接配置yml启动即可
1.1安装
o下载ES最新版本的安装包
截止到本笔记时间,最新版本是7.10.2,ES的更新速度非常之快,但是我们应该合理运用ES的官方文档帮助我们实现学习和深入.
o上传到服务器解压安装
这里我们准备3个服务器分别上传,并且进行解压.使用的软件是开源免费的finalshell
调用命令解压:
[root@10-42-17-191 software]# tar -zxvf /home/resource/elasticsearch-7.10.2-linux-x86_64.tar.gz -C /home/software/
o配置es根目录下的jdk环境
当前版本的es7.10.2,要求最推荐使用jdk11以上的环境,根目录已经准备好了jdk15.
1.2启动
安装完成之后我们可以在服务器启动这个ES软件
o添加用户用户组
ES拒绝root用户直接启动软件,需要创建新用户,并且赋权启动.
[root@10-9-182-139 config]# groupadd tedu
[root@10-9-182-139 config]# useradd tedu -g tedu
o赋权
[root@10-42-17-191 config]# chown -R tedu:tedu /home/software/elasticsearch-7.10.2
o启动ES
[tedu@10-42-17-191 elasticsearch-7.10.2]$ ./bin/elasticsearch
o测试es进程
发送一个http请求,查看es
1.3配置集群启动
上述启动的是单节点进程,如果按照默认配置,实际上是多个服务器各自启动一个ES进程,想要将他们添加到同一个集群,需要进行配置.配置文件就是ES目录中的config/elasticsearch.yml文件:
cluster.name: es-cluster
node.name: es01
path.data: /home/software/elasticsearch-7.10.2/data
path.logs: /home/software/elasticsearch-7.10.2/logs
bootstrap.memory_lock: false
bootstrap.system_call_filter: false
network.host: 0.0.0.0
http.port: 9200
transport.tcp.port: 9300
discovery.seed_hosts: [“192.168.42.129”, “192.168.42.131”,“192.168.42.130”]
cluster.initial_master_nodes: [“192.168.42.129”]
http.cors.enabled: true
http.cors.allow-origin: “*”
ocluster.name
集群名称,同一个服务器集群的节点实例的集群名称要一致.
onode.name
节点名称,也就是你启动的每个es的进程的名字.
opath.data
数据路径,指定当前es进程节点的存储数据的文件夹.
注意:如果路径不在启动es用户的权限范围之内,启动会报错
opath.logs
es启动的日志文件夹.
注意:如果路径不在启动es用户的权限范围之内,启动会报错
obootstrap.memory_lock
锁定物理内容,会造成性能降低,将其关闭.
obootstrap.system_call_filter
系统调用过滤器主要是防止任意代码攻击漏洞的.将其关闭.
onetwork.host
es进程绑定的物理ip可以ipv4 可以ipv6,默认是localhost不允许外界访问.
ohttp.port
es进程绑定占用的http协议访问端口,默认9200.
otransport.tcp.port:
es进程绑定单用的tcp协议访问端口,默认9300
odiscovery.seed_hosts
配置主机hosts名单,主要可以使用ip:port的方式来配置.有几个节点,就配置几个节点.
ocluster.initial_master_nodes
集群第一次启动初始化时,想要指定的主机节点是谁,一旦配置,初始主机节点就定了,后续可以将这个配置删除.
ohttp.cors.enabled
是否支持跨域访问,如果支持,有一些代理软件是可以访问es的比如head插件
ohttp.cors.allow-origin
当设置了支持跨域后,允许的跨域域名都有哪些,默认是*.
将多个es节点按照需求配置完成所有内容,如下所示.
ES01节点:
cluster.name: es-cluster
node.name: es01
path.data: /home/software/elasticsearch-7.10.2/data
path.logs: /home/software/elasticsearch-7.10.2/logs
bootstrap.memory_lock: false
bootstrap.system_call_filter: false
network.host: 0.0.0.0
http.port: 9200
transport.tcp.port: 9300
discovery.seed_hosts: [“192.168.42.129”, “192.168.42.131”,“192.168.42.130”]
cluster.initial_master_nodes: [“192.168.42.129”]
http.cors.enabled: true
http.cors.allow-origin: “*”
ES02节点:
cluster.name: es-cluster
node.name: es02
path.data: /home/software/elasticsearch-7.10.2/data
path.logs: /home/software/elasticsearch-7.10.2/logs
bootstrap.memory_lock: false
bootstrap.system_call_filter: false
network.host: 0.0.0.0
http.port: 9200
transport.tcp.port: 9300
discovery.seed_hosts: [“192.168.42.129”, “192.168.42.131”,“192.168.42.130”]
cluster.initial_master_nodes: [“192.168.42.129”]
http.cors.enabled: true
http.cors.allow-origin: “*”
ES03节点:
cluster.name: es-cluster
node.name: es03
path.data: /home/software/elasticsearch-7.10.2/data
path.logs: /home/software/elasticsearch-7.10.2/logs
bootstrap.memory_lock: false
bootstrap.system_call_filter: false
network.host: 0.0.0.0
http.port: 9200
transport.tcp.port: 9300
discovery.seed_hosts: [“192.168.42.129”, “192.168.42.131”,“192.168.42.130”]
cluster.initial_master_nodes: [“192.168.42.129”]
http.cors.enabled: true
http.cors.allow-origin: “*”
最后将3个节点挨个启动.一个分布式的集群就搭建好了
o启动错误问题
http://blog.csdn.net/weixin_38361347/article/details/93386009
1.4测试访问
我们可以连接任意一个节点的9200端口进行集群的测试
http://192.168.42.129:9200 得到返回结果
{
“name” : “es01”,
“cluster_name” : “es-cluster”,
“cluster_uuid” : “h9ffNy1YRleRuimyJ0AXQA”,
“version” : {
“number” : “7.10.2”,
“build_flavor” : “default”,
“build_type” : “tar”,
“build_hash” : “747e1cc71def077253878a59143c1f785afa92b9”,
“build_date” : “2021-01-13T00:42:12.435326Z”,
“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的head插件
head插件是一个可以帮助用户代理访问es的,可以图形界面展示数据的插件.我们配置了es的跨域开启,所以为了方便观察,我们可以安装head插件
2.1node.js安装
head插件成功运行的语言环境就是node,需要提前安装,意义就类似于我们想运行tomcat就必须在jdk安装的前提下进行.
o下载
wget https://nodejs.org/dist/v15.0.0/node-v15.0.0-linux-x64.tar.gz
在这里可以手动选择比较新的版本,node的环境也是对应操作系统的,所以这里选择linux系统.
o解压
[root@localhost resource]# tar -xf node-v15.0.0-linux-x64.tar.gz -C /home/presoft/
o环境变量
打开/etc/profile
[root@localhost resource]# vim /etc/profile
编辑内容
NODE_HOME=/home/presoft/node-15
PATH= P A T H : . / : / h o m e / s o f t w a r e / e l a s t i c s e a r c h − 7.10.2 / j d k / b i n : PATH:./:/home/software/elasticsearch-7.10.2/jdk/bin: PATH:./:/home/software/elasticsearch−7.10.2/jdk/bin:NODE_HOME/bin
export PATH NODE_HOME
退出编辑内容,使得编辑生效.
[root@localhost resource]# source /etc/profile
o验证node安装
2.2head插件安装和启动
o下载head插件
我们可以从git上下载head插件.
[root@localhost resource]# yum -y install git
[root@localhost resource]# git clone https://github.com/mobz/elasticsearch-head.git
onpm安装
在head插件文件夹的根目录执行npm安装
[root@localhost elasticsearch-head]#npm install -g grunt
[root@localhost elasticsearch-head]npm install
o配置head文件(云主机必做)
在GruntFile.js中配置97(有可能版本不同有上下浮动)行内容,添加一个hostname
o启动head(云主机必做)
直接在head根目录运行命令启动,grunt server.
o访问这个地址
ELASTICSEARCH核心概念
本章节目的
了解ES的多个核心概念
1.ES中的重要概念
1.1索引(index)
类似于关系型数据中的库-database,一个es的集群中可以有多个索引,每个索引都是一批独立的存储数据,按照一定的数据结构保存,方便查询.
1.2类型(type)
类似于关系型数据库中的表格-table,一个索引中可以有多个类型,每个类型中的数据结构是一致的.
注意:6.x中使用类型,但是在7.x版本中,所有索引的类型只有一个叫做_doc,在8.x的版本将会彻底移除类型的概念.目的是提升索引的效率.
1.3映射(mapping)
类似于关系型数据库中定义的结构-schema,主要用来定义我们想要往es索引里存储的数据结构,到底是string还是数字,还是布尔等等.
1.4文档(document)
类似于关系型数据库中的行数据-rows,是es中存储数据的最小数据单元.每个文档都能根据数据的结构,有多个字段field存在.而field的类型,在存储数据时,是由mapping映射决定的.
1.5字段(field)
类似于关系型数据库中的列数据-columns,每个文档都由多个field组成.
1.6集群(cluster)
Es是天生分布式集群的结构,每个es进程都属于一个集群,即使只有一个Es进程在启动,它也是一个集群.不同集群由集群名称分开.
1.7节点(node)
每个ES进程都是一个节点,每个节点都有自己的名字.
1.8分片(shard)
单台机器存储数据量是有限的,es可以将一个index下的数据分为多个shard,存储在不同的机器上,横向扩展,存储更多的数据,而且可以让搜索,分析等操作分步到多个机器上去执行,提升吞吐量和性能。每个shard都是一个lucene index
1.9分片复制(Replica)
每台机器都可能会不可用,此时shard上的数据就可能会丢失。因此可以为每个shard建立多个副本,保证在一个shard不可用时还可以使用副本,且保证数据不丢失,还能提升查询性能。
注意:主分片的个数是在建立索引时定下的,不能修改,默认为5个,副本分片,可以随时修改,默认是1个。因为要保证高可用,所以每个分片的的主分片和副本分片不能在一台机器上,所以保证最小高可用配置,需要两台服务器
1.10接近实时(Near Reatime-NRT)
从写入数据到可以被搜索到会有一些延时,大概1秒左右,基于es执行搜索和分析可以达到秒级
ELASTICSEARCH索引数据
本章节目的
掌握CURL命令的使用
掌握索引的相关操作
1.REST命令
ES支持http协议的REST风格接口访问,所以我们需要一个"东西"帮助我们发送http请求,这个东西可以是插件,软件,比如head,kibana,也可以使用CURL命令完成
1.1CURL
curl命令,是linux系统中的一个命令脚本,支持http协议.它的语法格式为:
curl -X ‘😕/:/
上述语法中有2个选项-X -d -H还有一些<**>的变量,这是一个最基本的使用语法,我们先来掌握它的使用
o选项
-X:表示请求方式
-d:请求实体内容,es中的实体内容就是json格式字符串
-H:请求头信息
o参数:
比如我们想要向ES集群新增一个索引文件,并且按照漂亮的json展示响应体我们可以这样执行命令
curl -X PUT “localhost:9200/person/_doc/1?pretty” -H ‘Content-Type: application/json’ -d
‘{
“name”: “liulaoshi”
}’
如果严格按照语法中使用协议也可以把"*"中的请求url替换成 http://localhost:9200/person/_doc/1
1.2KIBANA的developer tool
简化用户使用操作es的方便工具.后续我们会一直使用.
2.索引的管理
2.1索引的创建
[root@localhost ~]# curl -XPUT “192.168.42.129:9200/book?pretty”
{
“acknowledged” : true,
“shards_acknowledged” : true,
“index” : “book”
}
2.2插入一个文档
所有数据文档在ES中都是存储的一个json字符串,我们可以在已存在的索引中添加我们的第一条文档数据.
[root@localhost ~]# curl -XPUT “192.168.42.129:9200/book/_doc/1?pretty” -d ‘{“name”:“王翠花”}’ -H ‘Content-type:application/json’
{
“_index” : “book”,
“_type” : “_doc”,
“_id” : “1”,
“_version” : 1,
“result” : “created”,
“_shards” : {
“total” : 2,
“successful” : 2,
“failed” : 0
},
“_seq_no” : 0,
“_primary_term” : 1
}
2.3查询一个文档
[root@localhost ~]# curl -XGET “192.168.42.129:9200/book/_doc/1?pretty”
{
“_index” : “book”,
“_type” : “_doc”,
“_id” : “1”,
“_version” : 1,
“_seq_no” : 0,
“_primary_term” : 1,
“found” : true,
“_source” : {
“name” : “王翠花”
}
}
2.4更新一个文档
和新增一样,重新增加一个id相同的文档数据
2.4删除一个文档
[root@localhost ~]# curl -XDELETE “192.168.42.129:9200/book/_doc/1?pretty”
{
“_index” : “book”,
“_type” : “_doc”,
“_id” : “1”,
“_version” : 2,
“result” : “deleted”,
“_shards” : {
“total” : 2,
“successful” : 2,
“failed” : 0
},
“_seq_no” : 1,
“_primary_term” : 1
}
2.4删除一个索引
新创建完索引,也可以查询一下这个索引.
[root@localhost ~]# curl -XDELETE “192.168.42.129:9200/book?pretty”
{
“acknowledged” : true
}
2.5批量索引
如果有许多文档需要索引,可以使用批量API批量提交它们。使用批量来批处理文档操作比单独提交请求要快得多,因为它减少了网络往返。这里可以使用es官方提供的一个数据集合,当然也可以自己准备.
这是一个.json文件,叫做accounts.json放到服务器中.
[root@localhost resource]# curl -H “Content-Type: application/json” -XPOST “192.168.42.129:9200/bank/_bulk?pretty” --data-binary “-”
3.搜索功能
ES提供了非常多种的搜索功能.我们可以从多维度搜索我们需要的数据.所有的查询条件在HTTP的REST风格中都是对应的json字符串.
3.1match_all
这是一个最简单的,没有任何查询约束的查询条件,它就是将当前的所有文档数据查询出来.
[root@localhost resource]# curl -XGET -H ‘Content-Type:application/json’
http://192.168.42.129:9200/bank/_search?pretty -d ‘{“query”:{“match_all”:{}}}’
这里可以对一个具体的索引bank进行搜索请求,然后携带一个请求数据就是我们json中的查询条件,为了后续展示方便我们只展示这个json格式数据.
{
“query”: {
“match_all”: {
}
}
}
3.2term
term:词项,在文档增加到es的时候进行分词计算的一个单位.是查询过程中一个计算使用的最基本单位.词项的组成结构 field:query
o分词计算: 将一个文本按照分次计算器的逻辑拆分的过程,就是分词计算,例如当我们把{“name”:“王翠花”}写进es的时候,es会将"王翠花" 计算拆分成 “王”,“翠”,"花"三个词项,当然,文本如何进行拆分也可以指定拆分逻辑,以方便我们更好的查询和组织查询逻辑.es默认计算分词的逻辑是将英文按照词语拆分,将中文按照字拆分.
所以词项term查询,返回的文档是包含查询提供的确切词项进行的,如果文档没包含这个词项,那么就不会在查询结果中展示.
{
“query”: {
“term”: {
“address”: “lane”
}
}
}
上述查询条件中,使用term查询,会返回文档中 address字段包含"lane"的所有数据,还可以在term查询中添加附加条件属性.
{
“query”: {
“term”: {
“address”: {
“value”: “lane”,
“boost”: 1.5
}
}
}
}
由于多添加了boost属性,使得每个查询结果的文档评分最终会以相乘boost的结果返回.所以value属性必须添加.
注意:由于分词计算的存在,避免使用term查询做全文的文本检索,例如
{
“query”: {
“term”: {
“address”: {
“value”: “929 Eldert Lane”,
“boost”: 1.5
}
}
}
}
这样的搜索是查不到数据的,因为不存在一个在address字段中的词项叫做"929 Eldert Lane".要使用全文文本检索,请使用match查询
3.3range
返回一个范围内的所包含的文档,这个范围有上限和下限.
{
“query”: {
“range”: {
“balance”: {
“gte”: 19000,
“lte”: 20000
}
}
}
}
该查询定义了balance的一个范围 [19000,20000].范围定义可以使用的四个值是
ogt:大于
olt:小于
ogte:大于等于
olte:小于等于
3.4exits
返回的是包含某些指定查询的字段,包含则返回不包含则不返回
{
“query”: {
“exists”: {
“field”: “name”
}
}
}
这里表示,文档的字段不能存在的原因可以有以下几个:
o写入的索引字段值在json中是null或者[]
o字段设置了"index":false的映射导致不会写入到索引中
o字段设置了ignore_above,当超出长度不会写入索引
3.5match
匹配查询(match)会返回我们提供的查询条件文本,日期,布尔,数字相匹配的文档数据,与term查询不同的是,match查询会在查询之前对我们提供的数据先进行分词计算,默认使用的分词计算器就是查询的索引中的分词器,我们也可以使用自定义分词器.
{
“query”: {
“match”: {
“address”: {
“query”: “880 Holmes Lane”
}
}
}
}
这里的结构和term一样,match下的第一级字段就是我们想要查询的文档字段,里面包含了一个属性query就是我们要查询的文本,这时,es会接收到请求,根据"880 Holmes Lane"文本进行解析 “880” “Holmes” “Lane” ,只要包含这3个词项的都会返回,默认情况下我们的查询文本最长会使用解析出来的50个分词.我们还可以指定词项之间的逻辑关系,默认是OR也就是三个词项查询的并集,我们还可以查询交集AND,只要在match查询条件中附加更多的属性.
{
“query”: {
“match”: {
“address”: {
“query”: “880 Holmes Lane”,
“operator”: “and”
}
}
}
}
3.6bool
由多个子查询组成的布尔查询,可以定义多个子查询对最终结果的影响逻辑关系.
{
“query”: {
“bool” : {
“must”:{
“match” : {“address”:“lane”}
}
}
}
}
这个布尔查询中,就只有一个子条件match,所以和单独使用match查询结果是一样的.must表示布尔查询结果必须是这个子条件的子集(子集概念要清楚,全部算子集,一部分算子集,空集也算子集).
{
“query”: {
“bool”: {
“must”: [
{
“match”: {
“address”: “lane”
}
},
{
“match”: {
“state”: “MD”
}
}
]
}
}
}
这里有2个子条件,都是must逻辑关系,所以最终查询结果是两个match的交集.既要address中有lane也要state是MD.
布尔查询中有4中逻辑关系值,我们见到了一个must
omust: 布尔结果必须是must子条件的查询子集
omust_not: 布尔结果必须不是must_not子条件的子集
oshould: 查询结果可能是也可能不是这个条件的子集,should和must同时使用,should的唯一作用就是影响最终相关性的评分计算.会让计算考虑多一个条件.
{
“query”: {
“bool”: {
“must”: {
“match”: {
“address”: “lane”
}
},
“should”: {
“match”: {
“address”: “eldert”
}
}
}
}
}
ofilter:查询结果必须是该条件子集,但是满足filter子条件的结果要忽略评分,也就是其他子条件的查询评分不会应为filter的存在而变化
{
“query”: {
“bool”: {
“must”: {
“match”: {
“address”: “lane”
}
},
“filter”: {
“match”: {
“address”: “eldert”
}
}
}
}
}
ELASTICSEARCH索引的映射和设置
本章节目的
掌握mapping映射的使用
了解setting设置
理解索引分片和副本
1.MAPPING
映射(MAPPING)就是es中一个决定了文档如何存储,如何生成索引,字段各种类型定义的过程.类似于我们在关系型数据库中创建一个表格数据之前先定义表格有哪些字段,每个字段是什么类型,然后数据会按照这个配置写入表格,ES中同样是这个过程,它由两种映射组成.一个是动态映射(dynamic mapping)一个是静态映射(explicit mapping).各自都具备各自的长处和短处,比如动态映射使得我们索引数据的时候很方便,静态映射是当我们想特指一些特殊的,或者需求需要的结构时使用.
注意:从7.0开始,es中将逐渐删除type类型的概念,所以和7.0之前的版本设置mapping有区别,不在添加自定义类型,到了8.0就会彻底消失.
1.1Dynamic Mapping(动态映射)
动态映射允许您在刚刚开始时就对数据进行试验和研究。Elasticsearch通过索引文档自动添加新字段。您可以向顶级映射、内部对象和嵌套字段添加字段。
总的来讲,使用dynamic mapping你不需要做任何修改和操作,都是默认的.
o查询一个mapping
当我们创建一个索引之后,在索引中随意添加一个数据,都会自动生成mapping映射.
[root@localhost ~]# curl -XPUT http://192.168.42.129:9200/index02?pretty -H ‘Content-Type:application/json’
创建完这个索引我们来查看一下mapping.
[root@localhost ~]# curl -XGET http://192.168.42.129:9200/index02/_mappings?pretty -H ‘Content-Type:application/json’
其返回结果如下,看起来是个空的配置.
{
“index02” : {
“mappings” : { }
}
}
o新增一个索引文档
当我们新增一个文档数据的时候,就会因为默认的mapping,动态生成对应结构.
[root@localhost ~]# curl -XPUT http://192.168.42.129:9200/index02/_doc/1?pretty -H ‘Content-Type:application/json’ -d ‘{“name”:“王翠花”,“address”:“北京市大兴区亦庄开发区科创十三街28号”,“sorted”:false,“employed_time”:“2020-01-05”,“location”:{“lat”:41.12,“lon”:-71.34},“salary”:20000.0,“ip”:“10.9.42.129”,“age”:19}’
其中包含的数据json如下:
{
“name”: “王翠花”,
“address”: “北京市大兴区亦庄开发区科创十三街28号”,
“sorted”: false,
“employed_time”: “2020-01-05”,
“location”: {
“lat”: 41.12,
“lon”: -71.34
},
“salary”: 20000.0,
“ip”: “10.9.42.129”,
“age”: 19
}
在这个案例中,我们由数据字符串name,address,有日期 employed_time,有地理位置location 有浮点数salary 有ip地址 ip,还有整数一些常用的类型数据都包含在内.
o查看动态生成的mapping
[root@localhost ~]# curl -XGET http://192.168.42.129:9200/index02/_mappings?pretty -H ‘Content-Type:application/json’
得到的返回结果如下,这些都是动态mapping映射生成的结构.
{
“index02” : {
“mappings” : {
“properties” : {
“address” : {
“type” : “text”,
“fields” : {
“keyword” : {
“type” : “keyword”,
“ignore_above” : 256
}
}
},
“age” : {
“type” : “long”
},
“employed_time” : {
“type” : “date”
},
“ip” : {
“type” : “text”,
“fields” : {
“keyword” : {
“type” : “keyword”,
“ignore_above” : 256
}
}
},
“location” : {
“properties” : {
“lat” : {
“type” : “float”
},
“lon” : {
“type” : “float”
}
}
},
“name” : {
“type” : “text”,
“fields” : {
“keyword” : {
“type” : “keyword”,
“ignore_above” : 256
}
}
},
“salary” : {
“type” : “float”
},
“sorted” : {
“type” : “boolean”
}
}
}
}
}
这个JSON的基本格式是这样的:
{
“indexName”: {
“mappings”: {
“properties”: {
“所有字段的对应映射结构”: “结构的值”
}
}
}
}
我们可以分字段来讨论这些结构
字符串类型
在数据中我们字符串类型有name,address和ip,他们动态映射结构是一致的
“address”: {
“type”: “text”,
“fields”: {
“keyword”: {
“type”: “keyword”,
“ignore_above”: 256
}
}
}
address表示字段名称,后面的{}内容就是描述这个字段在映射中的结构.
1.type:表示字段类型,每一个字段都必须包含一个类型的属性,字符串字段会添加一个text类型
2.fields:可选的一个属性,它表示给当前字段扩展的属性,扩展了一个类型keyword,这样一来,当前字符串address就既具备text类型的特点,也具备keyword类型的特点.
3.text和keyword类型区别:都可以表示字符串,但是text类型会被分词器计算,默认使用索引的分词器,而keyword不会被计算分词,例如:名字,邮箱,id值,url地址这些文本计算分词是没有意义的.我们可以使用分词计算词项查询该字段,也可以在字段上添加*.keyword使用keyword类型查询数据的整体内容,ignore_above表示当我这个字段的长度大于256时,keyword扩展类型消失
整数类型
“age”: {
“type”: “long”
}
age字段,类型是long,所以整数默人映射是long,还可以时integer
浮点数类型
“salary” : {
“type” : “float”
}
salary字段,类型float,默人浮点数类型float,还可以时double
日期类型
“employed_time” : {
“type” : “date”
}
employed_time字段,日期类型,默人date yyyy-MM-dd
对象类型
“location”: {
“properties”: {
“lat”: {
“type”: “float”
},
“lon”: {
“type”: “float”
}
}
}
location字段,在写入索引文档时,location字段对应的是{}结构,所以在动态映射中对应它指定了对象类型,对于这个json里包含的lat,和lon继续按照默认配置指定了浮点数float类型
1.2EXPLICIT MAPPING(静态映射)
我们自己肯定比ES更了解我们的数据,如果需要我们可以在创建索引时就指定mapping或者在一个已存在的索引中添加mapping.
o创建索引时添加mapping
[root@localhost ~]# curl -XPUT http://192.168.42.129:9200/index03?pretty -H ‘Content-Type:application/json’ -d ‘{“mappings”:{“properties”:{“email”:{“type”:“keyword”}}}}’
在这个命令汇总我们添加了一个索引并且指定了一个mappings.
{
“mappings”: {
“properties”: {
“email”: {
“type”: “keyword”
}
}
}
}
这个mapping定义了email这个字段只有文本的keyword属性,并且不会被索引,也就是存储在索引中不能使用该字段搜索.
o在已存在的索引中添加mapping
[root@localhost ~]# curl -XPUT http://192.168.42.129:9200/index03/_mapping?pretty -H ‘Content-Type:application/json’ -d ‘{“properties”:{“id”:{“type”:“keyword”}}}’
这里我们新增了一个字段的mapping.
{
“properties”: {
“id”: {
“type”: “keyword”
}
}
}
o更新索引的映射
正常情况下,我们不能更新一个已存在的索引mapping,这会导致索引数据的无效.
o查看一个索引mapping
[root@localhost ~]# curl -XGET http://192.168.42.129:9200/index03/_mapping?pretty
o查看一个索引字段的mapping
可以一次只查看一个或几个字段的mapping映射.
[root@localhost ~]# curl -XGET http://192.168.42.129:9200/index03/_mapping/field/id?pretty
它的返回值是:
{
“index03”: {
“mappings”: {
“id”: {
“full_name”: “id”,
“mapping”: {
“id”: {
“type”: “keyword”
}
}
}
}
}
}
也可以一次查询多个字段,只要在字段位置使用逗号隔开多个字段即可
[root@localhost ~]# curl -XGET http://192.168.42.129:9200/index03/_mapping/field/id,email,name?pretty
2.SETTING设置
每一个索引都有一个settings属性,这里就是对该索引的一些设置值.也有动态和静态之分,静态索引创建后不可修改的值,动态是索引在使用时也可以修改,比如分片和副本
2.1分片
为了让一个索引文件行程并行读写,提升效率,每一个索引都有一个设置的属性叫做分片,在7.10.2版本中,索引的分片默认是1,这也就是为什么我们在搭建es之后通过head插件能够看到一个索引有一个分片.可以修改number_of_shards来决定分片个数.
curl -XPUT “http://10.42.60.224:9200/index07” -H ‘Content-Type: application/json’ -d’{ “settings”: { “number_of_shards”: 5 }}’
2.2副本
为了让一个索引的每一个分片都是高可用的,不会因为部分分片不可用导致整个索引丢失数据,可以引入副本的配置,每一个分片默认都是有一个副本的,副本的设置是动态的,所以我们可以对一个正在使用的索引修改他的副本数量,属性名称 index.number_of_replicas
[root@localhost ~]# curl -XPUT http://192.168.42.129:9200/index03/_settings?pretty -H ‘Content-Type:application/json’ -d ‘{“index”:{“number_of_replicas”:2}}’
ELASTICSEARCH分词计算和热词更新
本章节目的
了解分词器的概念
掌握IK分词器和热词配置
1.分词
ES中为了方便查询,提供多维度的查询功能,对存储在索引中的文档进行分词计算,但是文本内容不同,类型不同,语言不同分词计算逻辑就不会一样.
1.1概括
文本分析使Elasticsearch能够执行全文搜索,其中搜索返回所有相关结果,而不仅仅
是精确匹配.
如果您搜索"王者荣耀",您可能希望包含"王者","荣耀"和"王者荣耀"的文档,还可能希望包含相关"王"或"者"的文档。
oTokenization
该过程将文本拆分成一小块一小块,这一小块内容称之为token,大多数情况下一个token代表着一个词语;
oNormalization
词条化允许在单个术语上进行匹配,但是每个标记仍然是字面上匹配的。这就意味着:
1.搜索"Quick"不会匹配"quick",即使你觉得或希望这样
2.虽然"fox"和"foxes"有着相同的词根,但是对于fox的搜索并不匹配foxes,反之亦然。
3.搜索"jumps"不会匹配"leaps",他们不同根,但是同义.
这些问题可以通过Normalizatin解决,将词条规范化标记.这就允许你不仅能使用精确的匹配搜索,还可以使用相关性查询.
1.2分词器
应对不同的分词计算逻辑,ES中使用了底层的分词器.
ostandard analyzer
这是一个标准分词器,它可以应对多种不同的语言文本环境,是es的默认分词器.默认分词器,如果未指定,则使用该分词器。按词切分,支持多语言.小写处理,它删除大多数标点符号、小写术语,并支持删除停止词
[root@localhost ~]# curl -X POST “localhost:9200/_analyze?pretty” -H ‘Content-Type: application/json’ -d’{“text”:“王者荣耀”,“analyzer”:“standard”}’
这个命令是打算使用standard对王者荣耀进行分词计算.它的返回结果是:
{
“tokens”: [
{
“token”: “王”,
“start_offset”: 0,
“end_offset”: 1,
“type”: “”,
“position”: 0
},
{
“token”: “者”,
“start_offset”: 1,
“end_offset”: 2,
“type”: “”,
“position”: 1
},
{
“token”: “荣”,
“start_offset”: 2,
“end_offset”: 3,
“type”: “”,
“position”: 2
},
{
“token”: “耀”,
“start_offset”: 3,
“end_offset”: 4,
“type”: “”,
“position”: 3
}
]
}
osimple analyzer
按照非字母切分,简单分词器在遇到不是字母的字符时将文本分解为term小写处理,所有条款都是小写的。
oWhitespace Analyzer
空白字符作为分隔符,当遇到任何空白字符,空白分词器将文本分成term。
oStop Analyzer
类似于Simple Analyzer,但相比Simple Analyzer,支持删除停止字.停用词指语气助词等修饰性词语,如the, an, 等
1.3IK分词器
内置的分词器可以处理一下通用场景,对于中文来讲常用的是IK分词器,ES也支持IK分词器的插件,IK分词器是基于词典的分词器,这让我们可以自行扩展分词的词语.
o安装下载IK分词器插件
首先我们可以下载IK插件,这个版本是和ES严格对应的.
https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v710.2/elasticsearch-analysis-ik-7.10.2.zip
上传到服务器中,然后我们解压它到ES的plugins目录中即可.
注意: 多个ES节点的集群中,所有节点都需要配置IK分词器,否则计算会报错
o重启ES测试IK分词器
需要将ES重新启动以加载IK分词器插件.
[root@localhost ~]# curl -X POST “localhost:9200/_analyze?pretty” -H ‘Content-Type: application/json’ -d’{“text”:“王者荣耀”,“analyzer”:“ik_max_word”}’
这里可以选择2个不同名称的分词器,一个是ik_smart 一个是ik_max_word
前者是粗粒度分词,后者是细粒度分词计算.测试返回结果如下.
{
“tokens” : [
{
“token” : “王者”,
“start_offset” : 0,
“end_offset” : 2,
“type” : “CN_WORD”,
“position” : 0
},
{
“token” : “荣耀”,
“start_offset” : 2,
“end_offset” : 4,
“type” : “CN_WORD”,
“position” : 1
}
]
}
可以看到相对es内置的分词器,IK可以对中文做比较基本的词语分词计算,但是这不能是永远不变的.
oIK词典的配置
在我们解压的ik分词器的文件夹中**/plugins/ik/config有一个xml配置文件可以指定词典使用.
这可配置文件可以指定本地词典和远程词典
无论是远程还是本地,都可以有2种词典,一种是扩展,一种是扩展停用词典.扩展词典就是我们根据词典的配置填写准备我们想要让ik切分出来的词语,而停用词典就是停止计算一些无意义或少意义的词语,例如"这个"“哪些”“还是”"一个"等等类似于英文中的 a the an this that等
注意:使用本次词典,配置文件需要指定相对路径,一般我们都将词典和配置xml放到同一个目录中.
o本地词典扩展词典和停用词典
我们可以配置2个词典,一个叫做ext.dic一个叫做stop.dic.并且在IKAnalyzer.cfg.xml中配置2个词典
ext.dic
王者荣耀
stop.dic
王者
IKAnalyzer.cfg.xml
o远程词典热词更新
我们可以在任意一个远程的web服务器资源中配置这些词典,从而使IK分词器可以通过网络访问这些频繁变动的资源.
这里我们选择使用tomcat服务器 设置一个远程词典测试 http://192.168.42.130:8080/hot.dic 作为远程热词资源.并且在其中我们添加一个测试词语 “王者荣”
然后我们在每一个IK分词器的配置xml中指定远程扩展词典
IKAnalyzer.cfg.xml:
重启es测试访问ik分词器.得到“王者荣”的词语
{
“tokens” : [
{
“token” : “王者荣耀”,
“start_offset” : 0,
“end_offset” : 4,
“type” : “CN_WORD”,
“position” : 0
},
{
“token” : “王者荣”,
“start_offset” : 0,
“end_offset” : 3,
“type” : “CN_WORD”,
“position” : 1
},
{
“token” : “荣耀”,
“start_offset” : 2,
“end_offset” : 4,
“type” : “CN_WORD”,
“position” : 2
}
]
}
ELASTICSEARCH的java-api
本章节目的
了解ES版本,兼容性,和java的api
掌握Java High Level REST Client常用功能
1.客户端概括
1.1支持多种客户端
ES支持多种语言客户都安,包括ruby js python java go .net等,其中java目前最新版本的客户都安支持2种方式。一种是旧版已经过时的transport client 一种是java high level rest client,前者是通过tcp协议链接访问es,后者就是java代码实现的REST访问。
1.2兼容性问题
目前新版ES推荐使用的是java high level rest client,但是由于ES更新太快,很多企业工程都是使用过时产品transport client,这里给出兼容性的指导。
java high level rest client 要求基于jdk1.8以上,并且以来ES核心内容。版本与ES版本相同,他能接受和transportclient相同的请求参数,并返回相同的响应对象。
高级客户端保证能够与运行在相同主版本和更高或同等小版本上的任何Elasticsearch节点通信。它不需要与与它通信的Elasticsearch节点处于同一个小版本中,因为它是向前兼容的,这意味着它支持与比它开发的版本更高版本的Elasticsearch通信。
2.初始准备
我们在这里要讲解一批java high level rest client的api,先来准备一下环境
2.1maven依赖
在maven项目中使用java high level rest client需要提供依赖.
org.elasticsearch.client elasticsearch-rest-high-level-client 7.11.12.2初始化链接
提供对应的es节点,使得代码可以操作es集群。
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost(“localhost”, 9200, “http”),
new HttpHost(“localhost”, 9201, “http”)));
有了这个对象我们就可以使用ES了
2.索引API
2.1创建索引
public void test01() throws IOException {
CreateIndexRequest request=new CreateIndexRequest(“index02”);
request.settings(Settings.builder()
.put(“index.number_of_shards”,“3”)
.put(“index.number_of_replicas”,“2”));
client.indices().create(request,RequestOptions.DEFAULT);
}
2.2删除索引
`
public void test02() throws IOException {
DeleteIndexRequest request=new DeleteIndexRequest(“index01”);
client.indices().delete(request,RequestOptions.DEFAULT);
}
2.3分词计算
public class HighLevelTest {
private RestHighLevelClient client;
@Before
public void init(){
client = new RestHighLevelClient(
RestClient.builder(
new HttpHost(“192.168.42.129”, 9200, “http”)));
}
/**
* 创建一个索引
/
@Test
public void test01() throws IOException {
CreateIndexRequest request=new CreateIndexRequest(“index02”);
request.settings(Settings.builder().put(“index.number_of_shards”,“3”).put(“index.number_of_replicas”,“2”));
client.indices().create(request,RequestOptions.DEFAULT);
}
/*
* 删除一个索引
/
@Test
public void test02() throws IOException {
DeleteIndexRequest request=new DeleteIndexRequest(“index01”);
client.indices().delete(request,RequestOptions.DEFAULT);
}
/*
* 分词计算
*/
@Test
public void test03() throws IOException {
AnalyzeRequest request=AnalyzeRequest.withGlobalAnalyzer(
“ik_max_word”,“中华人民共和国”);
AnalyzeResponse response
= client.indices().analyze(request, RequestOptions.DEFAULT);
List<AnalyzeResponse.AnalyzeToken> tokens = response.getTokens();
for (AnalyzeResponse.AnalyzeToken token : tokens) {
System.out.println(token.getTerm());
}
}
}
2.4获取SETTINGS
public class HighLevelTest {
private RestHighLevelClient client;
@Before
public void init(){
client = new RestHighLevelClient(
RestClient.builder(
new HttpHost(“192.168.42.129”, 9200, “http”)));
}
/**
* 创建一个索引
/
@Test
public void test01() throws IOException {
CreateIndexRequest request=new CreateIndexRequest(“index02”);
request.settings(Settings.builder().put(“index.number_of_shards”,“3”).put(“index.number_of_replicas”,“2”));
client.indices().create(request,RequestOptions.DEFAULT);
}
/*
* 删除一个索引
/
@Test
public void test02() throws IOException {
DeleteIndexRequest request=new DeleteIndexRequest(“index01”);
client.indices().delete(request,RequestOptions.DEFAULT);
}
/*
* 分词计算
/
@Test
public void test03() throws IOException {
AnalyzeRequest request=AnalyzeRequest.withGlobalAnalyzer(
“ik_max_word”,“中华人民共和国”);
AnalyzeResponse response
= client.indices().analyze(request, RequestOptions.DEFAULT);
List<AnalyzeResponse.AnalyzeToken> tokens = response.getTokens();
for (AnalyzeResponse.AnalyzeToken token : tokens) {
System.out.println(token.getTerm());
}
}
/*
* 获取index的settings设置
*/
@Test
public void test04() throws IOException {
GetSettingsRequest request = new GetSettingsRequest().indices(“index02”);
GetSettingsResponse settings = client.indices().getSettings(request, RequestOptions.DEFAULT);
Settings indexSettings = settings.getIndexToSettings().get(“index02”);
Integer numberOfShards = indexSettings.getAsInt(“index.number_of_shards”, null);
System.out.println(numberOfShards);
}
}
2.5获取MAPPINGS
public class HighLevelTest {
private RestHighLevelClient client;
@Before
public void init(){
client = new RestHighLevelClient(
RestClient.builder(
new HttpHost(“192.168.42.129”, 9200, “http”)));
}
/**
* 创建一个索引
/
@Test
public void test01() throws IOException {
CreateIndexRequest request=new CreateIndexRequest(“index02”);
request.settings(Settings.builder().put(“index.number_of_shards”,“3”).put(“index.number_of_replicas”,“2”));
client.indices().create(request,RequestOptions.DEFAULT);
}
/*
* 删除一个索引
/
@Test
public void test02() throws IOException {
DeleteIndexRequest request=new DeleteIndexRequest(“index01”);
client.indices().delete(request,RequestOptions.DEFAULT);
}
/*
* 分词计算
/
@Test
public void test03() throws IOException {
AnalyzeRequest request=AnalyzeRequest.withGlobalAnalyzer(
“ik_max_word”,“中华人民共和国”);
AnalyzeResponse response
= client.indices().analyze(request, RequestOptions.DEFAULT);
List<AnalyzeResponse.AnalyzeToken> tokens = response.getTokens();
for (AnalyzeResponse.AnalyzeToken token : tokens) {
System.out.println(token.getTerm());
}
}
/*
* 获取index的settings设置
/
@Test
public void test04() throws IOException {
GetSettingsRequest request = new GetSettingsRequest().indices(“index02”);
GetSettingsResponse settings = client.indices().getSettings(request, RequestOptions.DEFAULT);
Settings indexSettings = settings.getIndexToSettings().get(“index02”);
Integer numberOfShards = indexSettings.getAsInt(“index.number_of_shards”, null);
System.out.println(numberOfShards);
}
/*
*获取mappings
*/
@Test
public void test05() throws IOException {
GetMappingsRequest request = new GetMappingsRequest();
request.indices(“index02”);.
GetMappingsResponse mappings = client.indices().getMapping(request, RequestOptions.DEFAULT);
Map<String, MappingMetadata> allMappings = mappings.mappings();
MappingMetadata indexMapping = allMappings.get(“index02”);
Map<String, Object> mapping = indexMapping.sourceAsMap();
System.out.println(mapping);
}
}
2.6设置MAPPINGS
public class HighLevelTest {
private RestHighLevelClient client;
@Before
public void init(){
client = new RestHighLevelClient(
RestClient.builder(
new HttpHost(“192.168.42.129”, 9200, “http”)));
}
/**
* 创建一个索引
/
@Test
public void test01() throws IOException {
CreateIndexRequest request=new CreateIndexRequest(“index02”);
request.settings(Settings.builder().put(“index.number_of_shards”,“3”).put(“index.number_of_replicas”,“2”));
client.indices().create(request,RequestOptions.DEFAULT);
}
/*
* 删除一个索引
/
@Test
public void test02() throws IOException {
DeleteIndexRequest request=new DeleteIndexRequest(“index01”);
client.indices().delete(request,RequestOptions.DEFAULT);
}
/*
* 分词计算
/
@Test
public void test03() throws IOException {
AnalyzeRequest request=AnalyzeRequest.withGlobalAnalyzer(
“ik_max_word”,“中华人民共和国”);
AnalyzeResponse response
= client.indices().analyze(request, RequestOptions.DEFAULT);
List<AnalyzeResponse.AnalyzeToken> tokens = response.getTokens();
for (AnalyzeResponse.AnalyzeToken token : tokens) {
System.out.println(token.getTerm());
}
}
/*
* 获取index的settings设置
/
@Test
public void test04() throws IOException {
GetSettingsRequest request = new GetSettingsRequest().indices(“index02”);
GetSettingsResponse settings = client.indices().getSettings(request, RequestOptions.DEFAULT);
Settings indexSettings = settings.getIndexToSettings().get(“index02”);
Integer numberOfShards = indexSettings.getAsInt(“index.number_of_shards”, null);
System.out.println(numberOfShards);
}
/*
获取mappings
/
@Test
public void test05() throws IOException {
GetMappingsRequest request = new GetMappingsRequest();
request.indices(“index02”);
GetMappingsResponse mappings = client.indices().getMapping(request, RequestOptions.DEFAULT);
Map<String, MappingMetadata> allMappings = mappings.mappings();
MappingMetadata indexMapping = allMappings.get(“index02”);
Map<String, Object> mapping = indexMapping.sourceAsMap();
System.out.println(mapping);
}
/**
* 设置mappings
*/
@Test
public void test06() throws IOException {
PutMappingRequest request = new PutMappingRequest(“index02”);
request.source("{“properties”:{“name”:{“type”:“text”}}}",XContentType.JSON);
client.indices().putMapping(request,RequestOptions.DEFAULT);
}
}
3.文档API
3.1新增文档
public class DocumentTest {
private RestHighLevelClient client;
@Before
public void init(){
client = new RestHighLevelClient(
RestClient.builder(
new HttpHost(“192.168.42.129”, 9200, “http”)));
}
/**
* 新增文档
*/
@Test
public void test01() throws IOException {
IndexRequest request=new IndexRequest(“index02”);
request.source("{“name”:“王翠花”}", XContentType.JSON);
request.id(“1”);
client.index(request, RequestOptions.DEFAULT);
}
}
3.2获取source
public class DocumentTest {
private RestHighLevelClient client;
@Before
public void init(){
client = new RestHighLevelClient(
RestClient.builder(
new HttpHost(“192.168.42.129”, 9200, “http”)));
}
/**
* 新增文档
/
@Test
public void test01() throws IOException {
IndexRequest request=new IndexRequest(“index02”);
request.source("{“name”:“王翠花”}", XContentType.JSON);
request.id(“1”);
client.index(request, RequestOptions.DEFAULT);
}
/*
* 获取文档
*/
@Test
public void test02() throws IOException {
GetRequest request=new GetRequest(“index02”);
request.id(“1”);
GetResponse documentFields = client.get(request, RequestOptions.DEFAULT);
String sourceAsString = documentFields.getSourceAsString();
System.out.println(sourceAsString);
}
}
3.3判断存在
public class DocumentTest {
private RestHighLevelClient client;
@Before
public void init(){
client = new RestHighLevelClient(
RestClient.builder(
new HttpHost(“192.168.42.129”, 9200, “http”)));
}
/**
* 新增文档
/
@Test
public void test01() throws IOException {
IndexRequest request=new IndexRequest(“index02”);
request.source("{“name”:“王翠花”}", XContentType.JSON);
request.id(“1”);
client.index(request, RequestOptions.DEFAULT);
}
/*
* 获取文档
/
@Test
public void test02() throws IOException {
GetRequest request=new GetRequest(“index02”);
request.id(“1”);
GetResponse documentFields = client.get(request, RequestOptions.DEFAULT);
String sourceAsString = documentFields.getSourceAsString();
System.out.println(sourceAsString);
}
/*
* 判断文档存在
*/
@Test
public void test03() throws IOException {
GetRequest request=new GetRequest(“index02”);
request.id(“1”);
boolean exists = client.exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
}
3.4删除文档
public class DocumentTest {
private RestHighLevelClient client;
@Before
public void init(){
client = new RestHighLevelClient(
RestClient.builder(
new HttpHost(“192.168.42.129”, 9200, “http”)));
}
/**
* 新增文档
/
@Test
public void test01() throws IOException {
IndexRequest request=new IndexRequest(“index02”);
request.source("{“name”:“王翠花”}", XContentType.JSON);
request.id(“1”);
client.index(request, RequestOptions.DEFAULT);
}
/*
* 获取文档
/
@Test
public void test02() throws IOException {
GetRequest request=new GetRequest(“index02”);
request.id(“1”);
GetResponse documentFields = client.get(request, RequestOptions.DEFAULT);
String sourceAsString = documentFields.getSourceAsString();
System.out.println(sourceAsString);
}
/*
* 判断文档存在
/
@Test
public void test03() throws IOException {
GetRequest request=new GetRequest(“index02”);
request.id(“1”);
boolean exists = client.exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
/*
* 删除文档对象
*/
@Test
public void test04() throws IOException {
DeleteRequest request=new DeleteRequest(“index02”);
request.id(“1”);
client.delete(request,RequestOptions.DEFAULT);
}
}
3.5bulk批量新增文档
//bulk批量新增文档数据
@Test
public void bulkDocs() throws IOException {
BulkRequest request = new BulkRequest();
request.add(new IndexRequest(“index02”).id(“1”)
.source(XContentType.JSON,“field”, “foo”,“name”,“王翠花”));
request.add(new IndexRequest(“index02”).id(“2”)
.source(XContentType.JSON,“field”, “bar”,“name”,“刘首付”));
request.add(new IndexRequest(“index02”).id(“3”)
.source(XContentType.JSON,“field”, “baz”,“name”,“王有才”));
client.bulk(request,RequestOptions.DEFAULT);
}
4.搜索API
4.1bool查询
我们可以从查询的基本api结构中延伸出多种查询的构造,这里以bool为例.
public class QueryTest {
private RestHighLevelClient client;
@Before
public void init(){
client = new RestHighLevelClient(
RestClient.builder(
new HttpHost(“192.168.42.129”, 9200, “http”)));
}
/**
* 搜索功能
/
@Test
public void test01() throws IOException {
SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
/{
“query”: {
“bool”: {
“must”: {
“match”: {
“address”: “lane”
}
},
“should”: {
“match”: {
“address”: “eldert”
}
}
}
}
}*/
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery(“address”,“lane”));
boolQueryBuilder.should(QueryBuilders.matchQuery(“address”,“eldert”));
searchSourceBuilder.query(boolQueryBuilder);
searchRequest.source(searchSourceBuilder);
searchRequest.indices(“bank”);
SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] hits = search.getHits().getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
System.out.println(sourceAsString);
}
}
}
4.2高亮查询
有时候我们需要对某些特定的字段值做高亮显示,ES也是支持的,直接返回对某个字段高亮的html包装.比如这里我们对地址address做高亮显示
public class QueryTest {
private RestHighLevelClient client;
@Before
public void init(){
client = new RestHighLevelClient(
RestClient.builder(
new HttpHost(“192.168.42.129”, 9200, “http”)));
}
/**
* 搜索功能
/
@Test
public void test01() throws IOException {
SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
/{
“query”: {
“bool”: {
“must”: {
“match”: {
“address”: “lane”
}
},
“should”: {
“match”: {
“address”: “eldert”
}
}
}
}
}*/
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery(“address”,“lane”));
boolQueryBuilder.should(QueryBuilders.matchQuery(“address”,“eldert”));
searchSourceBuilder.query(boolQueryBuilder);
searchRequest.source(searchSourceBuilder);
searchRequest.indices(“bank”);
SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] hits = search.getHits().getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
System.out.println(sourceAsString);
}
}
/**
* 高亮显示
* @throws IOException
*/
@Test
public void test02() throws IOException {
SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery(“address”,“lane”));
boolQueryBuilder.should(QueryBuilders.matchQuery(“address”,“eldert”));
searchSourceBuilder.query(boolQueryBuilder);
searchRequest.source(searchSourceBuilder);
searchRequest.indices(“bank”);
//设置高亮
HighlightBuilder highlightBuilder=new HighlightBuilder();
HighlightBuilder.Field highlightAddress =
new HighlightBuilder.Field(“address”);
highlightBuilder.field(highlightAddress);
searchSourceBuilder.highlighter(highlightBuilder);
SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] hits = search.getHits().getHits();
for (SearchHit hit : hits) {
//从hit中获取高亮结果.
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField address = highlightFields.get(“address”);
Text[] fragments = address.getFragments();
String string = fragments[0].string();
System.out.println(string);
}
}
}
ELASTICSEARCH的聚合查询
本章节内容
了解聚合查询的功能
掌握聚合查询的使用
1.聚合查询
1.1概括(什么是聚合查询)
Elasticsearch的聚合查询,跟数据库的聚合查询效果是一样的,我们可以将二者拿来对比学习,如求和、求平均值、求最大最小等等。
1.2入门案例(感受一下聚合查询)
o提供批量测试数据
将附件中的json导入到索引中,创建employee
o回顾查询
从数据中看到如下格式json
{“id”:28,“name”:“马云”,“job”:“java”,“age”:34,“salary”:22000,“gender”:“female”}
可以调用一个查询,将所有年龄在20-40之间的数据查询出来.
GET employee/_search
{
“query”:{
“range”: {
“age”: {
“gte”: 20,
“lte”: 40
}
}
}
}
o加入聚合(求和)
GET employee/_search
{“query”:{
“range”: {
“age”: {
“gte”: 20,
“lte”: 40
}
}
},
“size”: 0,
“aggs”: { ①
“sum_salary”: { ②
“sum”: { ③
“field”: “salary” ④
}
}
}
}
①: 聚合属性名称,固定值
②:自定义的聚合名称
③:聚合类型,elasticsearch提供的值,sum为求和
④:属性,对哪个文档属性进行sum的操作,由于为求和必须是数字,不同聚合类型要求字段具备不同条件.
可以看到返回结果如下:
{
“took” : 6,
“timed_out” : false,
“_shards” : {
“total” : 1,
“successful” : 1,
“skipped” : 0,
“failed” : 0
},
“hits” : {
“total” : {
“value” : 25,
“relation” : “eq”
},
“max_score” : null,
“hits” : [ ]
},
“aggregations” : {
“sum_salary” : {
“value” : 472000.0
}
}
}
这个结果中的aggregations就是查询聚合的数据,表示当前查询结果的所有salary综合是472000.
除了sum,我们还可以对字段数据在查询结果中进行avg平均数,max最大数,min最小数,和cardinality求count,的聚合类型的计算.
课堂练习1:聚合计算年龄的平均值,最大值,最小值,总和.
1.3多值输出
除了上述案例中,我们做的是单值输出,还可以进行多值输出,一次性将多个结果输出.
o查询工资信息stats
可以将最大最小平均和记数求和的所有结果对一个字段进行一次性输出
GET employee/_search
{
“size”: 0,
“aggs”: {
“salary_info”: {
“stats”: {
“field”: “salary”
}
}
}
}
得到返回结果如下
{
“took” : 11,
“timed_out” : false,
“_shards” : {
“total” : 1,
“successful” : 1,
“skipped” : 0,
“failed” : 0
},
“hits” : {
“total” : {
“value” : 37,
“relation” : “eq”
},
“max_score” : null,
“hits” : [ ]
},
“aggregations” : {
“salary_info” : {
“count” : 37,
“min” : 10000.0,
“max” : 35000.0,
“avg” : 19729.72972972973,
“sum” : 730000.0
}
}
}
o查询不同工作人数
GET employee/_search
{
“size”: 0,
“aggs”: {
“job_employee_info”: {
“terms”: {
“field”: “job.keyword” ①
}
}
}
}
①: 内涵term计算,所以使用字符串类型需要加keyword将字符串整体来使用.
返回结果如下:
{
“took” : 14,
“timed_out” : false,
“_shards” : {
“total” : 1,
“successful” : 1,
“skipped” : 0,
“failed” : 0
},
“hits” : {
“total” : {
“value” : 37,
“relation” : “eq”
},
“max_score” : null,
“hits” : [ ]
},
“aggregations” : {
“job_employee_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 0,
“buckets” : [
{
“key” : “python”,
“doc_count” : 12
},
{
“key” : “go”,
“doc_count” : 8
},
{
“key” : “html”,
“doc_count” : 7
},
{
“key” : “c”,
“doc_count” : 5
},
{
“key” : “java”,
“doc_count” : 5
}
]
}
}
}
o子聚合
查看喜欢不同类型的工作人数统计
{
“size”: 0,
“aggs”: {
“job_info”: {
“terms”: {
“field”: “job.keyword”,
“size”: 1
},
“aggs”: { ①
“job_like_info”: {
“terms”: {
“field”: “like.keyword”
}
}
}
}
}
}
①:嵌套在一个聚合下的子聚合,内容和聚合相同
上述案例中,可以统计 不同job(c,java,python,html,go) 喜欢(鲜肉,萝莉,大叔,御姐,人妖)的详细信息,返回结果如下(这里我们取一个聚合结果)
{
“took” : 6,
“timed_out” : false,
“_shards” : {
“total” : 1,
“successful” : 1,
“skipped” : 0,
“failed” : 0
},
“hits” : {
“total” : {
“value” : 37,
“relation” : “eq”
},
“max_score” : null,
“hits” : [ ]
},
“aggregations” : {
“job_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 26,
“buckets” : [
{
“key” : “java”,
“doc_count” : 11,
“job_like_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 0,
“buckets” : [
{
“key” : “人妖”,
“doc_count” : 3
},
{
“key” : “鲜肉”,
“doc_count” : 3
},
{
“key” : “御姐”,
“doc_count” : 2
},
{
“key” : “萝莉”,
“doc_count” : 2
},
{
“key” : “大叔”,
“doc_count” : 1
}
]
}
}
]
}
}
}
可以看到java工作的人员一共11个,其中喜欢鲜肉3个,御姐2个,萝莉2个,大叔1个,人妖3个.
课堂练习2:查询每个岗位下的工资信息(包括平均工资,最高工资,最少工资)
o三层聚合
和子聚合一样,在子聚合中还可以在嵌套子聚合,我们可以计算男女员工数量,每个性别下的不同工作的工资情况
GET employee/_search
{
“size”: 0,
“aggs”: {
“gender_info”: {
“terms”: {
“field”: “gender.keyword”
},
“aggs”: {
“gender_job_info”: {
“terms”: {
“field”: “job.keyword”
, “size”: 1
},
“aggs”: {
“gender_job_sal_info”: {
“stats”: {
“field”: “salary”
}
}
}
}
}
}
}
}
其返回结果如下:为了展示方便我们在第二层子聚合中的工作种类只显示1种
{
“took” : 3,
“timed_out” : false,
“_shards” : {
“total” : 1,
“successful” : 1,
“skipped” : 0,
“failed” : 0
},
“hits” : {
“total” : {
“value” : 37,
“relation” : “eq”
},
“max_score” : null,
“hits” : [ ]
},
“aggregations” : {
“gender_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 0,
“buckets” : [
{
“key” : “female”,
“doc_count” : 19,
“gender_job_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 14,
“buckets” : [
{
“key” : “html”,
“doc_count” : 5,
“gender_job_sal_info” : {
“count” : 5,
“min” : 12000.0,
“max” : 30000.0,
“avg” : 20800.0,
“sum” : 104000.0
}
}
]
}
},
{
“key” : “male”,
“doc_count” : 18,
“gender_job_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 12,
“buckets” : [
{
“key” : “java”,
“doc_count” : 6,
“gender_job_sal_info” : {
“count” : 6,
“min” : 12000.0,
“max” : 30000.0,
“avg” : 18666.666666666668,
“sum” : 112000.0
}
}
]
}
}
]
}
}
}
1.4其他聚合
otop-hits
GET employee/_search
{
“size”:0,
“aggs”: {
“oldest_emp”: {
“top_hits”: {
“size”: 2,
“sort”: [{ ①
“age”: {
“order”: “desc”
}
}],
“_source”: { ②
“includes”: [“like”,“name”]
}
}
}
}
}
①:表示当我们获取top_hits时使用的排序规则,这里是根据倒序age字段.
②:表示结果中我要展示的数据源包含或不包含哪些字段.
返回值如下:
{
“took” : 11,
“timed_out” : false,
“_shards” : {
“total” : 1,
“successful” : 1,
“skipped” : 0,
“failed” : 0
},
“hits” : {
“total” : {
“value” : 37,
“relation” : “eq”
},
“max_score” : null,
“hits” : [ ]
},
“aggregations” : {
“oldest_emp” : {
“hits” : {
“total” : {
“value” : 37,
“relation” : “eq”
},
“max_score” : null,
“hits” : [
{
“_index” : “employee”,
“_type” : “_doc”,
“_id” : “30”,
“_score” : null,
“_source” : {
“like” : “鲜肉”,
“name” : “马化腾”
},
“sort” : [
48
]
},
{
“_index” : “employee”,
“_type” : “_doc”,
“_id” : “31”,
“_score” : null,
“_source” : {
“like” : “人妖”,
“name” : “任正非”
},
“sort” : [
48
]
}
]
}
}
}
}
orange
GET employee/_search
{
“size”: 0,
“aggs”: {
“range_sla_info”: {
“range”: {
“field”: “salary”,①
“ranges”: [
{
“key”:“平民”,②
“from”: 10000,③
“to”: 15000 ④
},
{
“key”:“中产”,
“from”: 15000,
“to”: 25000
},
{
“key”:“小资”,
“from”: 25000,
“to”: 45000
}
]
}
}
}
}
①: 字段名,对salary进行范围聚合
②: 聚合结果的标签提示
③: 范围的起始(包含)
④: 范围的结束(不包含)
返回结果如下:
{
“took” : 3,
“timed_out” : false,
“_shards” : {
“total” : 1,
“successful” : 1,
“skipped” : 0,
“failed” : 0
},
“hits” : {
“total” : {
“value” : 37,
“relation” : “eq”
},
“max_score” : null,
“hits” : [ ]
},
“aggregations” : {
“range_sla_info” : {
“buckets” : [
{
“key” : “平民”,
“from” : 10000.0,
“to” : 15000.0,
“doc_count” : 9
},
{
“key” : “中产”,
“from” : 15000.0,
“to” : 25000.0,
“doc_count” : 12
},
{
“key” : “小资”,
“from” : 25000.0,
“to” : 45000.0,
“doc_count” : 16
}
]
}
}
}
ohistogram
以每5000元为区间查询员工工资信息
GET employee/_search
{
“size”: 0,
“aggs”: {
“histogram_info”: {
“histogram”: {
“field”: “salary”,
“interval”: 5000,①
“min_doc_count”: 0,②
“extended_bounds”: {③
“min”: 5000,
“max”: 30000
}
}
}
}
}
①: 直方图间隔,每5000一次间隔显示
②: 某个区间的最小命中doc数量
③: 扩展绑定,从10000开始,最高累计到50000
返回结果如下:
{
“took” : 4,
“timed_out” : false,
“_shards” : {
“total” : 1,
“successful” : 1,
“skipped” : 0,
“failed” : 0
},
“hits” : {
“total” : {
“value” : 37,
“relation” : “eq”
},
“max_score” : null,
“hits” : [ ]
},
“aggregations” : {
“histogram_info” : {
“buckets” : [
{
“key” : 5000.0,
“doc_count” : 0
},
{
“key” : 10000.0,
“doc_count” : 9
},
{
“key” : 15000.0,
“doc_count” : 6
},
{
“key” : 20000.0,
“doc_count” : 6
},
{
“key” : 25000.0,
“doc_count” : 5
},
{
“key” : 30000.0,
“doc_count” : 4
},
{
“key” : 35000.0,
“doc_count” : 7
}
]
}
}
}
omin_bucket
查询平均工资最低的工作种类
GET employee/_search
{
“size”: 0,
“aggs”: {
“job_info”: {
“terms”: {
“field”: “job.keyword”
},
“aggs”: {
“job_avg_info”: {
“avg”: {
“field”: “salary”
}
}
}
},
“min_avg_sal_job”:{
“min_bucket”: {①
“buckets_path”: "job_info>job_avg_info"②
}
}
}
}
①:最小桶配置聚合类型
②:桶路径,需要指向一个buket输出为数字的聚合结果
返回值如下:
{
“took” : 15,
“timed_out” : false,
“_shards” : {
“total” : 1,
“successful” : 1,
“skipped” : 0,
“failed” : 0
},
“hits” : {
“total” : {
“value” : 37,
“relation” : “eq”
},
“max_score” : null,
“hits” : [ ]
},
“aggregations” : {
“job_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 0,
“buckets” : [
{
“key” : “java”,
“doc_count” : 11,
“job_avg_info” : {
“value” : 21727.272727272728
}
},
{
“key” : “c”,
“doc_count” : 7,
“job_avg_info” : {
“value” : 30000.0
}
},
{
“key” : “html”,
“doc_count” : 7,
“job_avg_info” : {
“value” : 21571.428571428572
}
},
{
“key” : “go”,
“doc_count” : 6,
“job_avg_info” : {
“value” : 16833.333333333332
}
},
{
“key” : “python”,
“doc_count” : 6,
“job_avg_info” : {
“value” : 21333.333333333332
}
}
]
},
“min_avg_sal_job” : {
“value” : 16833.333333333332,
“keys” : [
“go”
]
}
}
}
2.聚合的分类
上述案例的聚合虽然实现了一些功能,但是感觉有点杂乱无章.除了我们要多练习,多熟悉不同聚合功能以外,我们可以根据官方提供的信息,整理3种聚合的类型,他们分别是buket,metric和pipeline
2.1buket
桶聚合是创建文档桶(分组)。每个存储桶都与一个标准(取决于聚合类型,例如terms)相关联,该标准确定当前上下文中的文档是否“属于”它。换句话说,存储桶有效地定义了文档集。除了存储桶本身之外,存储桶聚合还计算并返回“落入”每个存储桶的文档数量。
桶聚合可以容纳子聚合。这些子聚合将为它们的“父”桶聚合创建的桶再次生成聚合。
有不同的桶聚合器,每个都有不同的“桶”策略。有些定义单个存储桶,有些定义多个存储桶的固定数量,还有一些在聚合过程中动态创建存储桶。
利用上述官方定义,我们来看之前实现的案例terms聚合
o案例1
GET employee/_search
{
“size”: 0,
“aggs”: {
“job_employee_info”: {
“terms”: {
“field”: “job.keyword”
}
}
}
}
这是我们上面完成的terms案例.可以观察返回值.
{
“took” : 11,
“timed_out” : false,
“_shards” : {
“total” : 1,
“successful” : 1,
“skipped” : 0,
“failed” : 0
},
“hits” : {
“total” : {
“value” : 37,
“relation” : “eq”
},
“max_score” : null,
“hits” : [ ]
},
“aggregations” : {
“job_employee_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 0,
“buckets” : [
{
“key” : “java”,
“doc_count” : 11
},
{
“key” : “c”,
“doc_count” : 7
},
{
“key” : “html”,
“doc_count” : 7
},
{
“key” : “go”,
“doc_count” : 6
},
{
“key” : “python”,
“doc_count” : 6
}
]
}
}
}
这里可以理解为,我们利用terms的聚合,按照字段job将查询结果进行了分桶.所以一共有五个桶,按照值为 java c html go python.
我们的查询结果一共有37个doc,按照桶归类,java有11个,c有7个,go有7个,html有6个,python有6个.
如果我们在此基础之上实现子聚合,就可以在每个桶中,继续产生新的buket桶
GET employee/_search
{
“size”: 0,
“aggs”: {
“job_employee_info”: {
“terms”: {
“field”: “job.keyword”
},
“aggs”: {
“job_employee_gender_info”: {
“terms”: {
“field”: “gender.keyword”
}
}
}
}
}
}
返回结果如下:
{
“took” : 5,
“timed_out” : false,
“_shards” : {
“total” : 1,
“successful” : 1,
“skipped” : 0,
“failed” : 0
},
“hits” : {
“total” : {
“value” : 37,
“relation” : “eq”
},
“max_score” : null,
“hits” : [ ]
},
“aggregations” : {
“job_employee_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 0,
“buckets” : [
{
“key” : “java”,
“doc_count” : 11,
“job_employee_gender_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 0,
“buckets” : [
{
“key” : “male”,
“doc_count” : 6
},
{
“key” : “female”,
“doc_count” : 5
}
]
}
},
{
“key” : “c”,
“doc_count” : 7,
“job_employee_gender_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 0,
“buckets” : [
{
“key” : “female”,
“doc_count” : 4
},
{
“key” : “male”,
“doc_count” : 3
}
]
}
},
{
“key” : “html”,
“doc_count” : 7,
“job_employee_gender_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 0,
“buckets” : [
{
“key” : “female”,
“doc_count” : 5
},
{
“key” : “male”,
“doc_count” : 2
}
]
}
},
{
“key” : “go”,
“doc_count” : 6,
“job_employee_gender_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 0,
“buckets” : [
{
“key” : “male”,
“doc_count” : 4
},
{
“key” : “female”,
“doc_count” : 2
}
]
}
},
{
“key” : “python”,
“doc_count” : 6,
“job_employee_gender_info” : {
“doc_count_error_upper_bound” : 0,
“sum_other_doc_count” : 0,
“buckets” : [
{
“key” : “female”,
“doc_count” : 3
},
{
“key” : “male”,
“doc_count” : 3
}
]
}
}
]
}
}
}
在每一个父桶中,都会产生新的桶聚合结果.其中工作为java的11人,male6人,female5人.
那么所谓的桶聚合策略,指的就是这种聚合下我们可以指定的聚合类型,桶聚合在最新版本的7.12中,总共包含如下几种.
数据不同,需求不同我们可以使用多种桶聚合的方式.
2.2metric
metric中的聚合是从被聚合的文档中提取的值做计算,这些值常见的都是文档的字段值,也可以通过文档的字段进行脚本计算取值.
数值的metric聚合输出数值,有单个指标(avg),成为单值数值聚合,也可以聚合生成多个指标(stats),成为多值数值聚合.metric聚合可以作为buket聚合的子聚合使用,这样可以基于这种子聚合的计算结果实现排序.有很大的应用价值
oavg单值聚合
GET employee/_search
{
“size”: 0,
“aggs”: {
“avg_salary”: {
“avg”: {
“field”: “salary”
}
}
}
}
ostats多值聚合
GET employee/_search
{
“size”: 0,
“aggs”: {
“avg_salary”: {
“avg”: {
“field”: “salary”
}
}
}
}
o作为buket子聚合
GET employee/_search
{
“size”: 0,
“aggs”: {
“gender_info”: {
“terms”: {
“field”: “gender.keyword”
},
“aggs”: {
“gender_job_info”: {
“terms”: {
“field”: “job.keyword”
, “size”: 1
},
“aggs”: {
“gender_job_sal_info”: {
“stats”: {
“field”: “salary”
}
}
}
}
}
}
}
}
ometric的聚合种类
2.3pipeline 管道聚合
管道聚合就是将其他聚合的输出结果,作为管道聚合的输入结果,指定输入路径.根据输入路径对于管道聚合的相对为止,管道聚合分为两种类型
oparent:父管道聚集使用其父聚集的输出,它获取此聚合的值计算新的分组或聚集并将它们添加到已经存在的分组中
osibling:与父管道聚集相比,兄弟聚集使用兄弟聚集的输出。它获取该输出并计算一个新聚合,该聚合与兄弟聚合处于同一级别。
LOGSTASH概括
本章节内容
logstash概括介绍
logstash安装启动
logstash入门配置案例
2.1概括介绍
Logstash是具有实时流水线功能的开源数据收集引擎。Logstash可以动态统一来自不同来源的数据,并将数据标准化到所选择的目标位置。清除所有数据并使其民主化,以用于各种高级下游分析和可视化用例。
2.安装启动
o2.1下载
https://artifacts.elastic.co/downloads/logstash/logstash-7.10.2-linux-x86_64.tar.gz
o2.2安装
解压
tar -xf logstash-7.10.2-linux-x86_64.tar.gz -C /home/software/
调整jvm.options
如果我们的jdk环境高于jdk1.8,需要调整logstash的GC策略.
验证
cd logstash-7.10.2
bin/logstash -e ‘input { stdin { } } output { stdout {} }’
得到启动日志如下,这是一个标准的输入和标准的输出
当我们在日志中输入任何信息之后,可以看到
3.入门案例
3.1准备配置文件
编辑一个demo01.conf文件,内容如下.
input{
exec {
command => “ls”
interval => 30
}
}
output{
stdout {}
}
没个30秒钟执行一次 ls命令.
3.2启动
在bin下执行.
bin/logstash -f conf/demo01.conf
4.LOGSTASH原理
logstash内部是通过管道的方式进行数据的收集、处理、输出。在logstash中,包含了三个阶段:输入input --> 处理filter -->输出output
每个阶段可以指定多种方式,比如输出既可以输出到elasticsearch中,也可以指定到stdout在控制台打印
LOGSTASH的input组件
本章节内容
了解input组件的作用
理解exec插件
理解file插件
掌握jdbc插件
1.input组件介绍
数据往往以各种各样的形式,或分散或集中地存在与很多系统中。logstash支持各种输入选择,可以在同一时间众多的常用来源中捕捉数据。能够以连续的流式传输方式,轻松地从日志、指标、web应用、数据存储等采集数据.索引input组件顾名思义就是将数据源输入到logstash.
2.EXEC插件
2.1概括
定期运行shell命令并将整个输出捕获为事件。
2.2案例
配置入门案例中的demo01.conf文件
input{ ①
exec { ②
command => “ls” ③
interval => 30 ④
}
}
output{
stdout {}
}
①: input组件起始关键配置
②: exec插件配置
③: exec中的格式command表示运行的shell命令
④: exec中的格式interval表示间隔时间长短
运行
bin/logstash -f conf/demo01.conf
3.FILE插件
3.1概括
监控文件中的新事件,类似tail -f的作用.
3.2案例
创建一个文件
echo hello world 01 >> tomcat.log
编写logstash的配置文件demo02.conf
input{
file{①
path => "tomcat.log"②
}
}
output{
stdout{}
}
①: file插件,监控文件
②: path,file插件必须携带的一个值,类型是数组,可以监听单个/多个文件,可以监听patterns范围的文件,例如 /home/logs/*.log
追加tomcat.log的数据
echo hello world 02 >> tomcat.log
这样在logstash控制台中可以看到输出的日志新内容.
4.JDBC插件
4.1概括
我们可以让logstash通过JDBC将数据从数据库中导入到logstash中.
4.2案例
入门配置案例
input {
jdbc {
jdbc_driver_library => "/home/resource/mysql-connector-java-5.1.36.jar"①
jdbc_driver_class => "com.mysql.jdbc.Driver"②
jdbc_connection_string => "jdbc:mysql:///easydb?useSSL=false"③
jdbc_user => "root"④
jdbc_password => "July7@tedu"⑤
schedule => “* * * * "⑥
statement => "SELECT * from t_product"⑦
}
}
output{
stdout{}
}
①:驱动包
②:驱动类
③:jdbc url地址
④:数据库用户名
⑤:登录密码
⑥:定时执行,分 时 天 月 年,logstash最小时间定时单位是分,类似于cron表达式
⑦:执行的sql语句
增量索引
我们在数据库可以一次性读取所有数据,在数据量庞大时也可以分批次读取分页数据,当有新增数据时,也可以设置增量数据的读取.
input {
jdbc{
jdbc_driver_library => “/home/resource/mysql-connector-java-5.1.46.jar”
jdbc_driver_class => “com.mysql.jdbc.Driver”
jdbc_connection_string => “jdbc:mysql://localhost:3306/easydb?useSSL=false”
jdbc_user => “root”
jdbc_password => “July7@tedu”
jdbc_paging_enabled => "true"①
jdbc_page_size => "500"②
use_column_value=>true③
tracking_column=>"create_time"④
tracking_column_type=>"timestamp"⑤
last_run_metadata_path => "/home/software/logstash-7.10.2/config/station_parameter.txt"⑥
clean_run => false⑦
statement => “SELECT * from t_product where product_create_time> :sql_last_value order by product_create_time desc”
schedule => " * * * *”
}
}
output {
stdout{}
}
①:是否开启分页,数据量大时,需要开启,以批量读取
②:分页开启为true时,每次分页数量
③:是否以字段值记录最后一次执行
④:使用哪个字段值记录最后一次执行
⑤:使用的字段值类型是,目前有2中,numeric,timestamp
⑥:最后一次执行的字段值存储的文件
⑦:是否清除上一次存储的字段值文件,在增量索引时,这里必须是false
LOGSTASH的output组件
本章节内容
了解output组件
掌握stdout插件
掌握elasticsearch插件
1.output组件介绍
输出插件将事件数据发送到特定的目标。输出是事件管道的最后阶段。
2.stdout插件
2.1概括
一个简单的输出,打印到运行日志的数据。当调试插件配置时,这个输出可以非常方便,因为它允许在事件数据通过输入和过滤器后立即访问它。
2.2案例
input {
exec {
command => “ls”
interval => 30
}
}
output{
stdout{
codec=>json①
id=>"my_id1"②
}
stdout{
id=>“my_id2”
}
}
①: 输出编码格式
②: 给该输出组件添加一个id
3.elasticsearch插件
3.1概括
这个插件使用http协议与ES进行数据交互,主要是间input的数据输出到elasticsearch的索引中.
3.2案例
这里我们将数据库的数据,输出到索引elasticsearch中
input {
jdbc{
jdbc_driver_library => “/home/resource/mysql-connector-java-5.1.46.jar”
jdbc_driver_class => “com.mysql.jdbc.Driver”
jdbc_connection_string => “jdbc:mysql://localhost:3306/easydb?useSSL=false”
jdbc_user => “root”
jdbc_password => “July7@tedu”
jdbc_paging_enabled => “true”
jdbc_page_size => “5”
use_column_value=>true
tracking_column=>“create_time”
tracking_column_type=>“timestamp”
last_run_metadata_path => “/home/software/logstash-7.10.2/config/station_parameter.txt”
clean_run => false
statement => “SELECT * from t_product where create_time> :sql_last_value”
schedule => “* * * * *”
}
}
output {
elasticsearch{
action=>"index"①
hosts=>[“192.168.42.129:9200”]②
index=>“logstash-demo01"③
document_id=>”%{product_id}"④
}
stdout{}
}
①:es输出插件的目的,index表示创建document索引
②:es集群的节点信息
③:创建索引的名称
④:使用字段product_id作为商品的缓存使用
这个案例中,是支持批量索引和增量索引的,只要第一次完成多次分页导入的数据,后续会根据数据的新增时间不断向es输出数据索引.
LOGSTASH的filter组件
本章节内容
了解filter组件
掌握filter的grok插件
1.filter组件介绍
过滤器组件对事件执行的中间处理。过滤器通常根据事件的特征有条件地应用。这使得从数据源导入的数据在导出之前可以做一些规范或者调整.
2.grok插件
2.1grok概括
解析任意文本并对其进行结构化。
Grok是一种很好的方法,可以将非结构化日志数据解析为结构化和可查询的数据。
该工具非常适合syslog日志、apache和其他web服务器日志、mysql日志,以及一般情况下,任何一般为人类编写而不是计算机使用的日志格式。
2.1案例1
Grok的工作方式是将文本模式组合到匹配日志的内容中。
grok模式的语法是%{语法:语义}
语法是与文本匹配的模式的名称。例如:“3.44”匹配的是NUMERIC,“55.3.244.1”匹配的是IP模式。所以语法就是你如何取匹配数据.
语义是要匹配的文本片段的标识符或者字段名称。例如,3.44可以是一个事件的时间,所以你可以简单地称之为time。
input {stdin{}}①
filter{
grok{
match=>{“message”=>"%{IP:remote_address}"}②
}
}
output{stdout{}}③
①:标准输入,接收控制台信息,输出在message中
②:match是grok一个字段,内容用来匹配某个输出信息字段的值,这里是检查是否有IP格式的文本,将会以remote_address字段单独输出
③:标准输出,将信息打印在控制台
输入10.9.19.19运行看返回结果:
这里在控制台手动输入文本 10.9.19.19.
看到结果输出有remote_address字段.
2.3案例2
有时候logstash没有你需要的模式。你可以使用Oniguruma语法来命名捕获,这将让你
匹配一段文本并将其保存为一个字段.
input{stdin{}}
filter{
grok{
match=>{“message”=>"(?([A-Za-z0-9_-.])+@([A-Za-z0-9_-.])+.([A-Za-z]{2,4}))"}①
}
}
output{stdout{}}
①:正则表达式,满足email的格式,将会以email的字段返回
输入"我们都是一家人,我们在一起不是不开心,就在这里,10.9.18.18 haha@tedu.cn"返回结果:包含一个email的字段值
2.4案例3
除了2.3中我们直接使用正则表达式,我们还可以自定义创建语法.步骤如下:
o创建文件filter-pattern-demo01
MYEMAIL ([A-Za-z0-9_-.])+@([A-Za-z0-9_-.])+.([A-Za-z]{2,4})
o配置文件filter-demo03.conf
input{stdin{}}
filter{
grok{
patterns_dir => ["/home/software/logstash-7.10.2/conf/patterns"]①
match=>{“message”=>"%{NUMBER:num} %{WORD:name} %{MYEMAIL:email}"}
}
}
output{stdout{}}
①:读取自定义文件夹
o启动输入文本:12827 nmuaj asdo@tedu.con
KIBANA概括
本章节内容
了解kibana
掌握kibana的安装和配置
掌握kibana的视图化使用
理解kibana视图化原理
1.什么是kibana
可视化和分析你的数据和管理的一个工具软件,他的功能非常强大.它一个开源的分析和可视化平台。使用Kibana来探索你的Elasticsearch数据,然后建立漂亮的可视化和仪表板
2.安装和启动
2.1下载
https://artifacts.elastic.co/downloads/kibana/kibana-7.10.2-linux-x86_64.tar.gz
2.2解压安装
将下载的安装包解压到固定的文件夹
tar -xf kibana-7.10.2-linux-x86_64.tar.gz
2.3kibana配置文件
打开kibana目录中config/kibana.yml配置文件.
server.host
elasticsearch.hosts
2.4启动
进入到kibana的bin文件夹启动,由于kibana默认不允许root启动,所以可以添加选项–allow-root
bin/kibana --allow-root
2.5根据提示访问首页
ip:5601
2.6REST风格访问es的dev tool
KIBANA可视化
本章节内容
了解kibana可视化作用
掌握视图化的构建过程和步骤
掌握dashboard仪表盘创建
理解可视化底层原理
1.kibana的可视化(visualization)
1.1进入可视化
o进入kibana的home界面
o左上选择visualize
1.2配置index-patterns
在我们创建基于数据的可视化之前,需要对索引进行管理,就需要先创建index-patterns.将大量索引文件分组管理.
o进入discover
o选择manage space
o左侧选择index pattern
o点击create_pattern
o创建-next step
根据索引名称创建分组,选择next step,如果索引文件唯一,那么可以直接使用索引的全名称,如果索引文件分组了,比如:index-日期,按照每天创建一个索引.那么可以使用index*将所有这样的索引归类到一组
2.可视化模块
这里我们可以基于前面案例中employee的索引来创建我们需要的视图模型
2.1进入visualize创建视图
o根据第一章节我们进入visualize
o点击create visualization创建视图
o选择视图模型
我们可以选择非常丰富的视图模型来满足我们的需求,包括折线图,柱状图,饼图,地图,云图等等,这里我们选择lens先来完成一个柱状图的搭建
①:选择数据进行图形创建可以使用的过滤器,左侧是KQL或者filter编写,右侧是常用的时间过滤器,可以选择最近1小时,最近1天等数据
②:当前使用的index-pattern中的数据字段
③:创建不同图形需要的数据,这里是柱状图,所以有x轴和y轴,本质就是创建不同的聚合计算条件值
④:展示图形的地方
2.2柱状图分析
需求:我们按照工种job,来展示一下所有工种的平均工资.这样X轴就是工种,Y轴就是平均薪资
x轴配置
①:这里给了3个可以创建数据的函数
ofilters:使用KQL的筛选分桶
ointervals:使用数字范围的间隔分桶
otop values:使用数据中的分桶
②:可选索引字段,因为种类使用top values所以必须是有keyword类型的字段
③:排序方式,这里由于配置了Y轴,使用Y轴的值做排序
④:升降排序
⑤:X轴名称
Y轴配置
①:metric聚合类型,根据不同数据选择不同聚合类型
oaverage:平均值
ominimum:最小值
ocount:记数,对分桶数据做doc记数
osum:总和
omaximum:最大值
ounique count:唯一记数,做去重处理
这里我们如果对工资字段做计算,name可以选择平均值,最小值,总和,maximum最合理
②:可选索引字段,因为种类使用top values所以必须是有keyword类型的字段
③:排序方式,这里由于配置了Y轴,使用Y轴的值做排序
④:升降排序
⑤:X轴名称
视图化结果
break down
这里根据x-y轴的数据图形,我们可以继续实现底层子聚合,将数据展示更明确,例如:我们可以按工种查看工资平均值之后,继续看每个工种的性别平均工资.
①:和X轴配置相同
②:选择字段
③:选择取值区间的个数,这里只有2个,超过2个无效
④:选择排序依据
⑤:升序降序
⑥:标题
视图化结果
建议化扩展
在我们配置完XY轴和break down数据之后,kibana的视图化还会为我们推荐相同配置数据下可用的其他视图模型.
所以饼图可以使用相同数据就不在需要我们再配置一遍了
保存视图化结果
点击右上角save保存视图化结果
给当前视图化结果起名,和描述信息
3.仪表盘模块
当我们使用一套index-pattern创建了多个视图结果之后,我们就具备了一套可以查看这个index-pattern的可视化分析数据,这时我们可以创建仪表盘
注意:创建仪表盘不一定必须先创建可视化图形分析,也可以在仪表盘中直接创建,然后在新增可视化数据图形
3.1创建仪表盘
ohome中选择dashboard
o创建新的仪表盘
o添加已存在的可视化图形
由于这里我们是先创建的可视化,然后再创建的仪表盘所以可以直接add,当然也可以在下面直接创建这个仪表盘的可视化图形.
o将创建好的可视化选择添加
o保存仪表盘
填写仪表盘信息
3.2分享仪表盘结果
由于可视化是在kibana出现的,我们可以将仪表盘或者可视化图形利用连接形式分享给别人
o进入仪表盘的可视化图形点击share
o分享类型
embed code
这个是创建仪表盘的iframe,可以嵌套在html页面中使用,方便前端人员开发
permalinks
这个是直接创建仪表盘连接地址url分享的方式
4.kibana可视化底层原理
这里我们不讨论可视化效果是如何出现的,无论是哪种可视化插件或者程序,都是基于底层数据实现的,比如echarts只是提供了可视化的框架,如果我们需要进行可视化的使用需要自定义框架中的数据填充,kibana的好处就是对接了elasticsearch这个庞大的索引库,进行数据的获取和返回的解析.
每一个可视化图形,都是kibana根据我们配置的内容编写的底层聚合查询,根据返回结果进行数据的可视化显示,这就是他的原理,我们可以在discovery中查看每一个可视化的返回数据结果.
我们选择仪表盘的一张可视化图标
右上点击inspect
可以看到在这个饼图中,他使用了聚合的request请求,并且获取了聚合响应response
ES启动报错问题大全
1 内存不够报错
vi jvm.options
es 5.x版本默认启动内存需要4g ,对于一些低内存的虚拟机或者云服务器可能无法启动,需要修改默认内存参数
红色是默认的,黄色是我修改后我自己机器的,大小根据你机器配置修改。
2 can not run es as root
产生这个错误原因是:
这是出于系统安全考虑设置的条件。由于ElasticSearch可以接收用户输入的脚本并且执行,为了系统安全考虑,
建议创建一个单独的用户用来运行ElasticSearch
解决办法:
单独创建一个用户来专门启动 es
创建es用户组及es用户
groupadd es
useradd es-g es-p es
更改elasticsearch文件夹及内部文件的所属用户及组为es : es
chown -R es:es elasticsearch
elasticsearch为你elasticsearch的目录名称
切换到es用户再启动
su es #切换账户
cd elasticsearch/bin #进入你的elasticsearch目录下的bin目录
成功启动
3 UnsupportedOperationException: seccomp unavailable: CONFIG_SECCOMP not compiled into kernel, CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER are needed
解决办法:
原因: 因为Centos6不支持SecComp,而ES默认bootstrap.system_call_filter为true进行检测,所以导致检测失败,失败后直接导致ES不能启动解决:修改elasticsearch.yml 添加一下内容 :
bootstrap.memory_lock: false
bootstrap.system_call_filter: false
4 Caused by: java.net.BindException: Cannot assign requested address
配置外网
进入 config/ elasticsearch.ym
打开配置文件elasticsearch.yml 将 network.host: 192.168.0.1 修改为本机IP 0.0.0.0
5 initial heap size [104857600] not equal to maximum heap size [209715200]; this can cause resize pauses and prevents mlockall from locking the entire heap
[2]: max file descriptors [65535] for elasticsearch process is too low, increase to at least [65536]
[3]: max number of threads [1024] for user [es] is too low, increase to at least [2048]
[4]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
解决办法:
max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
解决:切换到root用户修改配置sysctl.conf
vi /etc/sysctl.conf
添加下面配置:
vm.max_map_count=655360
并执行命令:
sysctl -p
max number of threads [1024] for user [es] is too low, increase to at least [2048]
解决:切换到root用户,进入limits.d目录下修改配置文件。
vi /etc/security/limits.d/90-nproc.conf
修改如下内容:
soft nproc 1024
#修改为
soft nproc 2048
max file descriptors [65535] for elasticsearch process is too low, increase to at least [65536]
解决:切换到root用户,编辑limits.conf 添加类似如下内容
vi /etc/security/limits.conf
添加如下内容:
-
soft nofile 65536
-
hard nofile 131072
-
soft nproc 2048
*** soft nofile 65536
已使用 OneNote 创建。