Elasticsearch超详细笔记
引言:
本文主要分享了有关Elasticsearch(简称ES)的知识,包括:ES的简介、基本概念、倒序索引、ES的安装以及使用、Kibana可视化工具的安装与使用、分词的使用、使用可视化工具Kibana实现索引和文档的增删查改、以及一些高级的ES查询,如:批量获取文档信息、使用Bulk API实现批量操作、采用乐观锁机制实现并发控制、Mapping、通用查询(分词查找、短语查询、返回指定字段、高亮查询、范围查询 )、Bool过滤查询、范围查询、聚合查询等等;
文章目录
1. Elasticsearch简介
Elasticsearch 是一个开源的搜索引擎, 建立在全文搜索引擎库 Apache Lucene 基础上;内部使用 Lucene 做索引与搜索,但是它的目的是使全文检索变得简单,通过隐藏 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTFUL API;
Elasticsearch 不仅仅只是一个全文搜索引擎是一个分布式的实时文档存储,每个字段可以被索引与搜索可以作数据库用;还是一个分布式实时分析搜索引擎,能胜任上百个服务节点的扩展,并支持PB级别(大数据级别)的结构化(定死的,进去多少出来多少,多余的全部抛弃)或者非结构化数据(只需要直到其属性即可);
1.1 衍生的开源软件
- Elasticsearch :分布式搜索引擎
- Logstash:日志的采集与解析工具
- Kibana:可视化分析平台
- Beats:数据采集工具家族(替换Logstash)
- X-Pack:功能包
1.2 ES特性
- 速度快、易扩展、弹性、灵活、操作简单、多语言客户端
- 分布式:横向扩展非常灵活
- 全文检索:基于lucene的强大的全文检索能力;
- **近实时搜索和分析:**数据进入ES,可达到近实时搜索,还可进行聚合分析
- 高可用:容错机制,自动发现新的或失败的节点,重组和重新平衡数据
- 模式自由:ES的动态mapping机制可以自动检测数据的结构和类型,创建索引并使数据可搜索。
- RESTful API:JSON + HTTP
1.3 ES核心概念
Near Realtime(NRT) 近实时:数据提交索引后,立马就可以搜索到。
Cluster 集群:一个集群由一个唯一的名字标识,默认为“elasticsearch”。集群名称非常重要,具有相同集群名的节点才会组成一个集群。集群名称可以在配置文件中指定。
Node 节点:存储集群的数据,参与集群的索引和搜索功能。像集群有名字,节点也有自己的名称,默认在启动时会以一个随机的UUID的前七个字符作为节点的名字,你可以为其指定任意的名字。通过集群名在网络中发现同伴组成集群。一个节点也可是集群。
Index 索引:一个索引是一个文档的集合(等同于solr中的集合)。每个索引有唯一的名字,通过这个名字来操作它。一个集群中可以有任意多个索引。Type 类型:指在一个索引中,可以索引不同类型的文档,如用户数据、博客数据。从6.0.0 版本起已废弃,一个索引中只存放一类数据。
Document 文档:被索引的一条数据,索引的基本信息单元,以JSON格式来表示。
Shard 分片:在创建一个索引时可以指定分成多少个分片来存储。每个分片本身也是一个功能完善且独立的“索引”,可以被放置在集群的任意节点上。分片的好处:允许我们水平切分/扩展容量、可在多个分片上进行分布式的、并行的操作,提高系统的性能和吞吐量。
注意:分片数创建索引时指定,创建后不可改了。备份数可以随时改。
Replication 备份: 一个分片可以有多个备份(副本)。备份的好处:高可用。一个主分片挂了,副本分片就顶上去
扩展搜索的并发能力、吞吐量。搜索可以在所有的副本上并行运行。-高并发下副本也可搜索
1.4 结构化数据与非结构化数据
平时的数据可以分为结构化数据和非结构化数据:
- 结构化数据是指格式和长度固定的数据,如一个人的年龄,姓名等;
- 非结构化数据是指格式和长度不固定的数据,如一个文章的内容等;
对于结构化数据,可以存储在DB中通过精确匹配找到。但是对于非结构化数据,一般查询时只能提供查询的局部信息或模糊信息,传统数据库无法根据这些信息进行查询,效率差;
1.5 倒排索引
倒排索引是相对于正排索引而言的,可以有效的解决该问题;
正排索引可以通过id查找到对应的文章,但是无法通过给的部分内容如elasticsearch,找出含有该关键字的文档。
倒排索引会先对文档进行分析将其拆分成单个Term, 并存储包含该Term的文档id,这样便可以实现通过内容查找对应文档,如包含elasticsearch的文档为文档1;
倒排索引的过程:
- 通过倒排索引获得“搜索引擎”对应的文档id列表,有1和3;
- 通过正排索引查询1和3的完整内容;
- 返回最终结果;
2. ES的安装使用
2.1 安装
官网下载地址: https://www.elastic.co/downloads/elasticsearch
推荐到华为云镜像下载:https://mirrors.huaweicloud.com/
JDK版本: 1.8
2.2 启动
解压安装包,进入bin目录中双击elasticsearch.bat等待启动完毕;
打开浏览器,输入 http://localhost:9200 出现以下画面,说明ES安装成功;
2.3 ES端口
- 9300端口: ES节点之间通讯使用,9300是tcp通讯端口,集群间和TCPClient 都走它;
- 9200端口: ES节点和外部通讯使用,9200是http协议的RESTful接口;
2.4 可视化工具Kibana
Kibana是一个针对Elasticsearch的开源分析及可视化平台,使用Kibana可以查询、查看并与存储在ES索引的数据进行交互操作,同时也能执行高级的数据分析,并能以图表、表格和地图的形式查看数据 ;
2.4.1 下载安装Kibana
下载安装包,需要和ES版本匹配,下载完毕后安装到相应的目录就行;
下载地址:https://www.elastic.co/downloads/kibana
2.4.2 配置
在目录config/kibana.yml中配置 elasticsearch.hosts: 的值为ES的访问地址;
2.4.3 启动Kibana
双击bin/kibana.bat
- 访问地址:http://localhost:5601
2.5 集成Ikanalyzer
IK Analyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包;从3.0版本开始,IK发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了简单的分词歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化;
2.5.1 下载安装Ikanalyzer
获取 ES-IKAnalyzer插件,需要和ES版本一致;
将ik的压缩包解压到 ES安装目录的plugins/目录下(新建文件夹analysis-ik),重启ES;
下载地址: https://github.com/medcl/elasticsearch-analysis-ik/releases
2.5.2 两种分词模式
Ik分词器有ik_max_word和ik_smart两种模式:
- ik_max_word:会将文本做最细粒度的拆分;
- ik_smart:会做最粗粒度的拆分;
2.5.3 测试分词_细粒度拆分
{
"text":"我爱学习,最爱学Java",
"analyzer":"ik_max_word"
}
2.5.4 测试分词_粗粒度拆分
{
"text":"我爱学习,最爱学Java",
"analyzer":"ik_smart"
}
2.5.5 自定义分词库
首先在IKAnalyzer.cfg.xml里面定义自定义的字典,我这里是main.dic(自己随意命名);
<?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">main.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
3. 使用Kibana实现增删查改
3.1 创建索引
PUT /kak
{
"settings": {
"index":{
"number_of_shards":5,
"number_of_replicas":3
}
}
}
3.2 查看索引
#查看单个
GET /kak/_settings
#查看所有
GET /_all/_settings
3.3 添加文档
PUT /kak/student/1
{
"name":"kaka",
"sex":"man",
"age":23,
"score":100
}
POST /kak/student/2
{
"name":"taotao",
"sex":"woman",
"age":23,
"score":100
}
3.4 查询文档
GET /kak/student/1
GET /kak/student/_search
3.5 修改文档
PUT方法:存在就修改,不存在就新增;
PUT /kak/student/1
{
"name":"kaka",
"sex":"man",
"age":23,
"score":150
}
3.6 删除文档
#删除单个
DELETE /kak/student/2
#删除全部
DELETE /kak
4. 批量获取文档信息
创建索引、添加文档
PUT kak_01
PUT kak_01/student/1
{
"name":"kaka",
"age":23,
"sex":"man"
}
PUT kak_01/student/2
{
"name":"zhuangzhuang",
"age":23,
"sex":"woman"
}
PUT kak_02
PUT kak_02/student/1
{
"name":"zhangsan",
"age":23,
"sex":"man"
}
PUT kak_02/student/2
{
"name":"lisi",
"age":23,
"sex":"woman"
}
4.1 获取多个索引下面的多个文档对象
GET /_mget
{
"docs":[
{
"_index": "kak_01",
"_type": "student",
"_id": 1
},
{
"_index": "kak_01",
"_type": "student",
"_id": 2
}
]
}
4.2 指定具体的字段
GET /_mget
{
"docs":[
{
"_index":"kak_01",
"_type":"student",
"_id":1,
"_source":["name","age"]
},
{
"_index":"kak_01",
"_type":"student",
"_id":2,
"_source":["name","age","sex"]
}
]
}
4.3 简化批量获取的方式
GET /kak_01/student/_mget
{
"docs":[
{
"_id":1
},
{
"_id":2
}
]
}
5. 使用Bulk API实现批量操作
语法格式:
{action:{metadata}}
{requstbody}
action:(行为)
- create:文档不存在时创建
- update:更新文档
- index:创建新文档或替换已有文档
- delete:删除一个文档
create 和index的区别:
- 如果数据存在,使用create操作失败,会提示文档已经存在;
- 如果数据存在,使用index则可以成功执行;
5.1 批量添加
POST kak_01/student/_bulk
{
"index":{
"_id":3}}
{
"name":"kak_03","age":23,"sex":"woman"}
{
"index":{
"_id":4}}
{
"name":"kak_04","age":23,"sex":"woman"}
{
"index":{
"_id":5}}
{
"name":"kak_05","age":23,"sex":"man"}
5.2 批量查询
GET kak_01/student/_mget
{
"ids":[1,2,3]
}
5.3 批量删除
POST kak_01/student/_bulk
{
"delete":{
"_index":"kak_01","_type":"student","_id":4}}
{
"delete":{
"_index":"kak_01","_type":"student","_id":5}}
5.4 注意事项
操作的大小建议:
Bulk会把将要处理的数据载入内存中,所以数据量是有限制的,最佳的数据量不是一个确定的数值取决于硬件;
- 一般建议是1000-5000个文档,大小建议是5-15MB,默认不能超过100M,可以在ES的配置文件($ES_HOME下的config下的elasticsearch.yml)中设置;
6. 采用乐观锁机制实现并发控制
6.1 内部版本
ElasticSearch采用了乐观锁来保证数据的一致性,当用户对document进行操作时,并不需要对该document作加锁和解锁的操作,只需要指定要操作的版本;当版本号一致时,ElasticSearch会允许该操作顺利执行,当版本号存在冲突时,ElasticSearch会提示冲突并抛出异常(VersionConflictEngineException异常);
- ElasticSearch的版本号的取值范围为1到2^63-1。
- 使用的是_version(每修改一个版本加一)
6.2 外部版本
Elasticsearch在处理外部版本号时会与对内部版本号的处理有些不同。它不再是检查version是否与请求中指定的数值相同,而是检查当前的version是否比指定的数值小。如果请求成功,那么外部的版本号就会被存储到文档中的version中。为了保持version与外部版本控制的数据一致使用version_type=external ;
7. Mapping
查看自动生成的映射情况
GET kak_01/_mapping
核心数据类型:
字符型:string
string类型包括,text 和 keyword
- text类型被用来索引长文本,在建立索引前会将这些文本进行分词,转化为词的组合,建立索引。允许es来检索这些词语。text类型不能用来排序和聚合。
- Keyword类型不需要进行分词,可以被用来检索过滤、排序和聚合。
- keyword 类型字段只能用本身来进行检索
数字型:long, integer, short, byte, double, float
日期型:date
布尔型:boolean
二进制型:binary
关键的属性:
“index”: true分词,false不分词,设置成false,字段将不会被索引;
“analyzer”:"ik"指定分词器,默认分词器为standard analyzer
PUT /lib
{
"settings":{
"number_of_shards" : 3,
"number_of_replicas" : 0
},
"mappings":{
"properties":{
"title":{
"type":"text"},
"name":{
"type":"text","analyzer":"standard"},
"publish_date":{
"type":"date","index":false},
"price":{
"type":"double"}
}
}
}
- 定义Mapping没有类型,之前能写进去是因为通过PUT,放文档放进去的;
8. 通用查询
8.1 准备数据
PUT /lib2
{
"settings":{
"number_of_shards" : 3,
"number_of_replicas" : 0
},
"mappings":{
"properties":{
"name": {
"type":"text"},
"address": {
"type":"text"},
"age": {
"type":"integer"},
"interests": {
"type":"text"},
"birthday": {
"type":"date"}
}
}
}
PUT /lib2/_doc/1
{
"name" : "kak",
"address" : "sx",
"age" : 32,
"interests" : "java,music",
"birthday": "1988-08-08"
}
PUT /lib2/_doc/2
{
"name" : "taotao",
"address" : "sz",
"age" : 33,
"interests" : "java,music,money",
"birthday": "1983-08-08"
}
PUT /lib2/_doc/2
{
"name" : "taotao",
"address" : "sz",
"age" : 33,
"interests" : "java,music,money",
"birthday": "1983-08-08"
}
8.2 精确查找
term查询
8.2.1 查询某个字段里含有某个关键词的文档
GET /lib2/_search/
{
"query": {
"term": {
"name": {
"value": "kak"
}
}
}
}
8.2.2 查询某个字段里含有多个关键词的文档
GET /lib2/_search/
{
"query": {
"terms": {
"interests": [
"java",
"music"
]
}
}
}
8.3 设值返回的条数
GET /lib2/_search
{
"from": 0,
"size": 2,
"query": {
"terms": {
"interests": [
"java",
"music"
]
}
}
}
8.4 设值返回的版本
GET /lib2/_search
{
"version": true,
"from":0,
"size": 2,
"query": {
"terms": {
"interests": [
"java",
"money"
]
}
}
}
8.5 分词查找
match query知道分词器的存在,会对filed进行分词操作,然后再查询
GET /lib2/_search
{
"query": {
"match": {
"name": "kak helloworld"
}
}
}
8.6 指定多个字段进行查询
GET /lib2/_search
{
"query": {
"multi_match": {
"query": "java",
"fields": ["interests","name"]
}
}
}
8.7 短语查询
ElasticSearch引擎首先分析查询字符串,从分析后的文本中构建短语查询,这意味着必须匹配短语中的所有分词,并且保证各个分词的相对位置不变
GET /lib2/_search
{
"query": {
"match_phrase": {
"interests": "java,music"
}
}
}
8.8 指定返回的字段
GET /lib2/_search
{
"_source": ["address","name"],
"query": {
"match": {
"interests": "java"
}
}
}
8.9 排序
GET /lib2/_search
{
"query": {
"match_all": {
}
},
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
8.10 前缀匹配
GET /lib2/_search
{
"query": {
"match_phrase_prefix": {
"name": {
"query": "kak"
}
}
}
}
8.11 范围查询
GET /lib2/_search
{
"query": {
"range": {
"birthday": {
"from": "1988-08-08",
"to": "2020-08-08",
"include_lower": true,
"include_upper": false
}
}
}
}
8.12 通配符
允许使用通配符* 和 ?来进行查询:
- *代表0个或多个字符
- ?代表任意一个字符
GET /lib2/_search
{
"query": {
"wildcard": {
"name": "kak*"
}
}
}
8.13 高亮查询
GET /lib2/_search
{
"query": {
"match": {
"interests": "java"
}
},
"highlight": {
"fields": {
"interests": {
}
}
}
}
9. Bool过滤查询
- 可以实现组合过滤查询
- must:必须满足的条件(and)
- should:可以满足也可以不满足的条件(or)
- must_not:不需要满足的条件(not )
GET /lib2/_search
{
"post_filter": {
"bool": {
"should":[
{
"term":{
"age":32}},
{
"term":{
"interests":"java"}}
],
"must_not":{
"term":{
"age":90}
}
}
}
}
GET /lib2/_search
{
"post_filter": {
"bool": {
"must":[
{
"term":{
"age":32}},
{
"term":{
"interests":"java"}}
]
}
}
}
10. 范围查找
- gt:>
- lt: <
- gte: >=
- lte: <=
GET /lib2/_search
{
"post_filter": {
"range": {
"age": {
"gte": 10,
"lte": 32
}
}
}
}
11. 聚合查询
11.1 求和
sum:求和
GET /lib2/_search
{
"size": 0,
"aggs": {
"agesum": {
"sum": {
"field": "age"
}
}
}
}
11.2 最小值
min:最小值
GET /lib2/_search
{
"size": 0,
"aggs": {
"agemin": {
"min": {
"field": "age"
}
}
}
}
11.3 最大值
- max:最大值
GET /lib2/_search
{
"size": 0,
"aggs": {
"agemax": {
"max": {
"field": "age"
}
}
}
}
11.4 平均值
- avg:平均值
GET /lib2/_search
{
"size": 0,
"aggs": {
"ageavg": {
"avg": {
"field": "age"
}
}
}
}
11.5 求基数
- cardinality:基数(互不相同的值的个数 )
GET /lib2/_search
{
"size": 0,
"aggs": {
"agecardinality": {
"cardinality": {
"field": "age"
}
}
}
}
11.6 分组
- terms:分组
GET /lib2/_search
{
"size": 0,
"aggs": {
"ageterms": {
"terms": {
"field": "age"
}
}
}
}