文本内容来自“狂神说Java”
相关连接: 传送门
一、入门简介
Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。
相关功能:全文搜索、结构化搜索、分析
API:RESTful
1.1 市场
- 维基百科,类似百度百科,全文检索,高亮,搜索推荐(权重)
- The Guardian(国外新闻网站),类似搜狐新闻,用户行为日志(点击、浏览、收藏、评论),社交网络数据(对某新闻的相关看法),数据分析
- Stack Overflow(国外程序论坛),IT问题、程序报错提交,全文检索,搜索相关问题和答案
- Github(全球最大男性交往网站),搜索上亿行代码
- 电商网站,检索商品
- 日志数据分析,logstash采集日志,ES进行复杂数据分析,ELK技术(Elasticsearch + logstash + kibana)
- 商品加个监控网站,用户设定商品价格阈值,当低于该阈值,发送通知消息给用户
- BI系统,商业智能,大型商场集团分析某区域最近三年用户消费金额的趋势以及用户群体组成构成
1.2 ES与Solr差别
1. Solr简介
Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。
Solr是一个高性能,采用Java开发,基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。
API:Web-service API
2. Lucene简介
Lucene是apache软件基金会 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。人们经常提到信息检索程序库,虽然与搜索引擎有关,但不应该将信息检索程序库与搜索引擎相混淆。
ES和Solr都是基于Lucene进行开发的。
3. ES和Solr比较
- 当单纯对已有数据进行搜索时,Solr更快
- 当实时建立索引时(建立索引在MySQL中很慢),Solr会产生I/O阻塞,查询性能较差,ES有明显优势
- 随着数据量的增加,Solr搜索效率更低,ES没有明显变化
- 一个传统项目从Solr转为ES后搜索效率变化
总结:
- ES开箱即用(解压就能用)
- Solr利用Zookeeper进行分布式管理,而ES自身带有分布式协调管理功能
- Solr支持更多格式数据,比如Json、XML、CSV,而ES仅支持Json
- Solr官方提供功能更多,而ES更注重核心功能,高级功能多由第三方提供,例如图形界面需要Kibana支持
- Solr查询快,但是更新索引慢(即插入慢),多用于电商查询多的应用
- ES建立索引快(查询慢),即实时性查询快,用于Facebook新浪等搜索
- Solr是传统搜索应用有力解决方案,ES更适用于新兴实时搜索
1.3 了解ELK
那么,ELK 到底是什么呢? “ELK”是三个开源项目的首字母缩写,这三个项目分别是:Elasticsearch
、Logstash
和 Kibana
。任何做数据收集、分析、展示的工作都可以使用ELK实现。
- Elasticsearch 是一个搜索和分析引擎。
- Logstash 是服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到诸如 Elasticsearch 等“存储库”中。
- Kibana 则可以让用户在 Elasticsearch 中使用图形和图表对数据进行可视化。
1.4 ES安装
声明:
JDK1.8
及以上
ES版本和Java核心Jar包要版本对应
- ES默认端口:
9200
- head默认端口:
9100
- kibana默认端口:
5601
1. 下载
根据要部署的机器进行对应操作系统的下载,这里下载的是:
- ES版本(
elasticsearch-7.6.2-windows-x86_64.zip
) - Kibana版本(
kibana-7.6.2-windows-x86_64.zip
) - head版本(
elasticsearch-head-5.0.0.zip
)
ELK
是Web项目,需要前端环境,需要提前安装Nodejs
、python2
、npm
2. Windows安装ES
(1)解压即可使用
(2)熟悉安装目录
bin
:启动文件config
:配置文件elasticsearch.yml
:ES配置文件(默认9200
端口)jvm.options
:JVM配置文件log4j2.properities
:日志配置文件
jdk
:默认环境lib
:相关jar包(Lucene)modules
:功能模块plugins
:插件(IK分词器)
3. 启动
- 双击
bin/elasticsearch.bat
文件启动
- 访问
localhost:9200
显示主机名称,默认一台机子是一个集群,会生成一个
cluster_uuid
,显示当前ES版本7.6.2
,基于Lucene版本8.4.0
1.5 辅助插件安装
1. ES安装可视化界面 head插件
Chrome中有head的插件,这里通过软件安装
(1)在指定位置执行npm install
(2)执行npm run start
后访问9100
端口
这里
9100
的head要连接9200
的ES,会产生跨域问题
需要修改ES文件下config/elasticsearch.yml
(3)重启ES和head
跨域问题解决
2. 查看head
这个Head就当做数据展示工具,后面所有查询使用
kibana
- 查看索引(理解成数据库)
点击新建索引,可进行创建:
- 查看数据
之后创建数据就会放在这个里面
- 具体命令
具体命令可以在这里进行编写Json,使用RESTful风格
3. 安装kibana
下载安装包后进行解压
开启kibana
双击bin/kibana.bat
这里如果启动有问题,报错``,解决办法如下:
- 在ES配置文件中,修改:
- 在kibana配置文件中,修改:
修改完成后重新启动ES和kibana即可。
此时ES要通过ip加端口访问,head连接也要使用ip
- 访问kibana可以:
http://localhost:5601
- 可以使用kibana进行开发写数据
4. kibana中文汉化插件
- 地址
../kibana-7.6.2-windows-x86_64/x-pack/plugins/translations/translations
配置:修改kibana的yaml配置文件
重启kibana后访问5601
,显示在中文:
二、ES核心概念理解
2.1 概述
在前面学习中,掌握了ES是什么,同时也把ES服务安装,那么ES如何存储数据,数据结构是什么,又是如何实现搜索的呢?我们先看看ES的相关概念
1. 概念
- 集群
- 节点
- 索引
- 类型
- 文档
- 分片
- 映射
ES是面向文档的,关系型数据库和ES的对比如下:
关系型数据库 | ES |
---|---|
数据库DB | 索引Indices |
表tables | types (慢慢弃用) |
行rows | documents |
字段columns | fields |
ES集群中可以包含多个索引(DB),每个索引中可以包含多一个类型(表),每个类型下包含多个文档(行),每个文档中包含多个字段(列),一切都是Json。
物理设计: ES在后台把每个索引划分成多个分片,每个分片可以在集群中的不同服务器间迁移。一个ES就是一个集群,默认名称为elasticsearch
逻辑设计: 一个索引类型包含多个文档,比如文档1、文档2;当我们索引一篇文档时,可以通过这样的顺序找到它:索引 -> 类型 -> 文档ID
,通过这个组合我们能够所引导具体的文档,注意ID不必是整数,实际上是一个字符串。
2. 文档
ES是面向文档的,那么索引和搜索数据的最小单位就是文档,其有几个重要的属性:
- 自我包含,一篇文档同时包含字段和对应的值,也就是同时包含
key:value
- 可以是层次型的,一个文档中包含文档,复杂逻辑实体就是这样,就是json对象
- 灵活的结构,文档不依赖预先定义的模式,ES中对于字段非常灵活,有时候可以忽略字段,或者动态增加字段
尽管我们可随意增加或忽略字段,但每个字段类型非常重要,比如年龄字段,可以是字符、整形。ES会保存字段和类型之间的映射以及其他设置。这种映射具体到每一个映射的每种类型,这也是为什么在ES中类型也成为了映射类型。
文档就是一行行数据,每一个叫一个文档
user
1 nick 18
2 blade 20
3. 类型(映射类型,有点像表结构)
类型是文档的逻辑容器,就像关系数据库一样,表格是行的容器。类型中对于字段的定义成为映射
,比如name
映射为字符串类型。
文档是无模式的,他们不需要拥有映射中所定义的所有字段,比如新增一个字段,那么ES怎么做呢?它会自动将新字段加入映射,但这个字段不确定是什么类型,ES就开始猜,如果值为18
,那么ES认为是整形。但是也有可能猜不对,所以最安全的方式就是提前定义所需要的映射。
name:text
birth:date
4. 索引(数据库)
索引是映射类型的容器,ES索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。然后他们被存到各个分片上。
物理设计: 节点和分片如何工作?
一个集群至少有一个节点,一个节点就是一个ES进程,节点可以有多一个索引,所以你创建索引,那么索引将会有5个分片(primary shard
称为主片)构成,每一个主分片会有一个副本(replica shard
称为复制分片)
结构图如下:
上图是一个有三个节点的集群,可以看到主分片和对应的复制分片都不会在同一个节点,这样有利于某个节点挂了,数据也不至于丢失。实际上,一个分片是一个Lucene索引,一个包含倒排索引的文件目录,倒排索引的结构使得ES在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字。
5. 倒排索引
ES使用的是一种成为倒排索引的结构,采用Lucene倒排索引作为底层,这种结构适用于快速全文搜索,一个索引由文档中所有不重复的列表构成,对弈每一个词,都有一个包含他的文档列表。例如现在有两个文档,每个文档包含如下内容:
Study every day, good good up to forever # 文档1包含的内容
To forever, study every day, good good up $ 文档2包含的内容
为了创建倒排索引,首先要将每一个文档拆分成独立的词(或成为词条,tocken),然后穿件一个包含所有不重复词条的排除列表,然后列出每个词出现在哪个文档:
term | doc_1 | doc_2 |
---|---|---|
Study | ✔ | ✖ |
To | ✖ | ✔ |
every | ✔ | ✔ |
forever | ✔ | ✔ |
day | ✔ | ✔ |
study | ✖ | ✔ |
good | ✔ | ✔ |
every | ✔ | ✔ |
to | ✔ | ✖ |
up | ✔ | ✔ |
现在我们要搜索to forever
,只需要查看包含每个词条的文档:
term | doc_1 | doc_2 |
---|---|---|
to | ✔ | ✖ |
forever | ✔ | ✔ |
total | 2 | 1 |
两个文档都匹配,但是第一个文档比第二个匹配程度更高。如果没有别的条件,现在这两个包含关键字的文档都会返回。但是文档一的权重会更高。
- 具体示例:比如我们通过博客标签来搜索博客文章,那么倒排索引就是这样一个结构
如果要搜索含有python
标签的文章,那相对于产找所有原始数据而言,查找倒排索引后的数据将会快很多,只需要看标签这一栏,然后获取相关文章的ID即可。
6. ES索引和Lucene索引对比
在ES中,索引这个词被频繁使用,索引被分成多个分片,每个分片是一个Lucene索引。所以ES索引是由多个Lucene索引组成。
2.2 IK分词器插件
什么是IK分词器?
分词
:即把一段中文或者别的划分成一个个的关键字,在搜索的时候会把自己的信息进行分词,会把数据库中或者索引库中数据进行分词,然后进行匹配,默认的中文分词器是将每一个字看成一个词,比如“我爱学习”,会被分成“我”、“爱”、“学”、“习”,这显然是不符合要求的,所以我们需要安装中文分词器IK来解决这个问题。
IK提供两个分词算法:ik_smart
、ik_max_word
。
ik_smart
:为最少切分(结果不会有重复的)ik_max_word
为最细粒度划分(各种可能的组合都会存在)
1. IK分词器安装
IK分词器的版本与ES的版本要一致
下载链接:传送门
-
解压后放在ES目录下
../plugins/
下,重命名为ik
-
然后重启ES后观察:
log中显示已加载插件
analysis-ik
2. 使用kibana进行测试:
- 测试最少切分
ik_smart
GET _analyze
{
"analyzer": "ik_smart",
"text": "我爱学习,更爱玩游戏"
}
- 结果
{
"tokens" : [
{
"token" : "我",
"start_offset" : 0,
"end_offset" : 1,
"type" : "CN_CHAR",
"position" : 0
},
{
"token" : "爱学习",
"start_offset" : 1,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "更",
"start_offset" : 5,
"end_offset" : 6,
"type" : "CN_CHAR",
"position" : 2
},
{
"token" : "爱玩",
"start_offset" : 6,
"end_offset" : 8,
"type" : "CN_WORD",
"position" : 3
},
{
"token" : "游戏",
"start_offset" : 8,
"end_offset" : 10,
"type" : "CN_WORD",
"position" : 4
}
]
}
- 测试
ik_max_word
最细粒度划分
GET _analyze
{
"analyzer": "ik_max_word",
"text": "我爱学习,更爱玩游戏"
}
{
"tokens" : [
{
"token" : "我",
"start_offset" : 0,
"end_offset" : 1,
"type" : "CN_CHAR",
"position" : 0
},
{
"token" : "爱学习",
"start_offset" : 1,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "学习",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "更",
"start_offset" : 5,
"end_offset" : 6,
"type" : "CN_CHAR",
"position" : 3
},
{
"token" : "爱玩",
"start_offset" : 6,
"end_offset" : 8,
"type" : "CN_WORD",
"position" : 4
},
{
"token" : "玩游戏",
"start_offset" : 7,
"end_offset" : 10,
"type" : "CN_WORD",
"position" : 5
},
{
"token" : "玩游",
"start_offset" : 7,
"end_offset" : 9,
"type" : "CN_WORD",
"position" : 6
},
{
"token" : "游戏",
"start_offset" : 8,
"end_offset" : 10,
"type" : "CN_WORD",
"position" : 7
}
]
}
3. 问题
问题:如图,我们测试了“我是狂神说”,但是拆分之后并没有想要的“狂神说”,是因为这种我们自己创造的词汇,需要手动在分词器
字典
中添加
- IK分词器字典配置
路径:elasticsearch-7.6.2\plugins\ik\config
- 字典内容:
- 重启ES后重新测试:
可以看到log中加载了我们定义的字典
重新测试,可以看到分词器拆分出了“狂神说”
2.3 RESTful风格基本操作
一种软件架构风格,而不是标准,知识提供一组设计原则和约束条件,主要用于客户端和服务器交互类软件。基于这个风格设计的软件更加简洁,更有层次,更容易实现缓存等机制。
1. 基本测试
- 创建索引
删除之前为了测试创建的一个索引
nick
,之后在head中查看ES的索引,里面没有我们自定义的索引
里面这三个索引是kibana自己创建的
- 执行创建索引指令
// 格式: /索引名/类型名/文档id { 请求体 }
PUT /test1/type1/1
{
"name": "NickWang",
"age": 28
}
结果格式说明
{
"_index" : "test1", // 索引为test1
"_type" : "type1", // 类型为type1
"_id" : "1", // 文档id为1
"_version" : 1, // 当前数据没有被修改过,版本为1
"result" : "created", // 当前状态是已创建
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
- 在head中查看概览
- 在head中查看索引
- 在head中查看数据
2. 数据类型
刚才我们在类型type1
文档1
中添加数据name
这个字段是没有指定类型的,可选的类型如下:
数据类型 | 值类型 |
---|---|
字符串类型 | text 、keyword (不可分割) |
数值类型 | long 、integer 、short 、byte 、double 、float 、half float 、scaled float |
日期类型 | date |
布尔类型 | boolean |
二进制类型 | binary |
3. 指定字段的类型
我们重新创建一个索引,然后通过
mappings
下的properties
来创建索引规则
4. 查看信息
使用
GET
请求来获取,这里写索引就获取索引信息,写文档就获取文档信息
5. 查看默认类型
- 创建一个
test3
索引
这里指令
PUT /索引/_doc/1
中_doc
就是默认的类型,后面直接跟文档编号
- 查看默认类型
不在索引上设置
mappings
时是有默认类型的,其中name
的类型keyword
是一种不可分割的类型
6. 扩展命令
GET _cat/health
:查看健康值
GET _cat/indices?v
:查看总览数据
7. 修改索引
方式一:修改依旧使用
PUT
,覆盖值即可
- 缺点:如果忘记某个字段,则字段就不在了
方式二(现在的方法):使用
POST
方法,在后面加上_update
即可修改文档
- 其中
doc
就是要修改具体的文档
8. 删除索引
使用
DELETE
即可
9. 根据条件查询
如果删除类型的情况,直接
GET /索引名/_search?q=字段:值