ELK搜索高级课程

第一章 课程简介

一、课程内容

ELK是包含但不限于Elasticsearch(简称es)、Logstash、Kibana 三个开源软件的组成的一个整体,分别取其首字母组成ELK。ELK是用于数据抽取(Logstash)、搜索分析(Elasticsearch)、数据展现(Kibana)的一整套解决方案,所以也称作ELK stack。

​ 本课程分别对三个组件进行详细介绍,尤其是Elasticsearch,因为它是ELK的核心。Elasticsearch会对底层的文档、索引、搜索、聚合、集群进行介绍,从搜索和聚合分析实例来展现它的魅力。Logstash会从内部如何采集数据到指定地方来展现它数据采集的功能。Kibana则从数据绘图来展现它数据可视化的能力。

二、面向人员

  • java工程师:深入研究es,使得java工程师向搜索工程师迈进。
  • 运维工程师:搭建整体elk集群。不需写代码,仅需配置,即可收集服务器指标、日志文件、数据库数据,并在前端华丽展现。
  • 数据分析人员:不需写代码,仅需配置kibana图表,即可完成数据可视化工作,得到想要的数据图表。
  • 大厂架构师:完成数据中台的搭建。对公司数据流的处理得心应手,对接本公司大数据业务。

三、课程优势

  • 基于最新的elk7.3版本讲解。最新api。包含sql功能。

  • 理论和实际代码相辅相成。理论结合画图讲解。代码与spring boot结合。

  • 包含实际运维部署理论与实践。

  • Elk整体流程项目,包含数据采集。

四、学习路径

参照目录,按照介绍,es入门,文档、映射、索引、分词器、搜索、聚合。logstash、kibana。集群部署。项目实战。

每个知识点先学概念,在学rest api,最后java代码上手。

第二章 Elastic Stack简介

一、简介

ELK是一个免费开源的日志分析架构技术栈总称,官网https://www.elastic.co/cn。包含三大基础组件,分别是Elasticsearch、Logstash、Kibana。但实际上ELK不仅仅适用于日志分析,它还可以支持其它任何数据搜索、分析和收集的场景,日志分析和收集只是更具有代表性。并非唯一性。下面是ELK架构:open in new window

1567691051440
1567691051440

随着elk的发展,又有新成员Beats、elastic cloud的加入,所以就形成了Elastic Stack。所以说,ELK是旧的称呼,Elastic Stack是新的名字。

1567691098941
1567691098941

二、特色

  • 处理方式灵活:elasticsearch是目前最流行的准实时全文检索引擎,具有高速检索大数据的能力。

  • 配置简单:安装elk的每个组件,仅需配置每个组件的一个配置文件即可。修改处不多,因为大量参数已经默认配在系统中,修改想要修改的选项即可。

  • 接口简单:采用json形式RESTFUL API接受数据并响应,无关语言。

  • 性能高效:elasticsearch基于优秀的全文搜索技术Lucene,采用倒排索引,可以轻易地在百亿级别数据量下,搜索出想要的内容,并且是秒级响应。

  • 灵活扩展:elasticsearch和logstash都可以根据集群规模线性拓展,elasticsearch内部自动实现集群协作。

  • 数据展现华丽:kibana作为前端展现工具,图表华丽,配置简单。

三、组件介绍

Elasticsearch

Elasticsearch 是使用java开发,基于Lucene、分布式、通过Restful方式进行交互的近实时搜索平台框架。它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。

Logstash

Logstash 基于java开发,是一个数据抽取转化工具。一般工作方式为c/s架构,client端安装在需要收集信息的主机上,server端负责将收到的各节点日志进行过滤、修改等操作在一并发往elasticsearch或其他组件上去。

Kibana

Kibana 基于nodejs,也是一个开源和免费的可视化工具。Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以汇总、分析和搜索重要数据日志。

Beats

Beats 平台集合了多种单一用途数据采集器。它们从成百上千或成千上万台机器和系统向 Logstash 或 Elasticsearch 发送数据。

Beats由如下组成:

​ Packetbeat:轻量型网络数据采集器,用于深挖网线上传输的数据,了解应用程序动态。Packetbeat 是一款轻量型网络数据包分析器,能够将数据发送至 Logstash 或 Elasticsearch。其支 持ICMP (v4 and v6)、DNS、HTTP、Mysql、PostgreSQL、Redis、MongoDB、Memcache等协议。

​ Filebeat:轻量型日志采集器。当您要面对成百上千、甚至成千上万的服务器、虚拟机和容器生成的日志时,请告别 SSH 吧。Filebeat 将为您提供一种轻量型方法,用于转发和汇总日志与文件,让简单的事情不再繁杂。

​ Metricbeat :轻量型指标采集器。Metricbeat 能够以一种轻量型的方式,输送各种系统和服务统计数据,从 CPU 到内存,从 Redis 到 Nginx,不一而足。可定期获取外部系统的监控指标信息,其可以监控、收集 Apache http、HAProxy、MongoDB、MySQL、Nginx、PostgreSQL、Redis、System、Zookeeper等服务。

​ Winlogbeat:轻量型 Windows 事件日志采集器。用于密切监控基于 Windows 的基础设施上发生的事件。Winlogbeat 能够以一种轻量型的方式,将 Windows 事件日志实时地流式传输至 Elasticsearch 和 Logstash。

​ Auditbeat:轻量型审计日志采集器。收集您 Linux 审计框架的数据,监控文件完整性。Auditbeat 实时采集这些事件,然后发送到 Elastic Stack 其他部分做进一步分析。

​ Heartbeat:面向运行状态监测的轻量型采集器。通过主动探测来监测服务的可用性。通过给定 URL 列表,Heartbeat 仅仅询问:网站运行正常吗?Heartbeat 会将此信息和响应时间发送至 Elastic 的其他部分,以进行进一步分析。

​ Functionbeat:面向云端数据的无服务器采集器。在作为一项功能部署在云服务提供商的功能即服务 (FaaS) 平台上后,Functionbeat 即能收集、传送并监测来自您的云服务的相关数据。

Elastic cloud

基于 Elasticsearch 的软件即服务(SaaS)解决方案。通过 Elastic 的官方合作伙伴使用托管的 Elasticsearch 服务。

1567691213668
1567691213668

第三章 Elasticsearch是什么

一、搜索是什么

概念:用户输入想要的关键词,返回含有该关键词的所有信息。

场景:

​ 1互联网搜索:谷歌、百度、各种新闻首页

​ 2 站内搜索(垂直搜索):企业OA查询订单、人员、部门,电商网站内部搜索商品(淘宝、京东)场景。

二、数据库做搜索弊端

1、站内搜索(垂直搜索):数据量小,简单搜索,可以使用数据库。

问题出现:

l 存储问题。电商网站商品上亿条时,涉及到单表数据过大必须拆分表,数据库磁盘占用过大必须分库(mycat)。

l 性能问题:解决上面问题后,查询“笔记本电脑”等关键词时,上亿条数据的商品名字段逐行扫描,性能跟不上。

l 不能分词。如搜索“笔记本电脑”,只能搜索完全和关键词一样的数据,那么数据量小时,搜索“笔记电脑”,“电脑”数据要不要给用户。

2、互联网搜索,肯定不会使用数据库搜索。数据量太大。PB级。

三、全文检索、倒排索引和Lucene

1、全文检索

倒排索引。数据存储时,经行分词建立term索引库。见画图。

倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件open in new window,简称倒排文件open in new window(inverted file)。

2、Lucene

就是一个jar包,里面封装了全文检索的引擎、搜索的算法代码。开发时,引入lucene的jar包,通过api开发搜索相关业务。底层会在磁盘建立索引库。

四、什么是Elasticsearch

1、简介

1567696557762
1567696557762

官网:https://www.elastic.co/cn/products/elasticsearchopen in new window

1567696574236
1567696574236

2、Elasticsearch的功能

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

搜索:互联网搜索、电商网站站内搜索、OA系统查询

数据分析:电商网站查询近一周哪些品类的图书销售前十;新闻网站,最近3天阅读量最高的十个关键词,舆情分析。

  • 全文检索,结构化检索,数据分析

全文检索:搜索商品名称包含java的图书select * from books where book_name like "%java%"。

结构化检索:搜索商品分类为spring的图书都有哪些,select * from books where category_id='spring'

数据分析:分析每一个分类下有多少种图书,select category_id,count(*) from books group by category_id

  • 对海量数据进行近实时的处理

分布式:ES自动可以将海量数据分散到多台服务器上去存储和检索,经行并行查询,提高搜索效率。相对的,Lucene是单机应用。

近实时:数据库上亿条数据查询,搜索一次耗时几个小时,是批处理(batch-processing)。而es只需秒级即可查询海量数据,所以叫近实时。秒级。

3、Elasticsearch的使用场景

国外:

  • 维基百科,类似百度百科,“网络七层协议”的维基百科,全文检索,高亮,搜索推荐

  • Stack Overflow(国外的程序讨论论坛),相当于程序员的贴吧。遇到it问题去上面发帖,热心网友下面回帖解答。

  • GitHub(开源代码管理),搜索上千亿行代码。

  • 电商网站,检索商品

  • 日志数据分析,logstash采集日志,ES进行复杂的数据分析(ELK技术,elasticsearch+logstash+kibana)

  • 商品价格监控网站,用户设定某商品的价格阈值,当低于该阈值的时候,发送通知消息给用户,比如说订阅《java编程思想》的监控,如果价格低于27块钱,就通知我,我就去买。

  • BI系统,商业智能(Business Intelligence)。大型连锁超市,分析全国网点传回的数据,分析各个商品在什么季节的销售量最好、利润最高。成本管理,店面租金、员工工资、负债等信息进行分析。从而部署下一个阶段的战略目标。

国内:

  • 百度搜索,第一次查询,使用es。

  • OA、ERP系统站内搜索。

