大数据技术ELK实时检索

一 elasticsearch简介

ElasticSearch是一个高性能,基于Lucene的全文检索服务,是一个分布式的Restful风格的搜索和数据分析引擎,也可以作为NoSQL数据库使用。

对Lucene进行了扩展
原型环境和生产环境可无缝切换
能够水平扩展
支持结构化和非结构化数据

​ ElasticSearch对Lucene 进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展,并对查询性能进行了优化,还提供了一个完善的功能管理界面。
​ 原型环境和生产环境可无缝切换;无论ElasticSearch是在一个节点上运行,还是在一个包含300节点的集群上运行,都能够以相同的方式与ElasticSearch进行通信。它能够水平扩展,每秒钟可处理海量事件,同时能够自动管理索引和查询在集群中的分布方式,以实现极其流畅的操作。支持数字、文本、地理位置、即结构化和非结构化数据。
​ Lucene 是apache软件基金会一个开放源代码的全文检索引擎工具包,是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎(搜索引擎和检索程序库不完全等同)。
​ lucene,最先进、功能最强大的搜索库,直接基于lucene开发,非常复杂,api复杂(实现一些简单的功能,写大量的java代码),需要深入理解原理(各种索引结构)。elasticsearch,基于lucene,隐藏复杂性,提供简单易用的restful api接口、java api接口

二 elasticSearch的使用场景

用于日志搜索和分析、时空检索、时序检索、智能搜索等场景。
检索的数据类型复杂:如需要查询的数据有结构化数据、半结构化数据、非结构化数据等,ElasticSearch可以对以上数据类型进行清洗、分词、建立倒排索引等一系列操作,然后提供全文检索的能力。
检索条件多样化:全文检索条件可以包括词或短语。
边写边读:写入的数据可以实时的进行检索。

​ 结构化数据(关系型数据库等)、半结构化数据(网页、XML等)、非结构化数据(日志、图片、图像等)
一系列操作指建立索引
​ 检索条件多样化如涉及字段太多;全文检索(查询)可以包括词和短语,或者词或短语的多种形式。
检索或者说查询。

时空检索,针对时空数据:

时空数据是同时具有时间和空间维度的数据,现实世界中的数据超过80%与地理位置有关。
时空大数据包括时间、空间、专题属性三维信息,具有多源、海量、更新快速的综合特点。

时序检索,针对是时序数据:

时序数据是指时间序列数据。时间序列数据是同一统一指标按时间顺序记录的数据列。在同一数据列中的各个数据必须是同口径的,要求具有可比性。时序数据可以是时期数,也可以时点数。时间序列分析的目的是通过找出样本内时间序列的统计特性和发展规律性,构建时间序列模型,进行样本外预测。
传感器数据,温度等。

三 ElasticSearch特点

image-20201027211831426.png

高性能/速度

能立即获得搜索结果。我们通过有限状态转换器实现了用于全文检索的倒排索引,实现了用于存储数值数据和地理位置数据的 BKD 树,以及用于分析的列存储。由于每个数据都被编入了索引,就不必担心某些数据没有索引。

ElasticSearch倒排索引

  • 正排索引:是通过Key寻找Value,即从关键点出发,然后再通过关键点找到信息中满足搜索条件的特定信息。传统的搜索方式(正排序索引)是从关键点出发,通过正排序索引进行搜索,就是从通过文档编号找关键词。
  • 倒排索引:ElasticSearch所采用得排序方式,是通过Value找Key。而在全文搜索中Value就是要搜索的关键词,通过Value找到对应的文档。具体如下面的图所示:通过倒排索引进行搜索,就是通过关键词查询对应的文档编号,再通过文档编号找文档,类似于查字典,或通过查书目录查指定页码书的内容。

ElasticSearch倒排索引 - 效果图

  • 通过倒排索引进行搜索,就是通过关键词查询对应的文档编号,再通过文档编号找文档,类似于查字典,或通过查书目录查指定页码书的内容。

image-20201027214502934.png

可扩展性

可以在笔记本电脑上运行。也可在承载了 PB 级数据的成百上千台服务器上运行。
原型环境和生产环境可无缝切换;无论是在一个节点上运行,还是在一个包含 300 个节点的集群上运行,您都能够以相同的方式与 Elasticsearch 进行通信。
能够水平扩展,每秒钟可处理海量事件,同时能够自动管理索引和查询在集群中的分布方式,以实现极其流畅的操作。

相关度

搜索所有内容。找到所需的具体信息。基于各项元素(从词频或近因到热门度等)对搜索结果进行排序。将这些内容与功能进行混合和匹配,以对向用户显示结果的方式进行微调。Elasticsearch 功能齐全,可以处理包括各种复杂情况(例如拼写错误)在内的人为错误。

可靠性 /弹性

