黑马程序员--分布式搜索ElasticSearch学习笔记

写在最前

初识ElasticSearch

了解ES

ElasticSearch的作用
  • ElasticSearch是一款非常强大的开源搜素引擎,具备非常强大的功能,可以帮助我们从海量数据中快速找到需要的内容
  • 例如在电商平台搜索商品,搜索4090显卡会以红色标识
    zvVHPA.png
  • 在搜索引擎搜索答案,搜索到的内容同样会以红色标识,也可以实现搜索时的自动补全功能
    zvVLxP.png
ELK技术栈
  • ElasticSearch结合kibanaLogstashBeats,也就是elastic stack(ELK)。被广泛应用在日志数据分析、实时监控等领域
  • ElasticSearchelastic stack的核心,负责存储、搜索、分析数据
ElasticSearch和Lucene
  • ElasticSearch底层是基于Lucene来实现的

  • Lucene是一个Java语言的搜索引擎类库,是Apache公司的顶级项目,由DougCutting于1999年研发,官网地址:https://lucene.apache.org/

  • Lucene的优势

    • 易扩展
    • 高性能(基于倒排索引)
  • Lucene的缺点

    • 只限于Java语言开发
    • 学习曲线陡峭
    • 不支持水平扩展
  • ElasticSearch的发展史

    • 2004年,Shay Banon基于Lucene开发了Compass
    • 2010年,Shay Banon重写了Compass,取名为ElasticSearch,官网地址:https://www.elastic.co/cnl/
  • 相比于Lucene,ElasticSearch具备以下优势

    • 支持分布式,可水平扩展
    • 提供Restful接口,可以被任意语言调用
总结
  • 什么是ElasticSearch?
    • 一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析、系统监控等功能
  • 什么是Elastic Stack(ELK)?
    • 它是以ElasticSearch为核心的技术栈,包括beats、Logstash、kibana、elasticsearch
  • 什么是Lucene?
    • 是Apache的开源搜索引擎类库,提供了搜索引擎的核心API

倒排索引

  • 倒排索引的概念是基于MySQL这样的正向索引而言的
正向索引
  • 为了搞明白什么是倒排索引,我们先来看看什么是正向索引,例如给下表中的id创建索引
id title price
1 小米手机 3499
2 华为手机 4999
3 华为小米充电器 49
4 小米手环 49
  • 如果是基于id查询,那么直接走索引,查询速度非常快。
  • 但是实际应用里,用户并不知道每一个商品的id,他们只知道title(商品名称),所以对于用户的查询方式,是基于title(商品名称)做模糊查询,只能是逐行扫描数据
select id, title, price from tb_goods where title like %手机%
  • 1
  • 具体流程如下
    1. 用户搜索数据,搜索框输入手机,那么条件就是title符合%手机%
    2. 逐行获取数据
    3. 判断数据中的title是否符合用户搜索条件
    4. 如果符合,则放入结果集,不符合则丢弃
  • 逐行扫描,也就是全表扫描,随着数据量的增加,其查询效率也会越来越低。当数据量达到百万时,这将是一场灾难
倒排索引
  • 倒排索引中有两个非常重要的概念
    1. 文档(Document):用来搜索的数据,其中的每一条数据就是一个文档。例如一个网页、一个商品信息
    2. 词条(Term):对文档数据或用户搜索数据,利用某种算法分词,得到的具备含义的词语就是词条。例如:我最喜欢的FPS游戏是Apex,就可以分为我、我最喜欢、FPS游戏、最喜欢的FPS、Apex这样的几个词条
  • 创建倒排索引是对正向索引的一种特殊处理,流程如下
    • 将每一个文档的数据利用算法分词,得到一个个词条
    • 创建表,每行数据包括词条、词条所在文档id、位置等信息
    • 因为词条唯一性,可以给词条创建索引,例如hash表结构索引
词条(term) 文档id
小米 1,3,4
手机 1,2
华为 2,3
充电器 3
手环 4
  • 以搜索华为手机为例
    1. 用户输入条件华为手机,进行搜索。
    2. 对用户输入的内容分词,得到词条:华为、手机。
    3. 拿着词条在倒排索引中查找,可以得到包含词条的文档id为:1、2、3。
    4. 拿着文档id到正向索引中查找具体文档
  • 虽然要先查询倒排索引,再查询正向索引,但是无论是词条还是文档id,都建立了索引,所以查询速度非常快,无需全表扫描