4、Elasticsearch的特点

  • 可拓展性:大型分布式集群(数百台服务器)技术,处理PB级数据,大公司可以使用。小公司数据量小,也可以部署在单机。大数据领域使用广泛。

  • 技术整合:将全文检索、数据分析、分布式相关技术整合在一起:lucene(全文检索),商用的数据分析软件(BI软件),分布式数据库(mycat)

  • 部署简单:开箱即用,很多默认配置不需关心,解压完成直接运行即可。拓展时,只需多部署几个实例即可,负载均衡、分片迁移集群内部自己实施。

  • 接口简单:使用restful api经行交互,跨语言。

  • 功能强大:Elasticsearch作为传统数据库的一个补充,提供了数据库所不不能提供的很多功能,如全文检索,同义词处理,相关度排名。

1567696692347
1567696692347
1567696700718
1567696700718
1567696712256
1567696712256

五、elasticsearch核心概念

1、lucene和elasticsearch的关系

Lucene:最先进、功能最强大的搜索库,直接基于lucene开发,非常复杂,api复杂

Elasticsearch:基于lucene,封装了许多lucene底层功能,提供简单易用的restful api接口和许多语言的客户端,如java的高级客户端(Java High Level REST Clientopen in new window)和底层客户端(Java Low Level REST Clientopen in new window

1567696843304
1567696843304

起源:Shay Banon。2004年失业,陪老婆去伦敦学习厨师。失业在家帮老婆写一个菜谱搜索引擎。封装了lucene的开源项目,compass。找到工作后,做分布式高性能项目,再封装compass,写出了elasticsearch,使得lucene支持分布式。现在是Elasticsearch创始人兼Elastic首席执行官。

2、elasticsearch的核心概念

(1)NRT(Near Realtime):近实时

两方面:

  • 写入数据时,过1秒才会被搜索到,因为内部在分词、录入索引。

  • es搜索时:搜索和分析数据需要秒级出结果。

(2)Cluster:集群

包含一个或多个启动着es实例的机器群。通常一台机器起一个es实例。同一网络下,集群名一样的多个es实例自动组成集群,自动均衡分片等行为。默认集群名为“elasticsearch”。

(3)Node:节点

每个es实例称为一个节点。节点名自动分配,也可以手动配置。

(4)Index:索引

包含一堆有相似结构的文档数据。

索引创建规则:

  • 仅限小写字母

  • 不能包含\、/、 *、?、"、<、>、|、#以及空格符等特殊符号

  • 从7.0版本开始不再包含冒号

  • 不能以-、_或+开头

  • 不能超过255个字节(注意它是字节,因此多字节字符将计入255个限制)

(5)Document:文档

es中的最小数据单元。一个document就像数据库中的一条记录。通常以json格式显示。多个document存储于一个索引(Index)中。

From: 元动力
1
2
3
4
5
6
7
8
9
book document

{
"book_id": "1",
"book_name": "java编程思想",
"book_desc": "从Java的基础语法到最高级特性(深入的[面向对象](https://baike.baidu.com/item/面向对象)概念、多线程、自动项目构建、单元测试和调试等),本书都能逐步指导你轻松掌握。",
"category_id": "2",
"category_name": "java"
}
(6)Field:字段

就像数据库中的列(Columns),定义每个document应该有的字段。

(7)Type:类型

每个索引里都可以有一个或多个type,type是index中的一个逻辑数据分类,一个type下的document,都有相同的field。

注意:6.0之前的版本有type(类型)概念,type相当于关系数据库的表,ES官方将在ES9.0版本中彻底删除type。本教程typy都为_doc。

(8)shard:分片

index数据过大时,将index里面的数据,分为多个shard,分布式的存储在各个服务器上面。可以支持海量数据和高并发,提升性能和吞吐量,充分利用多台机器的cpu。

(9)replica:副本

在分布式环境下,任何一台机器都会随时宕机,如果宕机,index的一个分片没有,导致此index不能搜索。所以,为了保证数据的安全,我们会将每个index的分片经行备份,存储在另外的机器上。保证少数机器宕机es集群仍可以搜索。

能正常提供查询和插入的分片我们叫做主分片(primary shard),其余的我们就管他们叫做备份的分片(replica shard)。

es6默认新建索引时,5分片,2副本,也就是一主一备,共10个分片。所以,es集群最小规模为两台。

3、elasticsearch核心概念 vs. 数据库核心概念

关系型数据库(比如Mysql)非关系型数据库(Elasticsearch)
数据库Database索引Index
表Table索引Index(原为Type)
数据行Row文档Document
数据列Column字段Field
约束 Schema映射Mapping

第四章 Elasticsearch相关软件安装

一、Windows安装elasticsearch

1、安装JDK,至少1.8.0_73以上版本,验证:java -version。

2、下载和解压缩Elasticsearch安装包,查看目录结构。

https://www.elastic.co/cn/downloads/elasticsearchopen in new window

bin:脚本目录,包括:启动、停止等可执行脚本

config:配置文件目录

data:索引目录,存放索引文件的地方

logs:日志目录

modules:模块目录,包括了es的功能模块

plugins :插件目录,es支持插件机制

3、配置文件:

位置:

ES的配置文件的地址根据安装形式的不同而不同:

使用zip、tar安装,配置文件的地址在安装目录的config下。

使用RPM安装,配置文件在/etc/elasticsearch下。

使用MSI安装,配置文件的地址在安装目录的config下,并且会自动将config目录地址写入环境变量ES_PATH_CONF。

elasticsearch.yml

配置格式是YAML,可以采用如下两种方式:

方式1:层次方式

From: 元动力
1
2
3
path:
data: /var/lib/elasticsearch
logs: /var/log/elasticsearch

方式2:属性方式

From: 元动力
1
2
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch

常用的配置项如下

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
cluster.name: 
配置elasticsearch的集群名称,默认是elasticsearch。建议修改成一个有意义的名称。
node.name:
节点名,通常一台物理服务器就是一个节点,es会默认随机指定一个名字,建议指定一个有意义的名称,方便管理
一个或多个节点组成一个cluster集群,集群是一个逻辑的概念,节点是物理概念,后边章节会详细介绍。
path.conf:
设置配置文件的存储路径,tar或zip包安装默认在es根目录下的config文件夹,rpm安装默认在/etc/ elasticsearch
path.data:
设置索引数据的存储路径,默认是es根目录下的data文件夹,可以设置多个存储路径,用逗号隔开。
path.logs:
设置日志文件的存储路径,默认是es根目录下的logs文件夹
path.plugins:
设置插件的存放路径,默认是es根目录下的plugins文件夹
bootstrap.memory_lock: true
设置为true可以锁住ES使用的内存,避免内存与swap分区交换数据。
network.host:
设置绑定主机的ip地址,设置为0.0.0.0表示绑定任何ip,允许外网访问,生产环境建议设置为具体的ip。
http.port: 9200
设置对外服务的http端口,默认为9200。
transport.tcp.port: 9300 集群结点之间通信端口
node.master:
指定该节点是否有资格被选举成为master结点,默认是true,如果原来的master宕机会重新选举新的master。
node.data:
指定该节点是否存储索引数据,默认为true。
discovery.zen.ping.unicast.hosts: ["host1:port", "host2:port", "..."]
设置集群中master节点的初始列表。
discovery.zen.ping.timeout: 3s
设置ES自动发现节点连接超时的时间,默认为3秒,如果网络延迟高可设置大些。
discovery.zen.minimum_master_nodes:
主结点数量的最少值 ,此值的公式为:(master_eligible_nodes / 2) + 1 ,比如:有3个符合要求的主结点,那么这里要设置为2。
node.max_local_storage_nodes:
单机允许的最大存储结点数,通常单机启动一个结点建议设置为1,开发环境如果单机启动多个节点可设置大于1。

jvm.options

设置最小及最大的JVM堆内存大小:

在jvm.options中设置 -Xms和-Xmx:

1) 两个值设置为相等

2) 将Xmx 设置为不超过物理内存的一半。

log4j2.properties

日志文件设置,ES使用log4j,注意日志级别的配置。

4、启动Elasticsearch:bin\elasticsearch.bat,es的特点就是开箱即,无需配置,启动即可。

注意:es7 windows版本不支持机器学习,所以elasticsearch.yml中添加如下几个参数:

From: 元动力
1
2
3
4
5
node.name: node-1  
cluster.initial_master_nodes: ["node-1"]
xpack.ml.enabled: false
http.cors.enabled: true
http.cors.allow-origin: /.*/

5、检查ES是否启动成功:浏览器访问http://localhost:9200/?Prettyopen in new window

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name": "node-1",
"cluster_name": "elasticsearch",
"cluster_uuid": "HqAKQ_0tQOOm8b6qU-2Qug",
"version": {
"number": "7.3.0",
"build_flavor": "default",
"build_type": "zip",
"build_hash": "de777fa",
"build_date": "2019-07-24T18:30:11.767338Z",
"build_snapshot": false,
"lucene_version": "8.1.0",
"minimum_wire_compatibility_version": "6.8.0",
"minimum_index_compatibility_version": "6.0.0-beta1"
},
"tagline": "You Know, for Search"
}

解释:

name: node名称,取自机器的hostname

cluster_name: 集群名称(默认的集群名称就是elasticsearch)

version.number: 7.3.0,es版本号

version.lucene_version:封装的lucene版本号

6、浏览器访问 http://localhost:9200/_cluster/healthopen in new window 查询集群状态

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"cluster_name": "elasticsearch",
"status": "green",
"timed_out": false,
"number_of_nodes": 1,
"number_of_data_nodes": 1,
"active_primary_shards": 0,
"active_shards": 0,
"relocating_shards": 0,
"initializing_shards": 0,
"unassigned_shards": 0,
"delayed_unassigned_shards": 0,
"number_of_pending_tasks": 0,
"number_of_in_flight_fetch": 0,
"task_max_waiting_in_queue_millis": 0,
"active_shards_percent_as_number": 100
}