硬件故障。网络分割。Elasticsearch 为您检测这些故障并确保您的集群(和数据)的安全性和可用性。跨集群复制功能,辅助集群可作为热备份随时投入使用。Elasticsearch 运行在一个分布式的环境中,从设计之初就考虑到了这一点,目的就是让您永远高枕无忧。

四 ElasticSearch生态圈

image-20201027211932309.png

ELK/ELKB提供了一整套解决方案并且都是开源软件,之间互相配合使用,完美衔接,高效的满足了很多场合的应用。

插件扩展层:

  • L o g s t a s h Logstash Logstash:具备实时数据传输能力的管道,着重日志相关处理;负责将数据信息从管道的输入端传输到管道的输出端;支持灵活根据自己的需求在中间加上滤网,Logstash提供里很多功能强大的滤网以满足你的各种应用场景
  • K i b a n a Kibana Kibana:开源的分析和可视化平台,数据主要由es提供;基于es的搜索与分析能力,拿到用于上层分析和可视化需要的结果;开发者或运维人员可以轻松地执行高级数据分析,并在各种图表、表格和地图中可视化数据。
  • B e a t s Beats Beats:专门用于发送数据的平台,可以将数据无缝传输给logstash或是es;轻量级代理机制安装,类似于hadoop集群安装时候的ambari或cdh manager;可将数以百千计算机中的数据发送到logstash或是es中。
    e s − h a d o o p es-hadoop eshadoop:一个深度集成Hadoop和es的项目,是es官方维护的一个子项目;可以达到hadoop与es之间的输入与输出;重点是充分利用Map-Reduce的并行计算优势,为hdfs数据提供实时搜索能力。
  • e s − s q l es-sql essql:用sql来操作es,来代替之前需要写各种复杂的json查询才可解决的问题;es-sql目前有两个版本,第一,是多年前即开始的国内主推开源的nlpchina/es-sql插件,第二,是自2018.06官方es6.3.0发布后正式支持的es-sql功能。
  • e l a s t i c s e a r c h − h e a d elasticsearch-head elasticsearchhead:将是一款专门针对于elasticsearch的客户端工具,是一个界面化的集群操作和管理工具,可以对集群进行傻瓜式操作,是一个基于node.js的前端工程,
    B i g d e s k Bigdesk Bigdesk:是elasticsearch的一个集群监控工具,可以通过它来查看es集群的各种状态,如:cpu、内存使用情况,索引数据、搜索情况,http连接数等。

五 与其他数据存储进行比较

redis mysql elasticsearch hbase hadoop/hive
容量/容量扩展 较大 海量 海量
查询时效性 极高 中等 较高 中等
查询灵活性 较差 k-v模式 非常好,支持sql 较好,关联查询较弱,但是可以全文检索,DSL语言可以处理过滤、匹配、排序、聚合等各种操作 较差,主要靠rowkey,scan的话性能不行,或者建立二级索引 非常好,支持sql
写入速度 极快 中等 较快 较快
一致性、事务

六 elasticSearch的安装

1.解压es压缩包到opt目录下

[root@houda software]# tar -zxvf /software/elasticsearch-6.6.0.tar.gz -C /opt/

2.添加es相关配置

[root@houda opt]# vim /opt/elasticsearch-6.6.0/config/elasticsearch.yml
# ======================== Elasticsearch Configuration =========================
#
# NOTE: Elasticsearch comes with reasonable defaults for most settings.
#       Before you set out to tweak and tune the configuration, make sure you
#       understand what are you trying to accomplish and the consequences.
#
# The primary way of configuring a node is via this file. This template lists
# the most important settings you may want to configure for a production cluster.
#
# Please consult the documentation for further information on configuration options:
# https://www.elastic.co/guide/en/elasticsearch/reference/index.html
#
# ---------------------------------- Cluster -----------------------------------
#
# Use a descriptive name for your cluster:
# 设置es的集群名称
cluster.name: my-es
#
# ------------------------------------ Node ------------------------------------
#
# Use a descriptive name for the node:
# 设置子节点名称
node.name: node-1
#
# Add custom attributes to the node:
#
#node.attr.rack: r1
#
# ----------------------------------- Paths ------------------------------------
#
# Path to directory where to store the data (separate multiple locations by comma):
#
#path.data: /path/to/data
#
# Path to log files:
#
#path.logs: /path/to/logs
#
# ----------------------------------- Memory -----------------------------------
#
# Lock the memory on startup:
# bootstrap 自检程序关闭
bootstrap.memory_lock: false
bootstrap.system_call_filter: false
#
# Make sure that the heap size is set to about half the memory available
# on the system and that the owner of the process is allowed to use this
# limit.
#
# Elasticsearch performs poorly when the system is swapping the memory.
#
# ---------------------------------- Network -----------------------------------
#
# Set the bind address to a specific IP (IPv4 or IPv6):
# 改为当前的ip或hostname
network.host: houda
#
# Set a custom port for HTTP:
#
#http.port: 9200
#
# For more information, consult the network module documentation.
#
# --------------------------------- Discovery ----------------------------------
#
# Pass an initial list of hosts to perform discovery when new node is started:
# The default list of hosts is ["127.0.0.1", "[::1]"]
# 自发现配置:新节点向集群报到的主机名
discovery.zen.ping.unicast.hosts: ["houda", "houda02", "houda03"]
#
# Prevent the "split brain" by configuring the majority of nodes (total number of master-eligible nodes / 2 + 1):
#
#discovery.zen.minimum_master_nodes:
#
# For more information, consult the zen discovery module documentation.
#
# ---------------------------------- Gateway -----------------------------------
#
# Block initial recovery after a full cluster restart until N nodes are started:
#
#gateway.recover_after_nodes: 3
#
# For more information, consult the gateway module documentation.
#
# ---------------------------------- Various -----------------------------------
#
# Require explicit names when deleting indices:
# #action.destructive_requires_name: true

