ElasticSearch搜索引擎
一、ElasticSearch介绍
ES是一款非常强大的开源搜索引擎,可以帮助我们从海量数据中快速找到需要的内容,可以用来实现搜索、日志统计、分析、系统监控等功能。
elasticsearch结合kibana、Logstash、Beats,也就是elastic stack(ELK)。被广泛应用在日志数据分析、实时监控等领域。
elasticsearch是elastic stack的核心,复杂存储、搜索、分析数据。
小结:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SUTAvUsc-1666856773309)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220804150426844.png)]
二、倒排索引
首先,倒排索引的概念是基于 MySQL 这样的正向索引而言的。
那么我们先讲何为正向索引。例如给下表(tb_goods)中的 id 创建索引
如果是根据 id 查询,那么直接走索引,查询速度非常快。
但如果是基于 title 做模糊查询,只能是逐行扫描数据,流程如下:
- 用户搜索数据,条件是 title 符合
"%手机%"
- 逐行获取数据,比如 id 为 1 的数据
- 判断数据中的 title 是否符合用户搜索条件
- 如果符合则放入结果集,不符合则丢弃。然后回到步骤1
逐行扫描,也就是全表扫描,随着数据量增加,其查询效率也会越来越低。当数据量达到数百万时,就是。。。
而倒排索引中有两个非常重要的概念:
- 文档(
Document
):用来搜索的数据,其中的每一条数据就是一个文档。例如一个网页、一个商品信息 - 词条(
Term
):对文档数据或用户搜索数据,利用某种算法分词,得到的具备含义的词语就是词条。例如:我是中国人,就可以分为:我、是、中国人、中国、国人这样的几个词条
创建倒排索引是对正向索引的一种特殊处理,流程如下:
- 将每一个文档的数据利用算法分词,得到一个个词条
- 创建表,每行数据包括词条、词条所在文档 id、位置等信息
- 因为词条唯一性,可以给词条创建索引,例如 hash 表结构索引
如图:
倒排索引的搜索流程如下(以搜索"华为手机"为例)
- 用户输入条件
"华为手机"
进行搜索 - 对用户输入内容分词,得到词条:
华为
、手机
- 拿着词条在倒排索引中查找,可以得到包含词条的文档 id 有 1、2、3
- 拿着文档 id 到正向索引中查找具体文档
虽然要先查询倒排索引,再查询正向索引,但是词条和文档id 都建立了索引,查询速度非常快!无需全表扫描。
为什么一个叫做正向索引,一个叫做倒排索引呢?
正向索引是最传统的,根据 id 索引的方式。但根据词条查询时,必须先逐条获取每个文档,然后判断文档中是否包含所需要的词条,是根据文档找词条的过程
倒排索引则相反,是先找到用户要搜索的词条,根据得到的文档 id 获取该文档。是根据词条找文档的过程
小结:
三、ES与MySQL概念对比
1、文档和字段
elasticsearch是面向文档存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为json格式存储在elasticsearch中。
而 JSON 文档中往往包含很多的字段(Field),类似于数据库中的列。
2、索引和映射
索引(Index),就是相同类型的文档的集合。
例如:
- 所有用户文档,就可以组织在一起,称为用户的索引;
- 所有商品的文档,可以组织在一起,称为商品的索引;
- 所有订单的文档,可以组织在一起,称为订单的索引;
因此,我们可以把索引当做是数据库中的表。
数据库的表会有约束信息,用来定义表的结构、字段的名称、类型等信息。因此,索引库中就有映射(mapping),是索引中文档的字段约束信息,类似表的结构约束。
mysql 与 elasticsearch
MySQL | Elasticsearch | 说明 |
---|---|---|
Table | Index | 索引(index),就是文档的集合,类似数据库的表(table) |
Row | Document | 文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式 |
Column | Field | 字段(Field),就是JSON文档中的字段,类似数据库中的列(Column) |
Schema | Mapping | Mapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema) |
SQL | DSL | DSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD |
- Mysql:擅长事务类型操作,可以确保数据的安全和一致性
- Elasticsearch:擅长海量数据的搜索、分析、计算
因此在企业中,往往是两者结合使用:
- 对安全性要求较高的写操作,使用 MySQL 实现
- 对查询性能要求较高的搜索需求,使用 ELasticsearch 实现
- 两者再基于某种方式,实现数据的同步,保证一致性
三、安装ES 、安装kibana 、安装IK分词器
1、安装ES:
因为我们还需要部署 kibana 容器,需要让 es 和 kibana 容器互联。这里先创建一个网络:
docker network create es-net
安装
docker run -d \
--name es \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
-e "discovery.type=single-node" \
-v es-data:/usr/share/elasticsearch/data \
-v es-plugins:/usr/share/elasticsearch/plugins \
--privileged \
--network es-net \
-p 9200:9200 \
-p 9300:9300 \
elasticsearch:7.12.1
命令解释:
-e "cluster.name=es-docker-cluster"
:设置集群名称-e "http.host=0.0.0.0"
:监听的地址,可以外网访问-e "ES_JAVA_OPTS=-Xms512m -Xmx512m"
:内存大小-e "discovery.type=single-node"
:非集群模式-v es-data:/usr/share/elasticsearch/data
:挂载逻辑卷,绑定es的数据目录-v es-logs:/usr/share/elasticsearch/logs
:挂载逻辑卷,绑定es的日志目录-v es-plugins:/usr/share/elasticsearch/plugins
:挂载逻辑卷,绑定es的插件目录--privileged
:授予逻辑卷访问权--network es-net
:加入一个名为 es-net 的网络中-p 9200:9200
:端口映射配置
访问地址:http://192.168.211.128 即可看到 elasticsearch 的响应结果
参考视频p81-p83链接:05-初识ES-安装es_哔哩哔哩_bilibili
2、安装kibana
安装kibana
kibana 可以给我们提供一个 elasticsearch 的可视化界面,便于我们学习命令。
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601 \
kibana:7.12.1
--network es-net
:加入一个名为 es-net 的网络中,与 elasticsearch 在同一个网络中-e ELASTICSEARCH_HOSTS=http://es:9200"
:设置 elasticsearch 的地址,因为 kibana 已经与 elasticsearch 在一个网络,因此可以用容器名直接访问 elasticsearch-p 5601:5601
:端口映射配置
访问地址:http://192.168.211.128,即可看到结果
控制面板:http://192.168.211.128/app/dev_tools#/console
3、安装IK分词器
由于国内访问 GitHub 较慢,我们选择离线模式安装。
安装插件需要知道 elasticsearch 的 plugins 目录位置,而我们用了数据卷挂载,因此需要查看 elasticsearch 的数据卷目录,通过下面命令查看
docker volume inspect es-plugins
显示结果:
[
{
"CreatedAt": "2022-05-06T10:06:34+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/es-plugins/_data",
"Name": "es-plugins",
"Options": null,
"Scope": "local"
}
]
说明 plugins 目录被挂载到了 /var/lib/docker/volumes/es-plugins/_data
这个目录中
重启容器
# 4、重启容器
docker restart es
# 查看es日志
docker logs -f es
IK分词器包含两种模式:
ik_smart
:智能切分,粗粒度ik_max_word
:最细切分,细粒度
我们在上面的 Kibana 控制台测试
GET /_analyze
{
"analyzer": "ik_max_word",
"text": "钟老师你好菜啊"
}
扩展词词典
在上面的IK分词器我们可以随着热点词来扩展,可以自己添加,比如 ”钟老师应该是一个热点词“,另外你也可以配置一些停用掉的敏感词,让其不进行分词。
打开IK分词器 config 目录是 IKAnalyzer.cfg.xml
,添加一个文件名,我们以 ext.dic
文件名为例。
我们去创建 ext.dic
,在其中添加热点词就好了,一个词一行。
重启 elasticsearch
docker restart es
重新测试
GET /_analyze
{
"analyzer": "ik_max_word",
"text": "钟老师你好菜啊"
}
IK分词器简介:
四、操作索引库
索引库就类似数据库表,mapping 映射就类似表的结构
我们要向 es 中存储数据,必须先创建“库”和“表”
mapping 是对索引库中文档的约束,常见的 mapping 属性包括:
- type:字段数据类型,常见的简单类型有:
- 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)
- 数值:long、integer、short、byte、double、float、
- 布尔:boolean
- 日期:date
- 对象:object
- index:是否创建索引,默认为 true
- analyzer:使用哪种分词器
- properties:该字段的子字段
我们以需要存储下面的 JSON 为例来讲解
{
"age": 21,
"weight": 52.1,
"isMarried": false,
"info": "钟老师真菜",
"email": "jialna@qq.com",
"score": [99.1, 99.5, 98.9],
"name": {
"firstName": "湖",
"lastName": "心"
}
}
首先对应的每个字段映射(mapping)情况如下:
- age:类型为 integer;参与搜索,index 为 true;无需分词器
- weight:类型为 float;参与搜索,index 为 true;无需分词器
- isMarried:类型为boolean;参与搜索,index 为 true;无需分词器
- info:类型为字符串,需要分词,因此是 text;参与搜索,index为true;分词器可以用 ik_smart
- email:类型为字符串,但是不需要分词,因此是 keyword;不参与搜索,index 为 false;无需分词器
- score:虽然是数组,但是我们只看元素的类型,类型为 float;参与搜索,index 为 true;无需分词器
- name:类型为 object,需要定义多个子属性
- name.firstName:类型为字符串,不需要分词,keyword;参与搜索,index 为 true;无需分词器
- name.lastName:类型为字符串,不需要分词,keyword;参与搜索,index 为 true;无需分词器
1、创建索引库和映射
上面我们了解了 Mapping 属性映射,接下来我们就去看看如何创建索引库及映射。
PUT /索引库名称
{
"mappings": {
"properties": {
"字段名":{
"type": "text",
"analyzer": "ik_smart"
},
"字段名2":{
"type": "keyword",
"index": "false"
},
"字段名3":{
"properties": {
"子字段": {
"type": "keyword"
}
}
}
// ...略
}
}
}
PUT /xn2001
{
"mappings": {
"properties": {
"info":{
"type": "text",
"analyzer": "ik_smart"
},
"email":{
"type": "keyword",
"index": "false"
},
"name":{
"properties": {
"firstName": {
"type": "keyword"
},
"lastName": {
"type": "keyword"
}
}
}
}
}
}
我们用真实的数据库表来创建一个索引库
- 字段名、字段数据类型,可以参考数据表结构的名称和类型
- 是否参与搜索要分析业务来判断,例如图片地址,就无需参与搜索
- 是否分词呢要看内容,内容如果是一个整体就无需分词
- 分词器,我们可以统一使用
ik_max_word
PUT /hotel
{
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"name":{
"type": "text",
"analyzer": "ik_max_word",
"copy_to": "all"
},
"address":{
"type": "keyword",
"index": false
},
"price":{
"type": "integer"
},
"score":{
"type": "integer"
},
"brand":{
"type": "keyword",
"copy_to": "all"
},
"city":{
"type": "keyword",
"copy_to": "all"
},
"starName":{
"type": "keyword"
},
"business":{
"type": "keyword"
},
"location":{
"type": "geo_point"
},
"pic":{
"type": "keyword",
"index": false
},
"all":{
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
特殊字段说明:
- location:地理坐标,里面包含精度、纬度
- all:一个组合字段,其目的是将多字段的值利用
copy_to
合并,提供给用户搜索,这样一来就只需要搜索一个字段就可以得到结果,性能更好。
ES中支持两种地理坐标数据类型:
- geo_point:由纬度(latitude)和经度(longitude)确定的一个点。例如:“32.8752345, 120.2981576”
- geo_shape:有多个 geo_point 组成的复杂几何图形。例如一条直线,“LINESTRING (-77.03653 38.897676, -77.009051 38.889939)”
2、修改索引库
倒排索引结构虽然不复杂,但是一旦数据结构改变(比如改变了分词器),就需要重新创建倒排索引,这简直是灾难。因此索引库一旦创建,无法修改 mapping
虽然无法修改 mapping 中已有的字段,但是却允许添加新的字段到 mapping 中,不会对倒排索引产生影响。
PUT /索引库名/_mapping
{
"properties": {
"新字段名":{
"type": "integer"
}
}
}
3、删除索引库
DELETE /索引库名
4、查询索引库
GET /数据库名
ongitude)确定的一个点。例如:“32.8752345, 120.2981576”
- geo_shape:有多个 geo_point 组成的复杂几何图形。例如一条直线,“LINESTRING (-77.03653 38.897676, -77.009051 38.889939)”
2、修改索引库
倒排索引结构虽然不复杂,但是一旦数据结构改变(比如改变了分词器),就需要重新创建倒排索引,这简直是灾难。因此索引库一旦创建,无法修改 mapping
虽然无法修改 mapping 中已有的字段,但是却允许添加新的字段到 mapping 中,不会对倒排索引产生影响。
PUT /索引库名/_mapping
{
"properties": {
"新字段名":{
"type": "integer"
}
}
}
3、删除索引库
DELETE /索引库名
4、查询索引库
GET /数据库名