解释:

Status:集群状态。Green 所有分片可用。Yellow所有主分片可用。Red主分片不可用,集群不可用。

二、 Windows安装Kibana

1、kibana是es数据的前端展现,数据分析时,可以方便地看到数据。作为开发人员,可以方便访问es。

2、下载,解压kibana。

3、启动Kibana:bin\kibana.bat

4、浏览器访问 http://localhost:5601open in new window 进入Dev Tools界面。像plsql一样支持代码提示。

5、发送get请求,查看集群状态GET _cluster/health。相当于浏览器访问。

1568622526251

​ 总览

1568622589010
1568622589010

​ Dev Tools界面

1568622607418
1568622607418

​ 监控集群界面

1568622619503
1568622619503

​ 集群状态(搜索速率、索引速率等)

三、 Windows安装postman

是什么:postman是一个模拟http请求的工具。能够非常细致地定制化各种http请求。如get]\post\pu\delete,携带body参数等。

为什么:在没有kibana时,可以使用postman调试。

怎么用:

get http://localhost:9200/open in new window

1568622671957
1568622671957

测试一下get方式查询集群状态http://localhost:9200/_cluster/healthopen in new window

1568622683707
1568622683707

四、Windows安装head插件

head插件是ES的一个可视化管理插件,用来监视ES的状态,并通过head客户端和ES服务进行交互,比如创建映射、创建索引等,head的项目地址在https://github.com/mobz/elasticsearch-headopen in new window

从ES6.0开始,head插件支持使得node.js运行。

1、安装node.js

2、下载head并运行

From: 元动力
1
2
3
4
git clone git://github.com/mobz/elasticsearch-head.git 
cd elasticsearch-head
npm install
npm run start

浏览器打开 http://localhost:9100/open in new window

3、运行

1568627572838
1568627572838

打开浏览器调试工具发现报错:

Origin null is not allowed by Access-Control-Allow-Origin.

原因是:head插件作为客户端要连接ES服务(localhost:9200),此时存在跨域问题,elasticsearch默认不允许跨域访问。

解决方案:

设置elasticsearch允许跨域访问。

在config/elasticsearch.yml 后面增加以下参数:

From: 元动力
1
2
3
4
#开启cors跨域访问支持,默认为false   
http.cors.enabled: true
#跨域访问允许的域名地址,(允许所有域名)以上使用正则
http.cors.allow-origin: /.*/

注意:将config/elasticsearch.yml另存为utf-8编码格式。

成功连接ES

1568627617789
1568627617789

注意:kibana\postman\head插件选择自己喜欢的一种使用即可。

本教程使用kibana的dev tool,因为地址栏省略了http://localhost:9200open in new window

第五章 es快速入门

一、文档(document)的数据格式

(1)应用系统的数据结构都是面向对象的,具有复杂的数据结构

(2)对象存储到数据库,需要将关联的复杂对象属性插到另一张表,查询时再拼接起来。

(3)es面向文档,文档中存储的数据结构,与对象一致。所以一个对象可以直接存成一个文档。

(4)es的document用json数据格式来表达。

例如:班级和学生关系

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Student {
private String id;
private String name;

private String classInfoId;
}

private class ClassInfo {
private String id;
private String className;
。。。。。

}

数据库中要设计所谓的一对多,多对一的两张表,外键等。查询出来时,还要关联,mybatis写映射文件,很繁琐。

而在es中,一个学生存成文档如下:

From: 元动力
1
2
3
4
5
6
7
8
9
{
"id":"1",
"name": "张三",
"last_name": "zhang",
"classInfo": {
"id": "1",
"className": "三年二班",
}
}

二、图书网站商品管理案例:背景介绍

有一个售卖图书的网站,需要为其基于ES构建一个后台系统,提供以下功能:

(1)对商品信息进行CRUD(增删改查)操作

(2)执行简单的结构化查询

(3)可以执行简单的全文检索,以及复杂的phrase(短语)检索

(4)对于全文检索的结果,可以进行高亮显示

(5)对数据进行简单的聚合分析

三、简单的集群管理

1、快速检查集群的健康状况

es提供了一套api,叫做cat api,可以查看es中各种各样的数据

GET /_cat/health?v

From: 元动力
1
2
epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1568635460 12:04:20 elasticsearch green 1 1 4 4 0 0 0 0 - 100.0%

如何快速了解集群的健康状况?green、yellow、red?

green:每个索引的primary shard和replica shard都是active状态的

yellow:每个索引的primary shard都是active状态的,但是部分replica shard不是active状态,处于不可用的状态

red:不是所有索引的primary shard都是active状态的,部分索引有数据丢失了

2、快速查看集群中有哪些索引

GET /_cat/indices?v

From: 元动力
1
2
3
4
5
health status index                           uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green open .kibana_task_manager JBMgpucOSzenstLcjA_G4A 1 0 2 0 45.5kb 45.5kb
green open .monitoring-kibana-7-2019.09.16 LIskf15DTcS70n4Q6t2bTA 1 0 433 0 218.2kb 218.2kb
green open .monitoring-es-7-2019.09.16 RMeUN3tQRjqM8xBgw7Zong 1 0 3470 1724 1.9mb 1.9mb
green open .kibana_1 1cRiyIdATya5xS6qK5pGJw 1 0 4 0 18.2kb 18.2kb

3、简单的索引操作

创建索引:PUT /demo_index?pretty

From: 元动力
1
2
3
4
5
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "demo_index"
}

删除索引:DELETE /demo_index?pretty

4、商品的CRUD操作(document CRUD操作)

(1)新建图书索引

首先建立图书索引 book

语法:put /index

PUT /book

1568632608676
1568632608676
(2)新增图书 :新增文档

语法:PUT /index/type/id

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
PUT /book/_doc/1

{
"name": "Bootstrap开发",
"description": "Bootstrap是由Twitter推出的一个前台页面开发css框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长css页面开发的程序人员)轻松的实现一个css,不受浏览器限制的精美界面css效果。",
"studymodel": "201002",
"price":38.6,
"timestamp":"2019-08-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "bootstrap", "dev"]
}
From: 元动力
1
2
3
4
5
6
7
8
9
10
PUT /book/_doc/2
{
"name": "java编程思想",
"description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。",
"studymodel": "201001",
"price":68.6,
"timestamp":"2019-08-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "java", "dev"]
}
From: 元动力
1
2
3
4
5
6
7
8
9
10
PUT /book/_doc/3
{
"name": "spring开发基础",
"description": "spring 在java领域非常流行,java程序员都在用。",
"studymodel": "201001",
"price":88.6,
"timestamp":"2019-08-24 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "spring", "java"]
}

结果

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"_index" : "book",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
(3)查询图书:检索文档

语法:GET /index/type/id

查看图书:GET /book/_doc/1 就可看到json形式的文档。方便程序解析。

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
"_index" : "book",
"_type" : "_doc",

"_id" : "1",

"_version" : 4,

"_seq_no" : 5,

"_primary_term" : 1,

"found" : true,

"_source" : {

"name" : "Bootstrap开发",

"description" : "Bootstrap是由Twitter推出的一个前台页面开发css框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长css页面开发的程序人员)轻松的实现一个css,不受浏览器限制的精美界面css效果。",

"studymodel" : "201002",

"price" : 38.6,

"timestamp" : "2019-08-25 19:11:35",

"pic" : "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",

"tags" : [

"bootstrap",

"开发"

]

}

}

为方便查看索引中的数据,kibana可以如下操作

Kibana-discover- Create index pattern- Index pattern填book

1568632860925
1568632860925

下一步,再点击discover就可看到数据。

1568632872820
1568632872820

点击json还可以看到原始数据

1568632881931
1568632881931

为方便查看索引中的数据,head可以如下操作

点击数据浏览,点击book索引。

1568632895254
1568632895254
(4)修改图书:替换操作
From: 元动力
1
2
3
4
5
6
7
8
9
10
PUT /book/_doc/1
{
"name": "Bootstrap开发教程1",
"description": "Bootstrap是由Twitter推出的一个前台页面开发css框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长css页面开发的程序人员)轻松的实现一个css,不受浏览器限制的精美界面css效果。",
"studymodel": "201002",
"price":38.6,
"timestamp":"2019-08-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "bootstrap", "开发"]
}

替换操作是整体覆盖,要带上所有信息。

(5) 修改图书:更新文档

语法:POST /{index}/type /{id}/_update

或者POST /{index}/_update/

From: 元动力
1
2
3
4
5
6
POST /book/_update/1/ 
{
"doc": {
"name": " Bootstrap开发教程高级"
}
}

返回:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"_index" : "book",
"_type" : "_doc",
"_id" : "1",
"_version" : 10,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 11,
"_primary_term" : 1
}
(6)删除图书:删除文档

语法:

From: 元动力
1
DELETE /book/_doc/1

返回:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"_index" : "book",
"_type" : "_doc",
"_id" : "1",
"_version" : 11,
"result" : "deleted",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 12,
"_primary_term" : 1

}

第六章 文档document入门

一、 默认自带字段解析

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"_index" : "book",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 10,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "Bootstrap开发教程1",
"description" : "Bootstrap是由Twitter推出的一个前台页面开发css框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长css页面开发的程序人员)轻松的实现一个css,不受浏览器限制的精美界面css效果。",
"studymodel" : "201002",
"price" : 38.6,
"timestamp" : "2019-08-25 19:11:35",
"pic" : "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags" : [
"bootstrap",
"开发"
]
}
}

1、 _index

  • 含义:此文档属于哪个索引
  • 原则:类似数据放在一个索引中。数据库中表的定义规则。如图书信息放在book索引中,员工信息放在employee索引中。各个索引存储和搜索时互不影响。
  • 定义规则:英文小写。尽量不要使用特殊字符。order user