3.增大集群限制信息

[root@houda ~]# vim /etc/security/limits.conf

添加内容:(注意:*号不能省略)

  • * hard nofile 65536
    * soft nofile 131072
    * hard nproc 4096
    * soft nproc 2048
    

image-20201027223725445.png

4.调小es使用的内存大小,避免我们机器内存溢出

[root@houda ~]# vim /opt/elasticsearch-6.6.0/config/jvm.options

image-20201027223843112.png

增加es用户内存权限

[root@hd01 elasticsearch-6.6.0]# sysctl -w vm.max_map_count=262144

[root@hd01 elasticsearch-6.6.0]# vim /etc/sysctl.conf

vm.max_map_count=262144

image-20201103145056330.png

5.修改es所属用户和组,root用户无法启动es服务

[root@houda opt]# chown -R laohou:laohou /opt/elasticsearch-6.6.0/

6.切换用户,启动es服务

[root@houda opt]# su laohou
# 前台启动
[laohou@houda opt]$ /opt/elasticsearch-6.6.0/bin/elasticsearch
# 后台启动
[laohou@houda opt]$ /opt/elasticsearch-6.6.0/bin/elasticsearch -d

7.测试es是否正常

[root@houda ~]# curl [http://houda:9200/_cat/nodes?v](http://houda:9200/_cat/nodes?v)
ip            heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
192.168.8.120           36          44  20    1.22    0.41     0.35 mdi       *      node-1

七 kibana安装

# 解压kibana到opt目录
[root@houda opt]# tar -zxvf /software/kibana-6.6.0-linux-x86_64.tar.gz -C /opt/
# 进入opt目录
[root@houda opt]# cd /opt/
# 修改kibana目录名称
[root@houda opt]# mv kibana-6.6.0-linux-x86_64/ kibana-6.6.0
# 进入config
[root@houda kibana-6.6.0]# cd /opt/kibana-6.6.0/config/
# 修改参数
[root@houda config]# vim kibana.yml

image-20201028203123698.png

# 修改kibana所属用户和组
[root@houda opt]# chown -R laohou:laohou /opt/kibana-6.6.0/
# 启动kibana(请先启动es) 前台启动
[laohou@houda opt]$ /opt/kibana-6.6.0/bin/kibana
# 后台启动
[laohou@houda opt]$ nohup /opt/kibana-6.6.0/bin/kibana >/dev/null 2>&1 &

image-20201028204109990.png

  • Discover:数据搜索查看

  • Visualize:图标制作

  • Dashboard:仪表盘制作

  • Timeline:时序数据的高级可视化分析

  • DevTools:开发者工具

  • Management:kibana相关配置

八 elasticsearch的基本概念

cluster 整个elasticsearch 默认就是集群状态,整个集群是一份完整、互备的数据。
node 集群中的一个节点,一般只一个进程就是一个node
shard 分片,即使是一个节点中的数据也会通过hash算法,分成多个片存放,默认是5片。(7.0默认改为1片)
index 相当于rdbms的database(5.x), 对于用户来说是一个逻辑数据库,虽然物理上会被分多个shard存放,也可能存放在多个node中。 6.x 7.x index相当于table
type 类似于rdbms的table,但是与其说像table,其实更像面向对象中的class , 同一Json的格式的数据集合。(6.x只允许建一个,7.0被废弃,造成index实际相当于table级)
document 类似于rdbms的 row、面向对象里的object
field 相当于字段、属性
GET /_cat/nodes?v  查询各个节点状态
GET /_cat/indices?v  查询各个索引状态
GET /_cat/shards/xxxx  查询某个索引的分片情况

九 elasticsearch restful api (DSL)

DSL全称 Domain Specific language,即特定领域专用语言。

1、es中保存的数据结构

public class  Movie {
    
    
	 String id;
     String name;
     Double doubanScore;
     List<Actor> actorList;
}
public class Actor{
    
    
String id;
String name;
}

这两个对象如果放在关系型数据库保存,会被拆成2张表,但是elasticsearch是用一个json来表示一个document。
所以他保存到es中应该是:

