Elasticsearch教程
# Elasticsearch
# 1.Elasticsearch的前言
# 1.1 Elasticsearch概述
ElasticSearch(简称为es)是一个开源的高扩展
的分布式全文检索引擎
,它可以近乎 实时存储、检索数据
;
本身扩展性很好,可以扩展到上百台服务器,处理PB级别(大数据时代)的数据。
es是使用java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的REST-ful
的API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
# 1.2 Solr和Ela的区别
ElasticSearch和Solr区别总结
- es基本是开箱即用(解压就可以用),非常简单。Solr安装略微复杂一丢丢。
- Solr利用Zookeeper进行分布式管理,而Elasticsearch自身带有分布式协调管理功能。
- Solr支持更多格式的数据,比JSON、XML、CSV,而Elasticsearch仅支持json文件格式。
- Solr官方提供的功能更多,而Elasticsearch本身更注重于核心功能,高级功能多有第三方插件提供,例如图形化界面需要kibana友好支撑。
- Solr查询快,但更新索引时慢(即插入删除慢),用于电商等查询多的应用;
- ES建立索引快(即查询慢),即实时性查询快,用于facebook新浪等搜索。
- Solr是传统搜索应用的有力解决方案,但Elasticsearch更适用于新兴的实时搜索应用。
- Solr比较成熟,有一个更大,更成熟的用户、开发和贡献者社区,而Elasticsearch相对开发维护者较少,更新太快,学习使用成本较高。
# 1.3 ElasticSearch安装
1.这里使用Docker的方式安装,github的项目源码:https://github.com/cn-zhangyh/Elasticsearch.git
Elasticsearch + Kibana + Elasticsearch-Head
version: "3.0"
services:
elasticsearch:
image: elasticsearch:7.11.1
container_name: elasticsearch
hostname: elasticsearch
ports:
- "9200:9200"
- "9300:9300"
environment:
discovery.type: single-node
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
restart: always
volumes:
- elasticsearch/:/usr/share/elasticsearch/
kibana:
image: kibana:7.11.1
container_name: kibana
hostname: kibana
ports:
- "5601:5601"
environment:
LASTICSEARCH_HOSTS: http://elasticsearch:9200
depends_on:
- elasticsearch
links:
- elasticsearch:elasticsearch
volumes:
- kibana/:/usr/share/kibana/
restart: always
elasticsearch-head:
image: mobz/elasticsearch-head:5
container_name: elasticsearch-head
hostname: head
ports:
- "9100:9100"
restart: always
volumes:
elasticsearch:
external:
true
kibana:
external:
true
2.熟悉Elasticsearch的目录
bin:启动文件
config:配置文件
log4j2:日志配置文件
jvm.options:java虚拟机相关的配置
elasticsearch.yml:elasticsearch的配置文件!默认9200端口!跨域!
lib:相关jar包
logs:日志
modules:功能模块
plugins:插件!
3.访问测试
# 1.4 了解ELK
ELK是Elasticsearch、Logstash、Kibana
三大开源框架首字母大写简称。市面上也被成为Elastic Stack。其中Elasticsearch是一个基于Lucene、分布式、通过Restful方式进行交互的近实时搜索平台框架。像类似百度、谷歌这种大数据全文搜索引擎的场景都可以使用Elasticsearch作为底层支持框架,可见Elasticsearch提供的搜索能力确实强大,市面上很多时候我们简称Elasticsearch为es。
Logstash是ELK的中央数据流引擎
,用于从不同目标(文件/数据存储/MQ)收集的不同格式数据,经过过滤后支持输出到不同目的地(文件/MQ/redis/elasticsearch/kafka等)。Kibana可以将elasticsearch的数据通过友好的页面展示出来,提供实时分析的功能。
市面上很多开发只要提到ELK能够一致说出它是一个日志分析架构技术栈
总称,但实际上ELK不仅仅适用于日志分析,它还可以支持其它任何数据分析和收集的场景,日志分析和收集只是更具有代表性,并非唯一性。
# 1.5 了解Kibana
Kibana是一个针对Elasticsearch的开源分析及可视化平台
,用来搜索、查看交互存储在Elasticsearch索引中的数据。使用Kibana,可以通过各种图表进行高级数据分析及展示。Kibana让海量数据更容易理解。它操作简单,基于浏览器的用户界面可以快速创建仪表板
(dashboard)实时显示
Elasticsearch查询动态。设置Kibana非常简单。无需编码或者额外的基础架构,几分钟内就可以完成Kibana安装并启动Elasticsearch索引监测。
自己修改/config/Kibana.yml文件,添加i18n.locale: “zh-CN”,重启Kibana。
i18n.locale: "zh-CN"
# 1.6 ES的核心概念
索引
字段类型(mapping)
文档(documents)
1.概述
在前面的学习中,我们已经掌握了es是什么,同时也把es的服务已经安装启动,那么es是如何去存储数据,数据结构是什么,又是如何实现搜索的呢?
集群,节点,索引,类型,文档,分片,映射是什么?
2.关系型数据库和elasticsearch客观对比
elasticsearch面向文档
Relational DB | ElasticSearch |
---|---|
数据库(database) | 索引(indices) |
表(tables) | types |
行(rows) | documents |
字段(columns) | fields |
elasticsearch(集群)中可以包含多个索引(数据库),每个索引中可以包含多个类型(表),每个类型下又包含多个文档(行),每个文档中又包含多个字段(列)。
3.物理设计
elasticsearch在后台把每个索引划分成多个分片,每个分片可以在集群中的不同服务器间迁移! 一个人就是一个集群!默认的集群名elasticsearch
4.逻辑设计
一个索引类型中,包含多个文档,比如说文档1、文档2。当我们索引一篇文档时,可以通过这样的一个顺序找到它:索引>类型>文档ID,通过这个组合我们就能索引到某个具体的文档。 注意:ID不必是整数,实际上它是个字符串。
文档
之前说elasticsearch是面向文档的,那么就意味着索引和搜索数据的最小单位是文档,elasticsearch中,文档有几个重要属性:
自我包含
:一篇文档同时包含字段和对应的值,也就是同时包含 key:value!可以是层次型的
:一个文档中包含自文档,复杂的逻辑实体就是这么来的!(就是一个json对象
,fastjson进行自动转换)灵活的结构
:文档不依赖预先定义的模式,我们知道关系型数据库中,要提前定义字段才能使用,在elasticsearch中,对于字段是非常灵活的,有时候,我们可以忽略该字段,或者动态的添加一个新的字段。 尽管我们可以随意的新增或者忽略某个字段,但是,每个字段的类型非常重要,比如一个年龄字段类型,可以是字符串也可以是整形。因为elasticsearch会保存字段和类型之间的映射及其他的设置。这种映射具体到每个映射的每种类型,这也是为什么在elasticsearch中,类型有时候也称为映射类型。
类型
- 类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。类型中对于字段的定义称为映射,比如name映射为字符串类型。我们说文档是无模式的,它们不需要拥有映射中所定义的所有字段,比如新增一个字段,那么elasticsearch是怎么做的呢?
- elasticsearch会自动的将新字段加入映射,但是这个字段的不确定它是什么类型,elasticsearch就开始猜,如果这个值是18,那么elasticsearch会认为它是整形。但是elasticsearch也可能猜不对,所以最安全的方式就是提前定义好所需要的映射,这点跟关系型数据库殊途同归了,先定义好字段,然后再使用,别整什么么蛾子。
索引(就是数据库)
- 索引是映射类型的容器,elasticsearch中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。然后它们被存储到了各个分片上了。我们来研究下分片是如何工作的。
5.物理设计:节点和分片如何工作
一个集群至少有一个节点,而一个节点就是一个elasricsearch进程,节点可以有多个索引默认的,如果你创建索引,那么索引将会有个5个分片(primary shard,又称主分片)构成的,每一个主分片会有一个副本(replica shard,又称复制分片)
上图是一个有3个节点的集群,可以看到主分片和对应的复制分片都不会在同一个节点内,这样有利于某个节点挂掉了,数据也不至于丢失。实际上,一个分片是一个Lucene索引,一个包含倒排索引
的文件目录,倒排索引的结构使得elasticsearch在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字。
6.倒排索引(面试题)
elasticsearch使用的是一种称为倒排索引的结构,采用Lucene倒排索作为底层。这种结构适用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。例如,现在有两个文档,每个文档包含如下内容:
study every day,good good up to forever #文档1包含的内容
To forever,study every day,good good up #文档2包含的内容
为了创建倒排索引,我们首先要将每个文档拆分成独立的词(或称为词条或者tokens),然后创建一个包含所有不重复的词条的排序列表,然后列出每个词条出现在哪个文档:
term | doc_1 | doc_2 |
---|---|---|
Study | 有 | 无 |
To | 无 | 无 |
every | 有 | 有 |
forever | 有 | 有 |
day | 有 | 有 |
study | 无 | 有 |
good | 有 | 有 |
every | 有 | 有 |
to | 有 | 无 |
up | 有 | 有 |
现在,我们试图搜索to forever,只需要查看包含每个词条的文档。权重score
term | doc_1 | doc_2 |
---|---|---|
to | 有 | 无 |
forever | 有 | 有 |
total | 2 | 1 |
# 1.7 IK分词器插件
概念
分词:即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如“我爱狂神”会被分为“我””爱”,“狂”,”神”,这显然是不符合要求的,所以我们需要安装中文分词器ik来解决这个问题。 ik提供了两个分词算法:ik_smart和ik_max_word,其中ik_smart为最少切分,ik_max_word为最细粒度划分!一会我们测试!
Docker安装插件
插件安装可以用elasticsearch-plugin install url命令
比如安装:elasticsearch-analysis-ik(分词器),Ik分词器版本要和ES和Kibana版本保持一致
docker进入容器命令,容器id为 54d1c07bc236
$ docker exec -it 54d1c07bc236 /bin/bash
plugins安装步骤
$ cd /usr/share/elasticsearch/plugins/
安装插件,elasticsearch-analysis-ik版本与elasticsearch保持一致,即7.11.1
$ elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.11.1/elasticsearch-analysis-ik-7.11.1.zip
退出容器
$ exit
重启docker容器
$ docker restart 54d1c07bc236
# 1.8 restful风格说明
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
基本rest说明:
method | url地址 | 描述 |
---|---|---|
PUT | localhost:9200/索引名称/类型名称/文档id | 创建文档(指定文档id) |
POST | localhost:9200/索引名称/类型名称 | 创建文档(随机文档id) |
POST | localhost:9200/索引名称/类型名称/文档id/_update | 修改文档 |
DELETE | localhost:9200/索引名称/类型名称/文档id | 删除文档 |
GET | localhost:9200/索引名称/类型名称/文档id | 查询文档通过文档id |
POST | localhost:9200/索引名称/类型名称/_search | 查询所有数据 |
# 2.Elasticsearch使用
# 2.1 创建一个索引
# ElasticSearch的7.x.x版本
PUT /索引名/类型名/文档id
{
请求体
}
# ElasticSearch的8.x.x版本,类型已经弃用,默认写_doc。
PUT /索引名/_doc/文档id
{
请求体
}
===========================================
以下为测试案例
===========================================
PUT /test1/_doc/1
{
"name": "user1",
"age": 3,
"birthday": "1994-11-24"
}
name这个字段需要指定类型吗?需要(毕竟我们关系型数据库是需要指定类型的)
官网类型文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/current/keyword.html
- 字符串类型
text、keyword
- 数值类型
long,integer,short,byte,double,float,half float,scaled float
- 日期类型
date
- te布尔值类型
boolean·
- 二进制类型
binary
- 等等……
# 2.2 指定字段类型
PUT /test2
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "long"
},
"birthday": {
"type": "date"
}
}
}
}
获得这个规则(可以通过GET请求获取具体的信息)
GET /test2
# 2.3 查看默认信息
如果自己的文档字段没有指定,那么es就会给我们默认配置字段类型!
扩展:
通过命令elasticsearch索引情况!(通过get_cat/可以获得es的当前的很多信息!)
GET _cat/health # 查看健康值
GET _cat/indices?v #查看所有东西的版本信息
# 2.4 修改和删除
1.修改
提交,使用PUT,覆盖(弊端:如果漏掉一个字段,那么字段的值就没了)
PUT /test1/_doc/1
{
"name": "user2",
"age": 4,
"birthday": "2022-11-12"
}
新的修改方法:使用post提交(7.x.x版本和8.x.x版本提交方式不一样)
"这是7.xx版本的"
POST /test3/_doc/1/_update
{
"doc": {
"name": "法外狂徒张三"
}
}
"这是8.xx版本的"
POST /test3/_doc/1/_update
{
"doc": {
"name": "法外狂徒张三"
}
}
2.删除
通过DELETE命令实现删除、根据你的请求来判断是删除索引还是删除文档记录!
DELETE test1
DELETE test2
# 2.5 文档的基本操作
增删改查的回顾
1.添加数据
PUT /kuangshen/_doc/1
{
"name": "狂神说",
"age": 3,
"desc": "工资2500",
"tags": ["直男","温暖","技术宅"]
}
PUT /kuangshen/_doc/2
{
"name": "张三",
"age": 30,
"desc": "没有工资",
"tags": ["渣男","旅游","交友"]
}
2.查询数据
GET /kuangshen/_doc/1
GET /kuangshen/_doc/2
3.更新数据
PUT /kuangshen/_doc/1
{
"name": "狂神说123",
"age": 3,
"desc": "工资2500",
"tags": ["直男","温暖","技术宅"]
}
POST /kuangshen/_doc/1
{
"doc": {
"name": "狂神说123"
}
}
4.条件查询
// 支持模糊查询
GET /kuangshen/_search?q=name:狂神说
# 2.6 文档复杂操作
(查询select:排序,分页,高亮,模糊查询,精准查询
)
1.精确查询
GET /kuangshen/_search
{
"query": {
"match": {
"name": "狂神"
}
}
}
2.过滤字段查询
输出结果,不需要很多:(我们之后使用java操作es,所有的方法和对象就是这里面的key!)
GET /kuangshen/_search
{
"query": {
"match": {
"name": "狂神"
}
},
"_source": ["name","desc"]
}
3.排序查询
GET /kuangshen/_search
{
"query": {
"match": {
"name": "狂神"
}
},
"_source": ["name","age","desc"],
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
4.分页查询
GET /kuangshen/_search/{currentPage}/{pageSize}
GET /kuangshen/_search
{
"query": {
"match": {
"name": "狂神"
}
},
"_source": ["name","age","desc"],
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
5.布尔值查询
must (and),所有的条件都要符合 where id=1 and name=xxx
多条件精确查询
GET /kuangshen/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "狂神"
}
},
{
"match": {
"age": "3"
}
}
]
}
}
}
should(or),所有的条件都要符合 where id=1 or name=xxx
相当于or
GET /kuangshen/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"name": "狂神"
}
},
{
"match": {
"age": "3"
}
}
]
}
}
}
must_not (not)
不等于,查询age不等于3,或者name不包含狂神说
GET /kuangshen/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"name": "狂神"
}
},
{
"match": {
"age": "3"
}
}
]
}
}
}
6.filter查询过滤
GET /kuangshen/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "狂神"
}
}
],
"filter": [
{
"range": {
"age": {
"gte": 2
}
}
}
]
}
}
}
7.匹配多个条件
GET /kuangshen/_search
{
"query": {
"match": {
"tags": "男 温暖"
}
}
}
# 2.7 精确查询
term 查询是直接通过倒排索引指定的词条进程精确的查找的! 关于分词:
term
:直接查询精确的。(两种情况:text、keyword)match
:会使用分词器解析!(先分析文档,然后在通过分析的文档进行查询!)
1.创建数据
PUT testdb
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"desc":{
"type": "keyword"
}
}
}
}
PUT testdb/_doc/1
{
"name": "狂神说Java name",
"desc": "狂神说Java desc"
}
PUT testdb/_doc/2
{
"name": "狂神说Java name",
"desc": "狂神说Java desc2"
}
2.分析数据,查看结果
GET _analyze
{
"analyzer": "keyword",
"text": "狂神说Java name"
}
GET _analyze
{
"analyzer": "standard",
"text": "狂神说Java name"
}
3.查询数据(keyword类型的字段不会被分词器解析)
# 2.8 多值精确匹配
GET testdb/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"age": "2"
}
},
{
"term": {
"age": "30"
}
}
]
}
}
}
# 2.9 高亮查询
搜索结果可以显示高亮
GET kuangshen/_search
{
"query": {
"match": {
"name": "狂神说"
}
},
"highlight": {
"fields": {
"name": {}
}
}
}