2、_type

  • 含义:类别。book java node
  • 注意:以后的es9将彻底删除此字段,所以当前版本在不断弱化type。不需要关注。见到_type都为doc。

3、_id

含义:文档的唯一标识。就像表的id主键。结合索引可以标识和定义一个文档。

生成:手动(put /index/_doc/id)、自动

4、创建索引时,不同数据放到不同索引中

二、生成文档id

1、手动生成id

场景:数据从其他系统导入时,本身有唯一主键。如数据库中的图书、员工信息等。

用法:put /index/_doc/id

From: 元动力
1
2
3
4
PUT /test_index/_doc/1
{
"test_field": "test"
}

2、自动生成id

用法:POST /index/_doc

From: 元动力
1
2
3
4
POST /test_index/_doc
{
"test_field": "test1"
}

返回:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "x29LOm0BPsY0gSJFYZAl",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}

自动id特点:

长度为20个字符,URL安全,base64编码,GUID,分布式生成不冲突

三、 _source 字段

1、_source

含义:插入数据时的所有字段和值。在get获取数据时,在_source字段中原样返回。

GET /book/_doc/1

2、 定制返回字段

就像sql不要select *,而要select name,price from book …一样。

GET /book/_doc/1?__source_includes=name,price

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"_index" : "book",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 10,
"_primary_term" : 1,
"found" : true,
"_source" : {
"price" : 38.6,
"name" : "Bootstrap开发教程1"
}
}

四、文档的替换与删除

1、全量替换

执行两次,返回结果中版本号(_version)在不断上升。此过程为全量替换。

From: 元动力
1
2
3
4
PUT /test_index/_doc/1
{
"test_field": "test"
}

实质:旧文档的内容不会立即删除,只是标记为deleted。适当的时机,集群会将这些文档删除。

2、强制创建

为防止覆盖原有数据,我们在新增时,设置为强制创建,不会覆盖原有文档。

语法:PUT /index/ _doc/id/_create

From: 元动力
1
2
3
4
PUT /test_index/_doc/1/_create
{
"test_field": "test"
}

返回

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[2]: version conflict, document already exists (current version [1])",
"index_uuid": "lqzVqxZLQuCnd6LYtZsMkg",
"shard": "0",
"index": "test_index"
}
],
"type": "version_conflict_engine_exception",
"reason": "[2]: version conflict, document already exists (current version [1])",
"index_uuid": "lqzVqxZLQuCnd6LYtZsMkg",
"shard": "0",
"index": "test_index"
},
"status": 409
}

3、删除

DELETE /index/_doc/id

From: 元动力
1
DELETE  /test_index/_doc/1/

实质:旧文档的内容不会立即删除,只是标记为deleted。适当的时机,集群会将这些文档删除。

lazy delete

五、局部替换 partial update

使用 PUT /index/type/id 为文档全量替换,需要将文档所有数据提交。

partial update局部替换则只修改变动字段。

用法:

From: 元动力
1
2
3
4
5
6
post /index/type/id/_update 
{
"doc": {
"field""value"
}
}

图解内部原理

内部与全量替换是一样的,旧文档标记为删除,新建一个文档。

优点:

  • 大大减少网络传输次数和流量,提升性能
  • 减少并发冲突发生的概率。

演示

插入文档

From: 元动力
1
2
3
4
5
PUT /test_index/_doc/5
{
"test_field1": "ydl",
"test_field2": "ydlclass"
}

修改字段1

From: 元动力
1
2
3
4
5
6
POST /test_index/_doc/5/_update
{
"doc": {
"test_field2": " ydlclass 2"
}
}

六、 使用脚本更新

es可以内置脚本执行复杂操作。例如painless脚本。

注意:groovy脚本在es6以后就不支持了。原因是耗内存,不安全远程注入漏洞。

1、内置脚本

需求1:修改文档6的num字段,+1。

插入数据

From: 元动力
1
2
3
4
5
PUT /test_index/_doc/6
{
"num": 0,
"tags": []
}

执行脚本操作

From: 元动力
1
2
3
4
POST /test_index/_doc/6/_update
{
"script" : "ctx._source.num+=1"
}

查询数据

From: 元动力
1
GET /test_index/_doc/6

返回

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "6",
"_version" : 2,
"_seq_no" : 23,
"_primary_term" : 1,
"found" : true,
"_source" : {
"num" : 1,
"tags" : [ ]
}
}

需求2:搜索所有文档,将num字段乘以2输出

插入数据

From: 元动力
1
2
3
4
PUT /test_index/_doc/7
{
"num": 5
}

查询

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET /test_index/_search
{
"script_fields": {
"my_doubled_field": {
"script": {
"lang": "expression",
"source": "doc['num'] * multiplier",
"params": {
"multiplier": 2
}
}
}
}
}

返回

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "7",
"_score" : 1.0,
"fields" : {
"my_doubled_field" : [
10.0
]
}
}

2、外部脚本

Painless是内置支持的。脚本内容可以通过多种途径传给 es,包括 rest 接口,或者放到 config/scripts目录等,默认开启。

注意:脚本性能低下,且容易发生注入,本教程忽略。

官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.htmlopen in new window

七、 图解es的并发问题

如同秒杀,多线程情况下,es同样会出现并发冲突问题。

八、 图解悲观锁与乐观锁机制

为控制并发问题,我们通常采用锁机制。分为悲观锁和乐观锁两种机制。

悲观锁:很悲观,所有情况都上锁。此时只有一个线程可以操作数据。具体例子为数据库中的行级锁、表级锁、读锁、写锁等。

特点:优点是方便,直接加锁,对程序透明。缺点是效率低。

乐观锁:很乐观,对数据本身不加锁。提交数据时,通过一种机制验证是否存在冲突,如es中通过版本号验证。

特点:优点是并发能力高。缺点是操作繁琐,在提交数据时,可能反复重试多次。

九、图解es内部基于_version乐观锁控制

实验基于_version的版本控制

es对于文档的增删改都是基于版本号。

1新增多次文档:

From: 元动力
1
2
3
4
PUT /test_index/_doc/3
{
"test_field": "test"
}

返回版本号递增

2删除此文档

From: 元动力
1
DELETE /test_index/_doc/3

返回

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DELETE /test_index/_doc/3
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "2",
"_version" : 6,
"result" : "deleted",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 7,
"_primary_term" : 1
}

3再新增

From: 元动力
1
2
3
4
PUT /test_index/_doc/3
{
"test_field": "test"
}

可以看到版本号依然递增,验证延迟删除策略。

如果删除一条数据立马删除的话,所有分片和副本都要立马删除,对es集群压力太大。

图解es内部并发控制

es内部主从同步时,是多线程异步。乐观锁机制。

十、演示客户端程序基于_version并发操作流程

java python客户端更新的机制。

1、新建文档

From: 元动力
1
2
3
4
PUT /test_index/_doc/5
{
"test_field": "ydl"
}

返回:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "3",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 8,
"_primary_term" : 1
}

2、客户端1修改。带版本号1。

首先获取数据的当前版本号

From: 元动力
1
GET /test_index/_doc/5

更新文档

From: 元动力
1
2
3
4
5
6
7
8
PUT /test_index/_doc/5?version=1
{
"test_field": "ydl1"
}
PUT /test_index/_doc/5?if_seq_no=21&amp;if_primary_term=1
{
"test_field": "ydl1"
}

3、客户端2并发修改。带版本号1。

From: 元动力
1
2
3
4
5
6
7
8
PUT /test_index/_doc/5?version=1
{
"test_field": "ydl2"
}
PUT /test_index/_doc/5?if_seq_no=21&amp;if_primary_term=1
{
"test_field": "ydl1"
}

报错。

4、客户端2重新查询。得到最新版本为2。seq_no=22

From: 元动力
1
GET /test_index/_doc/4

5、客户端2并发修改。带版本号2。

From: 元动力
1
2
3
4
5
6
7
8
9
PUT /test_index/_doc/4?version=2
{
"test_field": "ydl2"
}
es7
PUT /test_index/_doc/5?if_seq_no=22&amp;if_primary_term=1
{
"test_field": "ydl2"
}

修改成功。

十一、演示自己手动控制版本号 external version

背景:已有数据是在数据库中,有自己手动维护的版本号的情况下,可以使用external version控制。hbase。

要求:修改时external version要大于当前文档的_version

对比:基于_version时,修改的文档version等于当前文档的版本号。

使用?version=1&version_type=external

1、新建文档

From: 元动力
1
2
3
4
PUT /test_index/_doc/4
{
"test_field": "ydl"
}

更新文档:

2、客户端1修改文档

From: 元动力
1
2
3
4
PUT /test_index/_doc/4?version=2&amp;version_type=external
{
"test_field": "ydl1"
}

3、客户端2同时修改

From: 元动力
1
2
3
4
PUT /test_index/_doc/4?version=2&amp;version_type=external
{
"test_field": "ydl2"
}

返回:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[4]: version conflict, current version [2] is higher or equal to the one provided [2]",
"index_uuid": "-rqYZ2EcSPqL6pu8Gi35jw",
"shard": "1",
"index": "test_index"
}
],
"type": "version_conflict_engine_exception",
"reason": "[4]: version conflict, current version [2] is higher or equal to the one provided [2]",
"index_uuid": "-rqYZ2EcSPqL6pu8Gi35jw",
"shard": "1",
"index": "test_index"
},
"status": 409
}

4、客户端2重新查询数据

From: 元动力
1
GET /test_index/_doc/4

5、客户端2重新修改数据

From: 元动力
1
2
3
4
PUT /test_index/_doc/4?version=3&amp;version_type=external
{
"test_field": "ydl2"
}

十二、更新时 retry_on_conflict 参数

1、retry_on_conflict

指定重试次数