{
    
    "id":"1",
  "name":"operation red sea",
  "doubanScore":"8.5",
  "actorList":[  
{
    
    "id":"1","name":"zhangyi"},
{
    
    "id":"2","name":"haiqing"},
{
    
    "id":"3","name":"zhanghanyu"}
] }

2 对数据的操作

2.1 查看es中有哪些索引

GET /_cat/indices?v
  • es 中会默认存在一个名为.kibana的索引

表头的含义

health green(集群完整) yellow(单点正常、集群不完整) red(单点不正常)
status 是否能使用
index 索引名
uuid 索引统一编号
pri 主节点几个
rep 从节点几个
docs.count 文档数
docs.deleted 文档被删了多少
store.size 整体占空间大小
pri.store.size 主节点占

2.2 增加一个索引

PUT /movie_index

2.3 删除一个索引

​ ES 是不删除也不修改任何数据的,而是增加版本号

DELETE /movie_index

2.4 新增文档

1、格式 PUT /index/type/id
PUT /movie_index/movie/1
{
    
     "id":1,
  "name":"operation red sea",
  "doubanScore":8.5,
  "actorList":[  
{
    
    "id":1,"name":"zhang yi"},
{
    
    "id":2,"name":"hai qing"},
{
    
    "id":3,"name":"zhang han yu"}
]
}
PUT /movie_index/movie/2
{
    
    
  "id":2,
  "name":"operation meigong river",
  "doubanScore":8.0,
  "actorList":[  
{
    
    "id":3,"name":"zhang han yu"}
]
}
PUT /movie_index/movie/3
{
    
    
  "id":3,
  "name":"incident red sea",
  "doubanScore":5.0,
  "actorList":[  
{
    
    "id":4,"name":"zhang chen"}
]
}
  • 如果之前没建过index或者type,es 会自动创建。

2.5 直接用id查找

GET movie_index/movie/1

2.6 修改—整体替换

和新增没有区别 要求:必须包括全部字段

PUT /movie_index/movie/3
{
    
    
  "id":"3",
  "name":"incident red sea",
  "doubanScore":"5.0",
  "actorList":[  
{
    
    "id":"1","name":"zhang chen"}
]
}

2.7修改—某个字段

POST movie_index/movie/3/_update
{
    
     
  "doc": {
    
    
    "doubanScore":"7.0"
  } 
}

2.8 删除一个document

DELETE movie_index/movie/3

2.9 搜索type全部数据