正向和倒排
  • 那么为什么一个叫做正向索引,一个叫做倒排索引呢?
    • 正向索引是最传统的,根据id索引的方式。但是根据词条查询是,必须先逐条获取每个文档,然后判断文档中是否包含所需要的词条,是根据文档查找词条的过程
    • 倒排索引则相反,是先找到用户要搜索的词条,然后根据词条得到包含词条的文档id,然后根据文档id获取文档,是根据词条查找文档的过程
  • 那么二者的优缺点各是什么呢?
    • 正向索引
      • 优点:可以给多个字段创建索引,根据索引字段搜索、排序速度非常快
      • 缺点:根据非索引字段,或者索引字段中的部分词条查找时,只能全表扫描
    • 倒排索引
      • 优点:根据词条搜索、模糊搜索时,速度非常快
      • 缺点:只能给词条创建索引,而不是字段,无法根据字段做排序

ES的一些概念

ElasticSearch中有很多独有的概念,与MySQL中略有差别,但也有相似之处

文档和字段
  • ElasticSearch是面向文档(Document)存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为json格式后存储在ElasticSearch中
{
	"id": 1,
	"title": "小米手机",
	"price": 3499
}

{
	"id": 2,
	"title": "华为手机",
	"price": 4999
}

{
	"id": 3,
	"title": "华为小米充电器",
	"price": 49
} 

{
	"id": 4,
	"title": "小米手环",
	"price ": 299
}

  • 而Json文档中往往包含很多的字段(Field),类似于数据库中的列
索引和映射
  • 索引(Index),就是相同类型的文档的集合

  • 例如

    • 所有用户文档,可以组织在一起,成为用户的索引
    {
        "id": 101,
        "name": "张三",
        "age": 39
    }
    
    {
        "id": 102,
        "name": "李四",
        "age": 49
    }
    
    {
        "id": 103,
        "name": "王五",
        "age": 69
    }
    

    • 1
    • 所有商品的文档,可以组织在一起,称为商品的索引
    {
        "id": 1,
        "title": "小米手机",
        "price": 3499
    }
    
    {
        "id": 2,
        "title": "华为手机",
        "price": 4999
    }
    
    {
        "id": 3,
        "title": "苹果手机",
        "price": 6999
    }
    

    • 1
    • 所有订单的文档,可以组织在一起,称为订单的索引
    {
        "id": 11,
        "userId": 101,
        "goodsId": 1,
        "totalFee": 3999
    }
    
    {
        "id": 12,
        "userId": 102,
        "goodsId": 2,
        "totalFee": 4999
    }
    
    {
        "id": 13,
        "userId": 103,
        "goodsId": 3,
        "totalFee": 6999
    }
    

    • 1
  • 因此,我们可以把索引当做是数据库中的表

  • 数据库的表会有约束信息,用来定义表的结构、字段的名称、类型等信息。因此,索引库就有映射(mapping),是索引中文档的字段约束信息,类似于表的结构约束

MySQL与ElasticSearch
  • 我们统一的把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

部署单点ES
  • 因为我们还需要部署Kibana容器,因此需要让es和kibana容器互联,这里先创建一个网络(使用compose部署可以一键互联,不需要这个步骤,但是将来有可能不需要kbiana,只需要es,所以先这里手动部署单点es)
docker network create es-net
  • 1
  • 拉取镜像,这里采用的是ElasticSearch的7.12.1版本镜像
docker pull elasticsearch:7.12.1
  • 1
  • 运行docker命令,部署单点ES
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 \
    elasticsearch:7.12.1
  • 1
  • 命令解释:

    • -e "ES_JAVA_OPTS=-Xms512m -Xmx512m":配置JVM的堆内存大小,默认是1G,但是最好不要低于512M
    • -e "discovery.type=single-node":单点部署
    • -v es-data:/usr/share/elasticsearch/data:数据卷挂载,绑定es的数据目录
    • -v es-plugins:/usr/share/elasticsearch/plugins:数据卷挂载,绑定es的插件目录
    • -privileged:授予逻辑卷访问权
    • --network es-net:让ES加入到这个网络当中
    • -p 9200:暴露的HTTP协议端口,供我们用户访问的
  • 成功启动之后,打开浏览器访问:http://192.168.128.130:9200/, 即可看到elasticsearch的响应结果