From: 元动力
1
2
3
4
5
6
POST /test_index/_doc/5/_update?retry_on_conflict=3
{
"doc": {
"test_field": "ydl1"
}
}

2、与 _version结合使用

From: 元动力
1
2
3
4
5
6
POST /test_index/_doc/5/_update?retry_on_conflict=3&amp;version=22&amp;version_type=external
{
"doc": {
"test_field": "ydl1"
}
}

十三、 批量查询 mget

单条查询 GET /test_index/_doc/1,如果查询多个id的文档一条一条查询,网络开销太大。

1、mget 批量查询:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GET /_mget
{
"docs" : [
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : 1
},
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : 7
}
]
}

返回:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"docs" : [
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "2",
"_version" : 6,
"_seq_no" : 12,
"_primary_term" : 1,
"found" : true,
"_source" : {
"test_field" : "test12333123321321"
}
},
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "3",
"_version" : 6,
"_seq_no" : 18,
"_primary_term" : 1,
"found" : true,
"_source" : {
"test_field" : "test3213"
}
}
]
}

提示去掉type

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
GET /_mget
{
"docs" : [
{
"_index" : "test_index",
"_id" : 2
},
{
"_index" : "test_index",
"_id" : 3
}
]
}

同一索引下批量查询:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
GET /test_index/_mget
{
"docs" : [
{
"_id" : 2
},
{
"_id" : 3
}
]
}

第三种写法:搜索写法

From: 元动力
1
2
3
4
5
6
7
8
post /test_index/_doc/_search
{
"query": {
"ids" : {
"values" : ["1", "7"]
}
}
}

十四、批量增删改 bulk

Bulk 操作解释将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数。

语法:

From: 元动力
1
2
3
POST /_bulk
{"action": {"metadata"}}
{"data"}

如下操作,删除5,新增14,修改2。

From: 元动力
1
2
3
4
5
6
POST /_bulk
{ "delete": { "_index": "test_index", "_id": "5" }}
{ "create": { "_index": "test_index", "_id": "14" }}
{ "test_field": "test14" }
{ "update": { "_index": "test_index", "_id": "2"} }
{ "doc" : {"test_field" : "bulk test"} }

总结:

1功能:

  • delete:删除一个文档,只要1个json串就可以了
  • create:相当于强制创建 PUT /index/type/id/_create
  • index:普通的put操作,可以是创建文档,也可以是全量替换文档
  • update:执行的是局部更新partial update操作

2格式:每个json不能换行。相邻json必须换行。

3隔离:每个操作互不影响。操作失败的行会返回其失败信息。

4实际用法:bulk请求一次不要太大,否则一下积压到内存中,性能会下降。所以,一次请求几千个操作、大小在几M正好。

十五、 文档概念学习总结

章节回顾

1文档的增删改查

2文档字段解析

3内部锁机制

4批量查询修改

es是什么

一个分布式的文档数据存储系统distributed document store。es看做一个分布式nosql数据库。如redis\mongoDB\hbase。

文档数据:es可以存储和操作json文档类型的数据,而且这也是es的核心数据结构。 存储系统:es可以对json文档类型的数据进行存储,查询,创建,更新,删除,等等操作。

应用场景

  • 大数据。es的分布式特点,水平扩容承载大数据。
  • 数据结构灵活。列随时变化。使用关系型数据库将会建立大量的关联表,增加系统复杂度。
  • 数据操作简单。就是查询,不涉及事务。

举例

电商页面、传统论坛页面等。面向的对象比较复杂,但是作为终端,没有太复杂的功能(事务),只涉及简单的增删改查crud。

这个时候选用ES这种NoSQL型的数据存储,比传统的复杂的事务强大的关系型数据库,更加合适一些。无论是性能,还是吞吐量,可能都会更好。

第七章 Java api 实现文档管理

一、es技术特点

1es技术比较特殊,不像其他分布式、大数据课程,haddop、spark、hbase。es代码层面很好写,难的是概念的理解。

2es最重要的是他的rest api。跨语言的。在真实生产中,探查数据、分析数据,使用rest更方便。

3本课程将会大量讲解内部原理及rest api。java代码会在重要的api后学习。

二、 java 客户端简单获取数据

java api 文档 https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.3/java-rest-overview.htmlopen in new window

low : 偏向底层。

high:高级封装。足够。

1导包

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
&lt;dependency&gt;
&lt;groupId&gt;org.elasticsearch.client&lt;/groupId&gt;
&lt;artifactId&gt;elasticsearch-rest-high-level-client&lt;/artifactId&gt;
&lt;version&gt;7.3.0&lt;/version&gt;
&lt;exclusions&gt;
&lt;exclusion&gt;
&lt;groupId&gt;org.elasticsearch&lt;/groupId&gt;
&lt;artifactId&gt;elasticsearch&lt;/artifactId&gt;
&lt;/exclusion&gt;
&lt;/exclusions&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.elasticsearch&lt;/groupId&gt;
&lt;artifactId&gt;elasticsearch&lt;/artifactId&gt;
&lt;version&gt;7.3.0&lt;/version&gt;
&lt;/dependency&gt;

2代码

步骤

​ 1 获取连接客户端

​ 2构建请求

​ 3执行

​ 4获取结果

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//获取连接客户端
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http")));
//构建请求
GetRequest getRequest = new GetRequest("book", "1");
// 执行
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
// 获取结果
if (getResponse.isExists()) {
long version = getResponse.getVersion();
String sourceAsString = getResponse.getSourceAsString();//检索文档(String形式)
System.out.println(sourceAsString);
}

三、结合spring-boot-test测试文档查询

0为什么使用spring boot test

  • ​ 当今趋势
  • ​ 方便开发
  • ​ 创建连接交由spring容器,避免每次请求的网络开销。

1导包

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter&lt;/artifactId&gt;
&lt;version&gt;2.0.6.RELEASE&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;version&gt;2.0.6.RELEASE&lt;/version&gt;
&lt;/dependency&gt;

2配置 application.yml

From: 元动力
1
2
3
4
5
6
spring:
application:
name: service-search
heima:
elasticsearch:
hostlist: 127.0.0.1:9200 #多个结点中间用逗号分隔

3代码

主类

配置类

测试类

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@SpringBootTest
@RunWith(SpringRunner.class)
//查询文档
@Test
public void testGet() throws IOException {
//构建请求
GetRequest getRequest = new GetRequest("test_post", "1");

//========================可选参数 start======================
//为特定字段配置_source_include
// String[] includes = new String[]{"user", "message"};
// String[] excludes = Strings.EMPTY_ARRAY;
// FetchSourceContext fetchSourceContext = new FetchSourceContext(true, includes, excludes);
// getRequest.fetchSourceContext(fetchSourceContext);

//为特定字段配置_source_excludes
// String[] includes1 = new String[]{"user", "message"};
// String[] excludes1 = Strings.EMPTY_ARRAY;
// FetchSourceContext fetchSourceContext1 = new FetchSourceContext(true, includes1, excludes1);
// getRequest.fetchSourceContext(fetchSourceContext1);

//设置路由
// getRequest.routing("routing");

// ========================可选参数 end=====================


//查询 同步查询
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);

//异步查询
// ActionListener&lt;GetResponse&gt; listener = new ActionListener&lt;GetResponse&gt;() {
// //查询成功时的立马执行的方法
// @Override
// public void onResponse(GetResponse getResponse) {
// long version = getResponse.getVersion();
// String sourceAsString = getResponse.getSourceAsString();//检索文档(String形式)
// System.out.println(sourceAsString);
// }
//
// //查询失败时的立马执行的方法
// @Override
// public void onFailure(Exception e) {
// e.printStackTrace();
// }
// };
// //执行异步请求
// client.getAsync(getRequest, RequestOptions.DEFAULT, listener);
// try {
// Thread.sleep(5000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }

// 获取结果
if (getResponse.isExists()) {
long version = getResponse.getVersion();

String sourceAsString = getResponse.getSourceAsString();//检索文档(String形式)
System.out.println(sourceAsString);
byte[] sourceAsBytes = getResponse.getSourceAsBytes();//以字节接受
Map&lt;String, Object&gt; sourceAsMap = getResponse.getSourceAsMap();
System.out.println(sourceAsMap);

}else {

}

}

四、结合spring-boot-test测试文档新增

rest api

From: 元动力
1
2
3
4
5
6
PUT test_post/_doc/2
{
"user":"tomas",
"postDate":"2019-07-18",
"message":"trying out es1"
}

代码:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
@Test
public void testAdd() throws IOException {
// 1构建请求
IndexRequest request=new IndexRequest("test_posts");
request.id("3");
// =======================构建文档============================
// 构建方法1
String jsonString="{\n" +
" \"user\":\"tomas J\",\n" +
" \"postDate\":\"2019-07-18\",\n" +
" \"message\":\"trying out es3\"\n" +
"}";
request.source(jsonString, XContentType.JSON);

// 构建方法2
// Map&lt;String,Object&gt; jsonMap=new HashMap&lt;&gt;();
// jsonMap.put("user", "tomas");
// jsonMap.put("postDate", "2019-07-18");
// jsonMap.put("message", "trying out es2");
// request.source(jsonMap);

// 构建方法3
// XContentBuilder builder= XContentFactory.jsonBuilder();
// builder.startObject();
// {
// builder.field("user", "tomas");
// builder.timeField("postDate", new Date());
// builder.field("message", "trying out es2");
// }
// builder.endObject();
// request.source(builder);
// 构建方法4
// request.source("user","tomas",
// "postDate",new Date(),
// "message","trying out es2");
//
// ========================可选参数===================================
//设置超时时间
request.timeout(TimeValue.timeValueSeconds(1));
request.timeout("1s");

//自己维护版本号
// request.version(2);
// request.versionType(VersionType.EXTERNAL);



// 2执行
//同步
IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
//异步
// ActionListener&lt;IndexResponse&gt; listener=new ActionListener&lt;IndexResponse&gt;() {
// @Override
// public void onResponse(IndexResponse indexResponse) {
//
// }
//
// @Override
// public void onFailure(Exception e) {
//
// }
// };
// client.indexAsync(request,RequestOptions.DEFAULT, listener );
// try {
// Thread.sleep(5000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }


// 3获取结果
String index = indexResponse.getIndex();
String id = indexResponse.getId();
//获取插入的类型
if(indexResponse.getResult()== DocWriteResponse.Result.CREATED){
DocWriteResponse.Result result=indexResponse.getResult();
System.out.println("CREATED:"+result);
}else if(indexResponse.getResult()== DocWriteResponse.Result.UPDATED){
DocWriteResponse.Result result=indexResponse.getResult();
System.out.println("UPDATED:"+result);
}

ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
if(shardInfo.getTotal()!=shardInfo.getSuccessful()){
System.out.println("处理成功的分片数少于总分片!");
}
if(shardInfo.getFailed()&gt;0){
for (ReplicationResponse.ShardInfo.Failure failure:shardInfo.getFailures()) {
String reason = failure.reason();//处理潜在的失败原因
System.out.println(reason);
}
}
}