GET movie_index/movie/_search
结果
{
    
    
  "took": 2,    //耗费时间 毫秒
  "timed_out": false, //是否超时
  "_shards": {
    
    
    "total": 5,   //发送给全部5个分片
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    
    
    "total": 3,  //命中3条数据
    "max_score": 1,   //最大评分
    "hits": [  // 结果
      {
    
    
        "_index": "movie_index",
        "_type": "movie",
        "_id": 2,
        "_score": 1,
        "_source": {
    
    
          "id": "2",
          "name": "operation meigong river",
          "doubanScore": 8.0,
          "actorList": [
            {
    
    
              "id": "1",
              "name": "zhang han yu"
            }
          ]
        }
      }

2.10 按条件查询(全部)

GET movie_index/movie/_search
{
    
    
  "query":{
    
    
    "match_all": {
    
    }
  }
}

2.11 分词查询

GET movie_index/movie/_search
{
    
    
  "query":{
    
    
    "match": {
    
    "name":"red"}
  }
}

2.12 按分词子属性查询

GET movie_index/movie/_search
{
    
    
  "query":{
    
    
    "match": {
    
    "actorList.name":"zhang"}
  }
}

2.13 match phrase

GET movie_index/movie/_search
{
    
    
    "query":{
    
    
      "match_phrase": {
    
    "name":"operation red"}
    }
}

按短语查询,不再利用分词技术,直接用短语在原始数据中匹配

2.14 fuzzy查询

GET movie_index/movie/_search
{
    
    
    "query":{
    
    
      "fuzzy": {
    
    "name":"rad"}
    }
}

校正匹配分词,当一个单词都无法准确匹配,es通过一种算法对非常接近的单词也给与一定的评分,能够查询出来,但是消耗更多的性能。

2.15 过滤–查询后过滤

GET movie_index/movie/_search
{
    
    
    "query":{
    
    
      "match": {
    
    "name":"red"}
    },
    "post_filter":{
    
    
      "term": {
    
    
        "actorList.id": 3
      }
    }
}

2.16 过滤–查询前过滤(推荐使用)

GET movie_index/movie/_search
{
    
     
    "query":{
    
    
        "bool":{
    
    
          "filter":[ {
    
    "term": {
    
      "actorList.id": "1"  }},
                     {
    
    "term": {
    
      "actorList.id": "3"  }}
           ], 
           "must":{
    
    "match":{
    
    "name":"red"}}
         }
    }
}

2.17 过滤–按范围过滤

GET movie_index/movie/_search
{
    
    
   "query": {
    
    
     "bool": {
    
    
       "filter": {
    
    
         "range": {
    
    
            "doubanScore": {
    
    "gte": 8}
         }
       }
     }
   }
}
  • 关于范围操作符:
    gt 大于
    lt 小于
    gte 大于等于 great than or equals
    lte 小于等于 less than or equals

2.18 排序

GET movie_index/movie/_search
{
    
    
  "query":{
    
    
    "match": {
    
    "name":"red sea"}
  }
  , "sort": [
    {
    
    
      "doubanScore": {
    
    
        "order": "desc"
      }
    }
  ]
}

2.19 分页查询

GET movie_index/movie/_search
{
    
    
  "query": {
    
     "match_all": {
    
    } },
  "from": 1,
  "size": 1
}

2.20 指定查询的字段

GET movie_index/movie/_search
{
    
    
  "query": {
    
     "match_all": {
    
    } },
  "_source": ["name", "doubanScore"]
}

2.21 高亮

GET movie_index/movie/_search
{
    
    
    "query":{
    
    
      "match": {
    
    "name":"red sea"}
    },
    "highlight": {
    
    
      "fields": {
    
    "name":{
    
    } }
    }
}

2.22 聚合

取出每个演员共参演了多少部电影
GET movie_index/movie/_search
{
    
     
  "aggs": {
    
    
    "groupby_actor": {
    
    
      "terms": {
    
    
        "field": "actorList.name.keyword"  
      }
    }
  }
}
每个演员参演电影的平均分是多少,并按评分排序
GET movie_index/movie/_search
{
    
     
  "aggs": {
    
    
    "groupby_actor_id": {
    
    
      "terms": {
    
    
        "field": "actorList.name.keyword" ,
        "order": {
    
    
          "avg_score": "desc"
          }
      },
      "aggs": {
    
    
        "avg_score":{
    
    
          "avg": {
    
    
            "field": "doubanScore" 
          }
        }
       }
    } 
  }
}
  • 聚合时为何要加 .keyword后缀?
    .keyword 是某个字符串字段,专门储存不分词格式的副本 ,在某些场景中只允许只用不分词的格式,比如过滤filter 比如 聚合aggs, 所以字段要加上.keyword的后缀。

3 中文分词

elasticsearch本身自带的中文分词,就是单纯把中文一个字一个字的分开,根本没有词汇的概念。但是实际应用中,用户都是以词汇为条件,进行查询匹配的,如果能够把文章以词汇为单位切分开,那么与用户的查询条件能够更贴切的匹配上,查询速度也更加快速。
分词器下载网址:https://github.com/medcl/elasticsearch-analysis-ik

3.1 安装

  • 下载好的zip包,请解压后放到 …/elasticsearch/plugins/ik

然后重启es

3.2测试使用

使用默认

GET movie_index/_analyze
{
    
      
  "text": "我是中国人"
}

请观察结果
使用分词器

GET movie_index/_analyze
{
    
      "analyzer": "ik_smart", 
  "text": "我是中国人"
}

请观察结果
另外一个分词器
ik_max_word

GET movie_index/_analyze
{
    
      "analyzer": "ik_max_word", 
  "text": "我是中国人"
}

请观察结果
能够看出不同的分词器,分词有明显的区别,所以以后定义一个type不能再使用默认的mapping了,要手工建立mapping, 因为要选择分词器。

3.3 自定义词库

修改/opt/elasticsearch-6.6.0/plugins/ik/config/中的IKAnalyzer.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 扩展配置</comment>
        <!--用户可以在这里配置自己的扩展字典 -->
        <entry key="ext_dict"></entry>
         <!--用户可以在这里配置自己的扩展停止词字典-->
        <entry key="ext_stopwords"></entry>
        <!--用户可以在这里配置远程扩展字典 -->
         <entry key="remote_ext_dict">http://192.168.67.163/fenci/myword.txt</entry>
        <!--用户可以在这里配置远程扩展停止词字典-->
        <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

按照标红的路径利用nginx发布静态资源
在nginx.conf中配置

 server {
    
    
        listen  80;
        server_name  192.168.67.163;
        location /fenci/ {
    
    
           root es;
    }
   }

并且在/usr/local/nginx/下建/es/fenci/目录,目录下加myword.txt
myword.txt中编写关键词,每一行代表一个词。
然后重启es服务器,重启nginx。
在kibana中测试分词效果
更新完成后,es只会对新增的数据用新词分词。历史数据是不会重新分词的。如果想要历史数据重新分词。需要执行:

POST movies_index/_update_by_query?conflicts=proceed

4 关于mapping

之前说type可以理解为table,那每个字段的数据类型是如何定义的呢

4.1 查看mapping

GET movie_index/_mapping/movie

实际上每个type中的字段是什么数据类型,由mapping定义。
但是如果没有设定mapping系统会自动,根据一条数据的格式来推断出应该的数据格式。

  • true/false → boolean
  • 1020 → long
  • 20.1 → double
  • “2018-02-01” → date
  • “hello world” → text +keyword

默认只有text会进行分词,keyword是不会分词的字符串。
mapping除了自动定义,还可以手动定义,但是只能对新加的、没有数据的字段进行定义。一旦有了数据就无法再做修改了。
注意:虽然每个Field的数据放在不同的type下,但是同一个名字的Field在一个index下只能有一种mapping定义。

4.2基于中文分词搭建索引

建立mapping
PUT movie_chn
{
    
    
  "mappings": {
    
    
    "movie":{
    
    
      "properties": {
    
    
        "id":{
    
    
          "type": "long"
        },
        "name":{
    
    
          "type": "text"
          , "analyzer": "ik_smart"
        },
        "doubanScore":{
    
    
          "type": "double"
        },
        "actorList":{
    
    
          "properties": {
    
    
            "id":{
    
    
              "type":"long"
            },
            "name":{
    
    
              "type":"keyword"
            }
          }
        }
      }
    }
  }
}
插入数据
PUT /movie_chn/movie/1
{
    
     "id":1,
  "name":"红海行动",
  "doubanScore":8.5,
  "actorList":[  
  {
    
    "id":1,"name":"张译"},
  {
    
    "id":2,"name":"海清"},
  {
    
    "id":3,"name":"张涵予"}
 ]
}
PUT /movie_chn/movie/2
{
    
    
  "id":2,
  "name":"湄公河行动",
  "doubanScore":8.0,
  "actorList":[  
{
    
    "id":3,"name":"张涵予"}
]
}
PUT /movie_chn/movie/3
{
    
    
  "id":3,
  "name":"红海事件",
  "doubanScore":5.0,
  "actorList":[  
{
    
    "id":4,"name":"张晨"}
]
}
查询测试
GET /movie_chn/movie/_search
{
    
    
  "query": {
    
    
    "match": {
    
    
      "name": "红海战役"
    }
  }
}
GET /movie_chn/movie/_search
{
    
    
  "query": {
    
    
    "term": {
    
    
      "actorList.name": "张译"
    }
  }
}

5索引别名 _aliases

索引别名就像一个快捷方式或软连接,可以指向一个或多个索引,也可以给任何一个需要索引名的API来使用。别名 带给我们极大的灵活性,允许我们做下面这些:

1.给多个索引分组 (例如, last_three_months)
2.给索引的一个子集创建视图
3.在运行的集群中可以无缝的从一个索引切换到另一个索引

5.1 创建索引别名

# 建表时直接声明
PUT movie_chn_2020
{
    
      "aliases": {
    
    
      "movie_chn_2020-query": {
    
    }
  }, 
  "mappings": {
    
    
    "movie":{
    
    
      "properties": {
    
    
        "id":{
    
    
          "type": "long"
        },
        "name":{
    
    
          "type": "text"
          , "analyzer": "ik_smart"
        },
        "doubanScore":{
    
    
          "type": "double"
        },
        "actorList":{
    
    
          "properties": {
    
    
            "id":{
    
    
              "type":"long"
            },
            "name":{
    
    
              "type":"keyword"
            }
          }
        }
      }
    }
  }
}
# 为已存在的索引增加别名
POST  _aliases
{
    
    
    "actions": [
        {
    
     "add":    {
    
     "index": "movie_chn_xxxx", "alias": "movie_chn_2020-query" }}
    ]
}
# 也可以通过加过滤条件缩小查询范围,建立一个子集视图
POST  _aliases
{
    
    
    "actions": [
        {
    
     "add":    
{
    
     "index": "movie_chn_xxxx", 
"alias": "movie_chn2019-query-zhhy",
            "filter": {
    
    
                "term": {
    
      "actorList.id": "3"
                 }
               }
 }
}
    ]
}

5.2 查询别名与使用普通索引没有区别

GET movie_chn_2020-query/_search

5.3 删除某个索引的别名

POST  _aliases
{
    
    
    "actions": [
        {
    
     "remove":    {
    
     "index": "movie_chn_xxxx", "alias": "movie_chn_2020-query" }}
    ]
}

5.4 为某个别名进行无缝切换

POST /_aliases
{
    
    
    "actions": [
        {
    
     "remove": {
    
     "index": "movie_chn_xxxx", "alias": "movie_chn_2020-query" }},
        {
    
     "add":    {
    
     "index": "movie_chn_yyyy", "alias": "movie_chn_2020-query" }}
    ]
}

5.5查询别名列表

GET  _cat/aliases?v

6 索引模板

Index Template 索引模板,顾名思义,就是创建索引的模具,其中可以定义一系列规则来帮助我们构建符合特定业务需求的索引的 mappings 和 settings,通过使用 Index Template 可以让我们的索引具备可预知的一致性。

6.1 常见的场景: 分割索引

​ 分割索引就是根据时间间隔把一个业务索引切分成多个索引。
比如 把order_info 变成 order_info_20200101,order_info_20200102 ……
这样做的好处有两个:
1结构变化的灵活性:因为elasticsearch不允许对数据结构进行修改。但是实际使用中索引的结构和配置难免变化,那么只要对下一个间隔的索引进行修改,原来的索引位置原状。这样就有了一定的灵活性。
2查询范围优化: 因为一般情况并不会查询全部时间周期的数据,那么通过切分索引,物理上减少了扫描数据的范围,也是对性能的优化。

6.2 创建模板

PUT _template/template_movie2020
{
    
    
  "index_patterns": ["movie_test*"],                  
  "settings": {
    
                                                   
    "number_of_shards": 1
  },
  "aliases" : {
    
     
    "{index}-query": {
    
    },
    "movie_test-query":{
    
    }
  },
  "mappings": {
    
                                              
"_doc": {
    
    
      "properties": {
    
    
        "id": {
    
    
          "type": "keyword"
        },
        "movie_name": {
    
    
          "type": "text",
          "analyzer": "ik_smart"
        }
      }
    }
  }
}

其中 “index_patterns”: [“movie_test*”], 的含义就是凡是往movie_test开头的索引写入数据时,如果索引不存在,那么es会根据此模板自动建立索引。
在 “aliases” 中用{index}表示,获得真正的创建的索引名。
测试

POST movie_test_2020xxxx/_doc
{
    
    
  "id":"333",
  "name":"zhang3"
}

6.3 查看系统中已有的模板清单

GET  _cat/templates

6.4查看某个模板详情

GET  _template/template_movie2020
或者
GET  _template/template_movie*

十 Elasticsearch流程

Elasticsearch索引流程

客户端发送索引请求给任意节点后,该节点判断索引分片位置并转发到对应的分片节点,分片节点执行请求后再并行发送至其余节点执行,其余节点全部执行成功后返会给用户执行成功信息。

image-20201031202924757.png

  • R表示replica shard 。P表示primary shard 。
  • 阶段1:客户端发送一个索引请求给任意节点,假设是Node 1。
  • 阶段2:Node 1通过请求判断出该文档应该被存储的分片,假设是shard 0 这个分片中,因此Node 1会把请求转发到shard 0的primary shard P0存在的Node 3节点上。
  • 阶段3:Node 3在shard 0 的primary shard P0上执行请求。如果请求执行成功,Node 3并行地将该请求发给shard 0的所有存在于Node 1和Node 2中的replica shard R0上。如果所有的replica shard都成功地执行了请求,那么将会向Node 3回复一个成功确认,当Node 3收到了所有replica shard的确认信息后,则向用户返回一个Success消息。

ElasticSearch批量索引流程

客户端发送批量索引请求至任意节点后,节点将转发请求至对应主分片节点,主分片节点按序操作,完成一个操作后再发送给其余复制节点执行。复制节点操作完成报告给主分片节点,主分片节点报告给请求节点并返回至客户端。

image-20201031203005726.png

  • 阶段1:客户端向Node 1发送大量(bulkv)请求。
  • 阶段2:Node 1为每个分片构建批量请求,然后转发到这些请求所需的主分片上。
  • 阶段3:主分片一个接一个的按序执行操作。当一个操作执行完,主分片转发新文档(或者删除部分)给对应的复制节点,然后执行下一个操作。复制节点为报告所有操作完成,节点报告给请求节点,请求节点整理响应并返回给客户端。

ElasticSearch搜索流程

节点收到所有待检索的数据后,发送请求给数据相关的分片,收到请求的分片节点会读取数据并返回给检索节点,检索节点汇总结果并返回给客户端。

image-20201031203110462.png

  • 该图为获取阶段。
  • 搜索流程分为两个阶段,即查询阶段与获取阶段。查询阶段主要定位了所要检索数据的具体位置,而获取阶段的任务就是将这些定位好的数据内容取回并返回给客户端
    查询阶段:
  • 阶段1:客户端发送一个检索请求给任意节点,假设是Node 3。
  • 阶段2:Node 3将检索请求发送给该index中的每一个shard,此时会采取轮询策略,在primary shard及其所有replica中随机选择一个,让读请求负载均衡。每个shard在本地执行检索,并将结果排序添加到本地。
    阶段3:每个shard返回本地所记录的结果,发送给Node 3。Node 3将这些值合并,做全局排序。
    获取阶段:
  • 阶段1:Node 3获取了所有待检索数据的定位之后,发送请求给与数据相关的shard。
  • 阶段2:每个收到Node 3请求的shard,将读取相关文档中的内容,并将它们返回给Node 3。
  • 阶段3:当Node 3获取到了所有shard返回的文档后,Node 3将它们合并成一条汇总结果,返回给客户端。

ElasticSearch批量搜索流程

客户端发送批量搜索请求至任意节点,该节点为每个分片分别构建一个多条数据检索请求,然后转发至主分片或副本分片。当所有请求执行完成后,请求节点汇总记录并返回给客户端。

image-20201031203205545.png

  • 阶段1:客户端向Node 1 发送mget 请求。
  • 阶段2:Node 1 为每个分片构建一个多条数据检索请求,然后转发到这些请求所需的主分片或副本分片上。当所有回复被接收,Node 1 构建响应并返回给客户端。
  • ElasticSearch其他特性:
    单节点多实例部署、副本自动跨节点分配策略、路由算法、平衡算法

十一 Elasticsearch Java API

1、依赖文件

</dependency>
<!-- ElasticSearch -->
<!-- https://mvnrepository.com/artifact/org.elasticsearch/elasticsearch -->
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>6.6.0</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
    <version>6.6.0</version>
</dependency>
<!-- 解锁ES运行时没有对应方法的的错误 -->
<dependency>
    <groupId>org.locationtech.spatial4j</groupId>
    <artifactId>spatial4j</artifactId>
    <version>0.6</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.13</version>
</dependency>
<dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.10.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.10.0</version>
    </dependency>

2、ES的增删改查

package utils;

import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.IndicesAdminClient;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.transport.client.PreBuiltTransportClient;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;

public class ElasticSearchUtil {
    
    
    private Settings settings = Settings.builder().put("cluster.name", "my-es").
            put("client.transport.sniff", false).build();
    private TransportClient client;

    /**
     * 获取es的客户端 client
     *
     * @return
     * @throws UnknownHostException
     */
    public TransportClient getClient() {
    
    
        if (client == null) {
    
    
            synchronized (TransportClient.class) {
    
    
                try {
    
    
                    client = new PreBuiltTransportClient(settings).
                            addTransportAddress(
                                    new TransportAddress(InetAddress.getByName("houda"), 9200));
                } catch (UnknownHostException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
        return client;
    }

    /**
     * 获取索引管理的IndicesAdminClient
     *
     * @return
     */
    public IndicesAdminClient getAdminClient() {
    
    
        return getClient().admin().indices();
    }

    /**
     * 判断下标是否存在
     *
     * @param indexName
     * @return
     */
    public boolean isExistsIndex(String indexName) {
    
    
        IndicesExistsResponse response = getAdminClient().prepareExists(indexName).get();
        return response.isExists();
    }

    /**
     * 创建索引
     *
     * @param indexName
     * @return
     */
    public boolean createIndex(String indexName) {
    
    
        CreateIndexResponse response = getAdminClient().prepareCreate(indexName.toLowerCase()).get();
        return response.isAcknowledged();
    }

    public boolean deleteIndex(String indexName) {
    
    
        AcknowledgedResponse response = getAdminClient().prepareDelete(indexName).execute().actionGet();
        return response.isAcknowledged();
    }

    public void setMapping(String indexName, String typeName, String mapping) {
    
    
        getAdminClient().preparePutMapping(indexName).setType(typeName).setSource(mapping, XContentType.JSON).get();
    }

    /**
     * 添加doc到索引 类型中
     *
     * @param indexName
     * @param typeName
     * @param id
     * @param document
     * @return
     * @throws IOException
     */
    public long addDocument(String indexName, String typeName, String id, Map<String, Object> document) throws IOException {
    
    
        IndexRequestBuilder builder = getClient().prepareIndex(indexName, typeName, id);
        Set<Map.Entry<String, Object>> entries = document.entrySet();
        XContentBuilder xContentBuilder = jsonBuilder().startObject();
        for (Map.Entry<String, Object> entry : entries) {
    
    
            xContentBuilder = xContentBuilder.field(entry.getKey(), entry.getValue());
        }
        IndexResponse indexResponse = builder.setSource(xContentBuilder.endObject()).get();
        return indexResponse.getVersion();
    }
# CRUT
    public List<Map<String, Object>> queryStringQuery(String text) {
    
    
        QueryStringQueryBuilder match = QueryBuilders.queryStringQuery(text);
        SearchRequestBuilder search = getClient().prepareSearch().setQuery(match);
        SearchResponse response = search.get();
//        命中的文档
        SearchHits hits = response.getHits();
//        命中文档数量
        long totalHits = hits.getTotalHits();
        SearchHit[] searchHits = hits.getHits();
        ArrayList<Map<String, Object>> maps = new ArrayList<>();
        for (SearchHit hit : searchHits) {
    
    
//            文档元数据
            String index = hit.getIndex();
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            maps.add(sourceAsMap);
        }
        return maps;
    }

    public static void main(String[] args) {
    
    
        ElasticSearchUtil util = new ElasticSearchUtil();
        IndicesAdminClient adminClient = util.getAdminClient();
        System.out.println(adminClient);
//        util.createIndex("houda");
//        System.out.println(util.getAdminClient());
//        System.out.println(util.isExistsIndex("houda"));
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_38620636/article/details/130435222