部署kibana
  • 同样是先拉取镜像,注意版本需要与ES保持一致
docker pull kibana:7.12.1
  • 1
  • 运行docker命令,部署kibana
docker run -d \
    --name kibana \
    -e ELASTICSEARCH_HOSTS=http://es:9200 \
    --network=es-net \
    -p 5601:5601 \
    kibana:7.12.1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 命令解释
    • --network=es-net:让kibana加入es-net这个网络,与ES在同一个网络中
    • -e ELASTICSEARCH_HOSTS=http://es:9200:设置ES的地址,因为kibana和ES在同一个网络,因此可以直接用容器名访问ES
    • -p 5601:5601:端口映射配置
  • 成功启动后,打开浏览器访问:http://192.168.128.130:5601/ ,即可以看到结果
DevTools
  • kibana中提供了一个DevTools界面,在这个界面中我们可以编写DSL来操作ElasticSearch,并且有对DSL语句的自动补全功能
安装IK分词器
  • 默认的分词对中文的支持不是很好,所以这里我们需要安装IK插件
  • 在线安装IK插件
# 进入容器内部
docker exec -it elasticsearch /bin/bash

# 在线下载并安装
./bin/elasticsearch-plugin  install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip

#退出
exit
#重启容器
docker restart elasticsearch
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • IK分词器包含两种模式
    • ik_smart:最少切分
    • ik_max_word:最细切分
  • 下面我们分别测试这两种模式
    {% tabs 测试两种分词模式 %}
GET /_analyze
{
    "analyzer": "ik_smart",
    "text": "青春猪头G7人马文不会梦到JK黑丝兔女郎铁驭艾许"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 结果
{
  "tokens" : [
    {
      "token" : "青春",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "猪头",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "G7",
      "start_offset" : 4,
      "end_offset" : 6,
      "type" : "LETTER",
      "position" : 2
    },
    {
      "token" : "人",
      "start_offset" : 6,
      "end_offset" : 7,
      "type" : "COUNT",
      "position" : 3
    },
    {
      "token" : "不会",
      "start_offset" : 7,
      "end_offset" : 9,
      "type" : "CN_WORD",
      "position" : 4
    },
    {
      "token" : "梦到",
      "start_offset" : 9,
      "end_offset" : 11,
      "type" : "CN_WORD",
      "position" : 5
    },
    {
      "token" : "jk",
      "start_offset" : 11,
      "end_offset" : 13,
      "type" : "ENGLISH",
      "position" : 6
    },
    {
      "token" : "黑",
      "start_offset" : 13,
      "end_offset" : 14,
      "type" : "CN_CHAR",
      "position" : 7
    },
    {
      "token" : "丝",
      "start_offset" : 14,
      "end_offset" : 15,
      "type" : "CN_CHAR",
      "position" : 8
    },
    {
      "token" : "兔女郎",
      "start_offset" : 15,
      "end_offset" : 18,
      "type" : "CN_WORD",
      "position" : 9
    },
    {
      "token" : "铁",
      "start_offset" : 18,
      "end_offset" : 19,
      "type" : "CN_CHAR",
      "position" : 10
    },
    {
      "token" : "驭",
      "start_offset" : 19,
      "end_offset" : 20,
      "type" : "CN_CHAR",
      "position" : 11
    },
    {
      "token" : "艾",
      "start_offset" : 20,
      "end_offset" : 21,
      "type" : "CN_CHAR",
      "position" : 12
    },
    {
      "token" : "许",
      "start_offset" : 21,
      "end_offset" : 22,
      "type" : "CN_CHAR",
      "position" : 13
    }
  ]
}

  • 1
GET /_analyze
{
    "analyzer": "ik_max_word",
    "text": "青春猪头G7人马文不会梦到JK黑丝兔女郎铁驭艾许"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 结果
{
  "tokens" : [
    {
      "token" : "青春",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "猪头",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "G7",
      "start_offset" : 4,
      "end_offset" : 6,
      "type" : "LETTER",
      "position" : 2
    },
    {
      "token" : "G",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "ENGLISH",
      "position" : 3
    },
    {
      "token" : "7",
      "start_offset" : 5,
      "end_offset" : 6,
      "type" : "ARABIC",
      "position" : 4
    },
    {
      "token" : "人马",
      "
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值