五、结合spring-boot-test测试文档修改

rest api

From: 元动力
1
2
3
4
5
6
post /test_posts/_doc/3/_update 
{
"doc": {
"user":"tomas J"
}
}

代码:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  @Test
public void testUpdate() throws IOException {
// 1构建请求
UpdateRequest request = new UpdateRequest("test_posts", "3");
Map&lt;String, Object&gt; jsonMap = new HashMap&lt;&gt;();
jsonMap.put("user", "tomas JJ");
request.doc(jsonMap);
//===============================可选参数==========================================
request.timeout("1s");//超时时间

//重试次数
request.retryOnConflict(3);

//设置在继续更新之前,必须激活的分片数
// request.waitForActiveShards(2);
//所有分片都是active状态,才更新
// request.waitForActiveShards(ActiveShardCount.ALL);

// 2执行
// 同步
UpdateResponse updateResponse = client.update(request, RequestOptions.DEFAULT);
// 异步

// 3获取数据
updateResponse.getId();
updateResponse.getIndex();

//判断结果
if (updateResponse.getResult() == DocWriteResponse.Result.CREATED) {
DocWriteResponse.Result result = updateResponse.getResult();
System.out.println("CREATED:" + result);
} else if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) {
DocWriteResponse.Result result = updateResponse.getResult();
System.out.println("UPDATED:" + result);
}else if(updateResponse.getResult() == DocWriteResponse.Result.DELETED){
DocWriteResponse.Result result = updateResponse.getResult();
System.out.println("DELETED:" + result);
}else if (updateResponse.getResult() == DocWriteResponse.Result.NOOP){
//没有操作
DocWriteResponse.Result result = updateResponse.getResult();
System.out.println("NOOP:" + result);
}
}

六、结合spring-boot-test测试文档删除

rest api

From: 元动力
1
DELETE /test_posts/_doc/3

代码

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 @Test
public void testDelete() throws IOException {
// 1构建请求
DeleteRequest request =new DeleteRequest("test_posts","3");
//可选参数
// 2执行
DeleteResponse deleteResponse = client.delete(request, RequestOptions.DEFAULT);


// 3获取数据
deleteResponse.getId();
deleteResponse.getIndex();

DocWriteResponse.Result result = deleteResponse.getResult();
System.out.println(result);
}

七、结合spring-boot-test测试文档bulk

rest api

From: 元动力
1
2
3
POST /_bulk
{"action": {"metadata"}}
{"data"}

代码

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Test
public void testBulk() throws IOException {
// 1创建请求
BulkRequest request = new BulkRequest();
// request.add(new IndexRequest("post").id("1").source(XContentType.JSON, "field", "1"));
// request.add(new IndexRequest("post").id("2").source(XContentType.JSON, "field", "2"));

request.add(new UpdateRequest("post","2").doc(XContentType.JSON, "field", "3"));
request.add(new DeleteRequest("post").id("1"));

// 2执行
BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);

for (BulkItemResponse itemResponse : bulkResponse) {
DocWriteResponse itemResponseResponse = itemResponse.getResponse();

switch (itemResponse.getOpType()) {
case INDEX:
case CREATE:
IndexResponse indexResponse = (IndexResponse) itemResponseResponse;
indexResponse.getId();
System.out.println(indexResponse.getResult());
break;
case UPDATE:
UpdateResponse updateResponse = (UpdateResponse) itemResponseResponse;
updateResponse.getIndex();
System.out.println(updateResponse.getResult());
break;
case DELETE:
DeleteResponse deleteResponse = (DeleteResponse) itemResponseResponse;
System.out.println(deleteResponse.getResult());
break;
}
}
}

第八章 图解es内部机制

一、图解es分布式基础

1、es对复杂分布式机制的透明隐藏特性

  • 分布式机制:分布式数据存储及共享。
  • 分片机制:数据存储到哪个分片,副本数据写入。
  • 集群发现机制:cluster discovery。新启动es实例,自动加入集群。
  • shard负载均衡:大量数据写入及查询,es会将数据平均分配。
  • shard副本:新增副本数,分片重分配。

2、Elasticsearch的垂直扩容与水平扩容

垂直扩容:使用更加强大的服务器替代老服务器。但单机存储及运算能力有上线。且成本直线上升。如10t服务器1万。单个10T服务器可能20万。

水平扩容:采购更多服务器,加入集群。大数据。

3、增减或减少节点时的数据rebalance

新增或减少es实例时,es集群会将数据重新分配。

4、master节点

功能:

  • 创建删除节点
  • 创建删除索引

5、节点对等的分布式架构

  • 节点对等,每个节点都能接收所有的请求
  • 自动请求路由
  • 响应收集

二、图解分片shard、副本replica机制

1、shard&replica机制

(1)每个index包含一个或多个shard

(2)每个shard都是一个最小工作单元,承载部分数据,lucene实例,完整的建立索引和处理请求的能力

(3)增减节点时,shard会自动在nodes中负载均衡

(4)primary shard和replica shard,每个document肯定只存在于某一个primary shard以及其对应的replica shard中,不可能存在于多个primary shard

(5)replica shard是primary shard的副本,负责容错,以及承担读请求负载

(6)primary shard的数量在创建索引的时候就固定了,replica shard的数量可以随时修改

(7)primary shard的默认数量是1,replica默认是1,默认共有2个shard,1个primary shard,1个replica shard

注意:es7以前primary shard的默认数量是5,replica默认是1,默认有10个shard,5个primary shard,5个replica shard

(8)primary shard不能和自己的replica shard放在同一个节点上(否则节点宕机,primary shard和副本都丢失,起不到容错的作用),但是可以和其他primary shard的replica shard放在同一个节点上

2、图解单node环境下创建index是什么样子的

(1)单node环境下,创建一个index,有3个primary shard,3个replica shard (2)集群status是yellow (3)这个时候,只会将3个primary shard分配到仅有的一个node上去,另外3个replica shard是无法分配的 (4)集群可以正常工作,但是一旦出现节点宕机,数据全部丢失,而且集群不可用,无法承接任何请求

From: 元动力
1
2
3
4
5
6
7
PUT /test_index1
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}

3、图解2个node环境下replica shard是如何分配的

(1)replica shard分配:3个primary shard,3个replica shard,1 node (2)primary ---> replica同步 (3)读请求:primary/replica

4、图解横向扩容

  • 分片自动负载均衡,分片向空闲机器转移。
  • 每个节点存储更少分片,系统资源给与每个分片的资源更多,整体集群性能提高。
  • 扩容极限:节点数大于整体分片数,则必有空闲机器。
  • 超出扩容极限时,可以增加副本数,如设置副本数为2,总共3*3=9个分片。9台机器同时运行,存储和搜索性能更强。容错性更好。
  • 容错性:只要一个索引的所有主分片在,集群就就可以运行。

5、 图解es容错机制 master选举,replica容错,数据恢复

以3分片,2副本数,3节点为例介绍。

  • master node宕机,自动master选举,集群为red
  • replica容错:新master将replica提升为primary shard,yellow
  • 重启宕机node,master copy replica到该node,使用原有的shard并同步宕机后的修改,green

第九章 图解文档存储机制

一、数据路由

1、文档存储如何路由到相应分片

一个文档,最终会落在主分片的一个分片上,到底应该在哪一个分片?这就是数据路由。

2、路由算法

From: 元动力
1
shard = hash(routing) % number_of_primary_shards

哈希值对主分片数取模。

举例:

对一个文档经行crud时,都会带一个路由值 routing number。默认为文档_id(可能是手动指定,也可能是自动生成)。

存储1号文档,经过哈希计算,哈希值为2,此索引有3个主分片,那么计算2%3=2,就算出此文档在P2分片上。

决定一个document在哪个shard上,最重要的一个值就是routing值,默认是_id,也可以手动指定,相同的routing值,每次过来,从hash函数中,产出的hash值一定是相同的

无论hash值是几,无论是什么数字,对number_of_primary_shards求余数,结果一定是在0~number_of_primary_shards-1之间这个范围内的。0,1,2。

3、手动指定 routing number

From: 元动力
1
2
3
4
5
PUT /test_index/_doc/15?routing=num
{
"num": 0,
"tags": []
}

场景:在程序中,架构师可以手动指定已有数据的一个属性为路由值,好处是可以定制一类文档数据存储到一个分片中。缺点是设计不好,会造成数据倾斜。

所以,不同文档尽量放到不同的索引中。剩下的事情交给es集群自己处理。

4、主分片数量不可变

涉及到以往数据的查询搜索,所以一旦建立索引,主分片数不可变。

二、 图解文档的增删改内部机制

增删改可以看做update,都是对数据的改动。一个改动请求发送到es集群,经历以下四个步骤:

(1)客户端选择一个node发送请求过去,这个node就是coordinating node(协调节点)

(2)coordinating node,对document进行路由,将请求转发给对应的node(有primary shard)

(3)实际的node上的primary shard处理请求,然后将数据同步到replica node。

(4)coordinating node,如果发现primary node和所有replica node都搞定之后,就返回响应结果给客户端。

三、图解文档的查询内部机制

1、客户端发送请求到任意一个node,成为coordinate node

2、coordinate node对document进行路由,将请求转发到对应的node,此时会使用round-robin随机轮询算法,在primary shard以及其所有replica中随机选择一个,让读请求负载均衡

3、接收请求的node返回document给coordinate node

4、coordinate node返回document给客户端

5、特殊情况:document如果还在建立索引过程中,可能只有primary shard有,任何一个replica shard都没有,此时可能会导致无法读取到document,但是document完成索引建立之后,primary shard和replica shard就都有了。

四、bulk api奇特的json格式

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
POST /_bulk
{"action": {"meta"}}\n
{"data"}\n
{"action": {"meta"}}\n
{"data"}\n

[
{
"action":{
"method":"create"
},
"data":{
"id":1,
"field1":"java",
"field1":"spring",
}
},
{
"action":{
"method":"create"
},
"data":{
"id":2,
"field1":"java",
"field1":"spring",
}
}
]

1、bulk中的每个操作都可能要转发到不同的node的shard去执行

2、如果采用比较良好的json数组格式

允许任意的换行,整个可读性非常棒,读起来很爽,es拿到那种标准格式的json串以后,要按照下述流程去进行处理

(1)将json数组解析为JSONArray对象,这个时候,整个数据,就会在内存中出现一份一模一样的拷贝,一份数据是json文本,一份数据是JSONArray对象

(2)解析json数组里的每个json,对每个请求中的document进行路由

(3)为路由到同一个shard上的多个请求,创建一个请求数组。100请求中有10个是到P1.

(4)将这个请求数组序列化

(5)将序列化后的请求数组发送到对应的节点上去

3、耗费更多内存,更多的jvm gc开销

我们之前提到过bulk size最佳大小的那个问题,一般建议说在几千条那样,然后大小在10MB左右,所以说,可怕的事情来了。假设说现在100个bulk请求发送到了一个节点上去,然后每个请求是10MB,100个请求,就是1000MB = 1GB,然后每个请求的json都copy一份为jsonarray对象,此时内存中的占用就会翻倍,就会占用2GB的内存,甚至还不止。因为弄成jsonarray之后,还可能会多搞一些其他的数据结构,2GB+的内存占用。

占用更多的内存可能就会积压其他请求的内存使用量,比如说最重要的搜索请求,分析请求,等等,此时就可能会导致其他请求的性能急速下降。

另外的话,占用内存更多,就会导致java虚拟机的垃圾回收次数更多,跟频繁,每次要回收的垃圾对象更多,耗费的时间更多,导致es的java虚拟机停止工作线程的时间更多。

4、现在的奇特格式

From: 元动力
1
2
3
4
5
6
POST /_bulk
{ "delete": { "_index": "test_index", "_id": "5" }} \n
{ "create": { "_index": "test_index", "_id": "14" }}\n
{ "test_field": "test14" }\n
{ "update": { "_index": "test_index", "_id": "2"} }\n
{ "doc" : {"test_field" : "bulk test"} }\n

(1)不用将其转换为json对象,不会出现内存中的相同数据的拷贝,直接按照换行符切割json

(2)对每两个一组的json,读取meta,进行document路由

(3)直接将对应的json发送到node上去

5、最大的优势在于,不需要将json数组解析为一个JSONArray对象,形成一份大数据的拷贝,浪费内存空间,尽可能地保证性能。

第十章 Mapping映射入门

一、 什么是mapping映射

概念:自动或手动为index中的_doc建立的一种数据结构和相关配置,简称为mapping映射。

插入几条数据,让es自动为我们建立一个索引

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
PUT /website/_doc/1
{
"post_date": "2019-01-01",
"title": "my first article",
"content": "this is my first article in this website",
"author_id": 11400
}

PUT /website/_doc/2
{
"post_date": "2019-01-02",
"title": "my second article",
"content": "this is my second article in this website",
"author_id": 11400
}

PUT /website/_doc/3
{
"post_date": "2019-01-03",
"title": "my third article",
"content": "this is my third article in this website",
"author_id": 11400
}

对比数据库建表语句

From: 元动力
1
2
3
4
5
6
create table website(
post_date date,
title varchar(50),
content varchar(100),
author_id int(11)
);

动态映射:dynamic mapping,自动为我们建立index,以及对应的mapping,mapping中包含了每个field对应的数据类型,以及如何分词等设置。

重点:我们当然,后面会讲解,也可以手动在创建数据之前,先创建index,以及对应的mapping

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
GET  /website/_mapping/
{
"website" : {
"mappings" : {
"properties" : {
"author_id" : {
"type" : "long"
},
"content" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"post_date" : {
"type" : "date"
},
"title" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}

尝试各种搜索

From: 元动力
1
2
3
4
GET /website/_search?q=2019        0条结果             
GET /website/_search?q=2019-01-01 1条结果
GET /website/_search?q=post_date:2019-01-01 1条结果
GET /website/_search?q=post_date:2019 0 条结果

搜索结果为什么不一致,因为es自动建立mapping的时候,设置了不同的field不同的data type。不同的data type的分词、搜索等行为是不一样的。所以出现了_all field和post_date field的搜索表现完全不一样。

二、精确匹配与全文搜索的对比分析

1、exact value 精确匹配

2019-01-01,exact value,搜索的时候,必须输入2019-01-01,才能搜索出来

如果你输入一个01,是搜索不出来的

select * from book where name= 'java'

2、full text 全文检索

搜“笔记电脑”,笔记本电脑词条会不会出现。

select * from book where name like '%java%'

(1)缩写 vs. 全称:cn vs. china

(2)格式转化:like liked likes

(3)大小写:Tom vs tom

(4)同义词:like vs love

2019-01-01,2019 01 01,搜索2019,或者01,都可以搜索出来

china,搜索cn,也可以将china搜索出来

likes,搜索like,也可以将likes搜索出来

Tom,搜索tom,也可以将Tom搜索出来

like,搜索love,同义词,也可以将like搜索出来

就不是说单纯的只是匹配完整的一个值,而是可以对值进行拆分词语后(分词)进行匹配,也可以通过缩写、时态、大小写、同义词等进行匹配。深入 NPL,自然语义处理。

三、全文检索下倒排索引核心原理快速揭秘

doc1:I really liked my small dogs, and I think my mom also liked them.

doc2:He never liked any dogs, so I hope that my mom will not expect me to liked him.

分词,初步的倒排索引的建立

termdoc1doc2
I**
really*
liked**
my**
small*
dogs*
and*
think*
mom**
also*
them*
He*
never*
any*
so*
hope*
that*
will*
not*
expect*
me*
to*
him*

演示了一下倒排索引最简单的建立的一个过程

搜索

mother like little dog,不可能有任何结果

mother

like

little

dog

这不是我们想要的结果。同义词mom\mother在我们人类看来是一样。想进行标准化操作。

重建倒排索引

normalization正规化,建立倒排索引的时候,会执行一个操作,也就是说对拆分出的各个单词进行相应的处理,以提升后面搜索的时候能够搜索到相关联的文档的概率

时态的转换,单复数的转换,同义词的转换,大小写的转换

mom ―> mother

liked ―> like

small ―> little

dogs ―> dog

重新建立倒排索引,加入normalization,再次用mother liked little dog搜索,就可以搜索到了

worddoc1doc2normalization
I**
really*
like**liked ―> like
my**
little*small ―> little
dog*dogs ―> dog
and*
think*
mother**mom ―> mother
also*
them*
He*
never*
any*
so*
hope*
that*
will*
not*
expect*
me*
to*
him*

重新搜索

搜索:mother liked little dog,

对搜索条件经行分词 normalization

mother

liked -》like

little

dog

doc1和doc2都会搜索出来

四、分词器 analyzer

1、什么是分词器 analyzer

作用:切分词语,normalization(提升recall召回率)

给你一段句子,然后将这段句子拆分成一个一个的单个的单词,同时对每个单词进行normalization(时态转换,单复数转换)

recall,召回率:搜索的时候,增加能够搜索到的结果的数量

analyzer 组成部分:

1、character filter:在一段文本进行分词之前,先进行预处理,比如说最常见的就是,过滤html标签(<span>hello<span> --> hello),& --> and(I&you --> I and you)

2、tokenizer:分词,hello you and me --> hello, you, and, me

3、token filter:lowercase,stop word,synonymom,dogs --> dog,liked --> like,Tom --> tom,a/the/an --> 干掉,mother --> mom,small --> little

stop word 停用词: 了 的 呢。

一个分词器,很重要,将一段文本进行各种处理,最后处理好的结果才会拿去建立倒排索引。

2、内置分词器的介绍

例句:Set the shape to semi-transparent by calling set_trans(5)

standard analyzer标准分词器:set, the, shape, to, semi, transparent, by, calling, set_trans, 5(默认的是standard)

simple analyzer简单分词器:set, the, shape, to, semi, transparent, by, calling, set, trans

whitespace analyzer:Set, the, shape, to, semi-transparent, by, calling, set_trans(5)

language analyzer(特定的语言的分词器,比如说,english,英语分词器):set, shape, semi, transpar, call, set_tran, 5

官方文档:

https://www.elastic.co/guide/en/elasticsearch/reference/7.4/analysis-analyzers.htmlopen in new window

1568978200919
1568978200919

五、query string根据字段分词策略

1、query string分词

query string必须以和index建立时相同的analyzer进行分词

query string对exact value和full text的区别对待

如: date:exact value 精确匹配

​ text: full text 全文检索

2、测试分词器

From: 元动力
1
2
3
4
5
GET /_analyze
{
"analyzer": "standard",
"text": "Text to analyze 80"
}

返回值:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
"tokens" : [
{
"token" : "text",
"start_offset" : 0,
"end_offset" : 4,
"type" : "&lt;ALPHANUM&gt;",
"position" : 0
},
{
"token" : "to",
"start_offset" : 5,
"end_offset" : 7,
"type" : "&lt;ALPHANUM&gt;",
"position" : 1
},
{
"token" : "analyze",
"start_offset" : 8,
"end_offset" : 15,
"type" : "&lt;ALPHANUM&gt;",
"position" : 2
},
{
"token" : "80",
"start_offset" : 16,
"end_offset" : 18,
"type" : "&lt;NUM&gt;",
"position" : 3
}
]
}


token 实际存储的term 关键字

position 在此词条在原文本中的位置

start_offset/end_offset字符在原始字符串中的位置

六、mapping回顾总结

(1)往es里面直接插入数据,es会自动建立索引,同时建立对应的mapping。(dynamic mapping)

(2)mapping中就自动定义了每个field的数据类型

(3)不同的数据类型(比如说text和date),可能有的是exact value,有的是full text

(4)exact value,在建立倒排索引的时候,分词的时候,是将整个值一起作为一个关键词建立到倒排索引中的;full text,会经历各种各样的处理,分词,normaliztion(时态转换,同义词转换,大小写转换),才会建立到倒排索引中。

(5)同时呢,exact value和full text类型的field就决定了,在一个搜索过来的时候,对exact value field或者是full text field进行搜索的行为也是不一样的,会跟建立倒排索引的行为保持一致;比如说exact value搜索的时候,就是直接按照整个值进行匹配,full text query string,也会进行分词和normalization再去倒排索引中去搜索

(6)可以用es的dynamic mapping,让其自动建立mapping,包括自动设置数据类型;也可以提前手动创建index和tmapping,自己对各个field进行设置,包括数据类型,包括索引行为,包括分词器,等。

七、mapping的核心数据类型以及dynamic mapping

1、核心的数据类型

string :text and keyword

byte,short,integer,long,float,double

boolean

date

详见:https://www.elastic.co/guide/en/elasticsearch/reference/7.3/mapping-types.htmlopen in new window

下图是ES7.3核心的字段类型如下:

1568989192034
1568989192034

2、dynamic mapping 推测规则

true or false --> boolean

123 --> long

123.45 --> double

2019-01-01 --> date

"hello world" --> text/keywod

3、查看mapping

GET /index/_mapping/

八、手动管理mapping

1、查询所有索引的映射

GET /_mapping

2、创建映射 !!

创建索引后,应该立即手动创建映射

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PUT book/_mapping
{
"properties": {
"name": {
"type": "text"
},
"description": {
"type": "text",
"analyzer":"english",
"search_analyzer":"english"
},
"pic":{
"type":"text",
"index":false
},
"studymodel":{
"type":"text"
}
}
}

Text 文本类型

1)analyzer

通过analyzer属性指定分词器。

上边指定了analyzer是指在索引和搜索都使用english,如果单独想定义搜索时使用的分词器则可以通过search_analyzer属性。

2)index

index属性指定是否索引。

默认为index=true,即要进行索引,只有进行索引才可以从索引库搜索到。

但是也有一些内容不需要索引,比如:商品图片地址只被用来展示图片,不进行搜索图片,此时可以将index设置为false。

删除索引,重新创建映射,将pic的index设置为false,尝试根据pic去搜索,结果搜索不到数据。

3)store

是否在source之外存储,每个文档索引后会在 ES中保存一份原始文档,存放在"_source"中,一般情况下不需要设置store为true,因为在_source中已经有一份原始文档了。

测试

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PUT book/_mapping
{
"properties": {
"name": {
"type": "text"
},
"description": {
"type": "text",
"analyzer":"english",
"search_analyzer":"english"
},
"pic":{
"type":"text",
"index":false
},
"studymodel":{
"type":"text"
}
}
}

插入文档:

From: 元动力
1
2
3
4
5
6
7
PUT /book/_doc/1
{
"name":"Bootstrap开发框架",
"description":"Bootstrap是由Twitter推出的一个前台页面开发框架,在行业之中使用较为广泛。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
"pic":"group1/M00/00/01/wKhlQFqO4MmAOP53AAAcwDwm6SU490.jpg",
"studymodel":"201002"
}

Get /book/_search?q=name:开发

Get /book/_search?q=description:开发

Get /book/_search?q=pic:group1/M00/00/01/wKhlQFqO4MmAOP53AAAcwDwm6SU490.jpg

Get /book/_search?q=studymodel:201002

通过测试发现:name和description都支持全文检索,pic不可作为查询条件。

keyword关键字字段

目前已经取代了"index": false。上边介绍的text文本字段在映射时要设置分词器,keyword字段为关键字字段,通常搜索keyword是按照整体搜索,所以创建keyword字段的索引时是不进行分词的,比如:邮政编码、手机号码、身份证等。keyword字段通常用于过虑、排序、聚合等。

date日期类型

日期类型不用设置分词器。

通常日期类型的字段用于排序。

format

通过format设置日期格式

例子:

下边的设置允许date字段存储年月日时分秒、年月日及毫秒三种格式。

{

​ "properties": {

​ "timestamp": {

​ "type": "date",

​ "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd"

​ }

​ }

}

插入文档:

Post book/doc/3

{

"name": "spring开发基础",

"description": "spring 在java领域非常流行,java程序员都在用。",

"studymodel": "201001",

"pic":"group1/M00/00/01/wKhlQFqO4MmAOP53AAAcwDwm6SU490.jpg",

"timestamp":"2018-07-04 18:28:58"

}

数值类型

下边是ES支持的数值类型

1568990520717
1568990520717

1、尽量选择范围小的类型,提高搜索效率

2、对于浮点数尽量用比例因子,比如一个价格字段,单位为元,我们将比例因子设置为100这在ES中会按 分 存储,映射如下:

From: 元动力
1
2
3
4
"price": {
"type": "scaled_float",
"scaling_factor": 100
},

由于比例因子为100,如果我们输入的价格是23.45则ES中会将23.45乘以100存储在ES中。

如果输入的价格是23.456,ES会将23.456乘以100再取一个接近原始值的数,得出2346。

使用比例因子的好处是整型比浮点型更易压缩,节省磁盘空间。

如果比例因子不适合,则从下表选择范围小的去用:

更新已有映射,并插入文档:

From: 元动力
1
2
3
4
5
6
7
8
9
PUT book/doc/3
{
"name": "spring开发基础",
"description": "spring 在java领域非常流行,java程序员都在用。",
"studymodel": "201001",
"pic":"group1/M00/00/01/wKhlQFqO4MmAOP53AAAcwDwm6SU490.jpg",
"timestamp":"2018-07-04 18:28:58",
"price":38.6
}

3、修改映射

只能创建index时手动建立mapping,或者新增field mapping,但是不能update field mapping。

因为已有数据按照映射早已分词存储好。如果修改,那这些存量数据怎么办。

新增一个字段mapping

From: 元动力
1
2
3
4
5
6
7
8
9
PUT /book/_mapping/
{
"properties" : {
"new_field" : {
"type" : "text",
"index": "false"
}
}
}

如果修改mapping,会报错

From: 元动力
1
2
3
4
5
6
7
8
PUT /book/_mapping/
{
"properties" : {
"studymodel" : {
"type" : "keyword"
}
}
}

返回:

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"error": {
"root_cause": [
{
"type": "illegal_argument_exception",
"reason": "mapper [studymodel] of different type, current_type [text], merged_type [keyword]"
}
],
"type": "illegal_argument_exception",
"reason": "mapper [studymodel] of different type, current_type [text], merged_type [keyword]"
},
"status": 400
}

4、删除映射

通过删除索引来删除映射。

九、 复杂数据类型

1、multivalue field

{ "tags": [ "tag1", "tag2" ]}

建立索引时与string是一样的,数据类型不能混

2、empty field

null,[],[null]

3、object field

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
PUT /company/_doc/1
{
"address": {
"country": "china",
"province": "guangdong",
"city": "guangzhou"
},
"name": "jack",
"age": 27,
"join_date": "2019-01-01"
}

address:object类型

查询映射

From: 元动力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
GET /company/_mapping
{
"company" : {
"mappings" : {
"properties" : {
"address" : {
"properties" : {
"city" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"country" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"province" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
},
"age" : {
"type" : "long"
},
"join_date" : {
"type" : "date"
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}

object

From: 元动力
1
2
3
4
5
6
7
8
9
10
{
"address": {
"country": "china",
"province": "guangdong",
"city": "guangzhou"
},
"name": "jack",
"age": 27,
"join_date": "2017-01-01"
}

底层存储格式

From: 元动力
1
2
3
4
5
6
7
8
{
"name": [jack],
"age": [27],
"join_date": [2017-01-01],
"address.country": [china],
"address.province": [guangdong],
"address.city": [guangzhou]
}

对象数组:

From: 元动力
1
2
3
4
5
6
7
{
"authors": [
{ "age": 26, "name": "Jack White"},
{ "age": 55, "name": "Tom Jones"},
{ "age": 39, "name": "Kitty Smith"}
]
}

存储格式:

From: 元动力
1
2
3
4
{
"authors.age": [26, 55, 39],
"authors.name": [jack, white, tom, jones, kitty, smith]
}

本站由 钟意 使用 Stellar 1.28.1 主题创建。
又拍云 提供CDN加速/云存储服务
vercel 提供托管服务
湘ICP备2023019799号-1
总访问 次 | 本页访问