分布式搜索引擎01

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言


一、elasticsearch

elasticsearch是一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析、系统监控等功能。http://www.elastic.co/cn/
elastic stack(ELK)是以elasticsearch为核心的技术栈,结合kibana、Logstash、Beats。被广泛应用在日志数据分析、实时监控等领域。而elasticsearch是elastic stack的核心,负责存储、搜索、分析数据。
在这里插入图片描述


elasticsearch底层是基于lucene来实现的。Lucene是一个Java语言的搜索引擎类库,是Apache公司的顶级项目,提供了搜索引擎的核心API。
官网地址:https://lucene.apache.org/
Lucene优势:- 易扩展
                       - 高性能
            缺点:- 只限于java开发
                       - 不支持水平扩展


1)正向和倒排索引

  • 正向索引是最传统的,根据id索引的方式。但根据词条查询时,必须先逐条获取每个文档,然后判断文档中是否包含所需要的词条,是根据文档找词条的过程

  • 倒排索引则相反,是先找到用户要搜索的词条,根据词条得到保护词条的文档的id,然后根据id获取文档。是根据词条找文档的过程

倒排索引中有两个非常重要的概念:

  • 文档(Document):用来搜索的数据,其中的每一条数据就是一个文档。例如一个网页、一个商品信息
  • 词条(Term):对文档数据或用户搜索数据,利用某种算法分词,得到的具备含义的词语就是词条。例如:我是中国人,就可以分为:我、是、中国人、中国、国人这样的几个词条

创建倒排索引是对正向索引的一种特殊处理,流程如下:

  • 将每一个文档的数据利用算法分词,得到一个个词条
  • 创建表,每行数据包括词条、词条所在文档id、位置等信息
  • 因为词条唯一性,可以给词条创建索引,例如hash表结构索引

正向索引:
        优点:- 可以给多个字段创建索引
                  - 根据索引字段搜索、排序速度非常快
        缺点:- 根据非索引字段,或者索引字段中的部分词条查找时,只能全表扫描。

倒排索引:
        优点:- 根据词条搜索、模糊搜索时,速度非常快
        缺点:- 只能给词条创建索引,而不是字段
                   - 无法根据字段做排序


2) mysql与elasticsearch

       elasticsearch是面向文档存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为json格式后存储在elasticsearch中。而Json文档中往往包含很多的字段(Field),类似于数据库中的列。
       索引(Index),就是相同类型的文档的集合。因此,我们可以把索引当做是数据库中的表。
       数据库的表会有约束信息,用来定义表的结构、字段的名称、类型等信息。因此,索引库中就有映射(mapping),是索引中文档的字段约束信息,类似表的结构约束。


MySQLElasticsearch说明
TableIndex索引(index),就是文档的集合,类似数据库的表(table)
RowDocument文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式
ColumnField字段(Field),就是JSON文档中的字段,类似数据库中的列(Column)
SchemaMappingMapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema)
SQLDSLDSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD
  • Mysql:擅长事务类型操作,可以确保数据的安全和一致性
  • Elasticsearch:擅长海量数据的搜索、分析、计算

3)安装es、kibana

  • 1)下载ElasticSearch7.10.1镜像
docker pull elasticsearch:7.10.1
  • 2)创建配置文件夹
mkdir -p /usr/soft/elasticsearch/config
mkdir -p /usr/soft/elasticsearch/data
mkdir -p /usr/soft/elasticsearch/plugins
mkdir -p /usr/soft/elasticsearch/logs
  • 3)配置ip信息和文件夹权限
echo "http.host: 0.0.0.0" > /usr/soft/elasticsearch/config/elasticsearch.yml
chmod -R 775 /usr/soft/elasticsearch
  • 4)启动容器并配置端口映射与目录映射
docker run -id --name elasticsearch \
    -e "cluster.name=ly-elastic" \       	//设置集群名称
    -e "http.host=0.0.0.0" \				//监听的地址,可以外网访问
    -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \	//内存大小
    -e "discovery.type=single-node" \		//非集群模式
    -e http.cors.enabled=true \
    -e http.cors.allow-origin="*" \
    -e http.cors.allow-headers=X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization \
    -e http.cors.allow-credentials=true \
  //挂载逻辑卷,绑定es的数据目录
    -v /usr/soft/elasticsearch/data:/usr/share/elasticsearch/data \  
    -v /usr/soft/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
  //挂载逻辑卷,绑定es的插件目录
    -v /usr/soft/elasticsearch/plugins:/usr/share/elasticsearch/plugins \ 
 //挂载逻辑卷,绑定es的日志目录
    -v /usr/soft/elasticsearch/logs:/usr/share/elasticsearch/logs \  
    --privileged \				//授予逻辑卷访问权
    --hostname elasticsearch \
    -p 9200:9200 \				//端口映射配置
    -p 9300:9300 \
elasticsearch:7.10.1

  • 1)下载Kibana镜像
docker pull kibana:7.10.1
  • 2)创建配置文件夹
mkdir -p  /usr/soft/kibana/config
  • 3)创建并修改kibana.yml文件
# 根据自己实际IP修改elasticsearch地址
server.name: kibana
server.host: "0"
elasticsearch.hosts: [ "http://192.168.200.128:9200" ]
xpack.monitoring.ui.container.elasticsearch.enabled: true
  • 4)启动容器
docker run -d \
  --name=kibana \
  -p 5601:5601 \
  -v /usr/soft/kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml \
  kibana:7.10.1
  //访问 http://ip:5601

4)分词器

分词器是在创建倒排索引时对文档分词,在用户搜索时,对输入的内容分词

ElasticSearch 内置分词器

  • Standard Analyzer - 默认分词器,按词切分,小写处理
  • Simple Analyzer - 按照非字母切分(符号被过滤), 小写处理
  • Stop Analyzer - 小写处理,停用词过滤(the,a,is)
  • Whitespace Analyzer - 按照空格切分,不转小写
  • Keyword Analyzer - 不分词,直接将输入当作输出
  • Patter Analyzer - 正则表达式,默认\W+(非字符分割)
  • Language - 提供了30多种常见语言的分词器

IK分词器

       IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包,是一个基于Maven构建的项目,具有60万字/秒的高速处理能力,支持用户词典扩展定义。
https://github.com/medcl/elasticsearch-analysis-ik
       IK分词器可以用ik_max_word(最细切分,细粒度的分析)和ik_smart(智能切分,粗粒度的分词)两种方式,分词粒度不同。


  • 离线安装ik插件(推荐)
# 1、进入/var/lib/docker/volumes/es-plugins/_data/
cd /var/lib/docker/volumes/es-plugins/_data/
# 2、新建文件目录 ik 并且进入
mkdir ik
cd ik

# 3、解压elasticsearch-analysis-ik-7.10.1.zip
yum -y install unzip 
unzip elasticsearch-analysis-ik-7.10.1.zip

# 4、重启容器
docker restart elasticsearch

# 查看es日志
docker logs -f elasticsearch

# 重启 Kibana
docker restart kibana

扩展词词典、停用词词典

1)打开IK分词器config目录:
2)IKAnalyzer.cfg.xml配置文件内容添加:
<?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">ext.dic</entry>
         <!--用户可以在这里配置自己的扩展停止词字典-->
        <entry key="ext_stopwords">stopword.dic</</entry>
        <!--用户可以在这里配置远程扩展字典 -->
        <!-- <entry key="remote_ext_dict">words_location</entry> -->
        <!--用户可以在这里配置远程扩展停止词字典-->
        <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

3)新建一个 ext.dic,添加扩展词
4)新建一个stopword.dic,添加停用词
5)重启elasticsearch 

二、索引库的CRUD

mapping是对索引库中文档的约束,常见的mapping属性包括:

  • type:字段数据类型,常见的简单类型有:
    • 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)
    • 数值:long、integer、short、byte、double、float、
    • 布尔:boolean
    • 日期:date
    • 对象:object
  • index:是否创建索引,默认为true
  • analyzer:使用哪种分词器
  • properties:该字段的子字段

1)创建索引库和映射

PUT /索引库名称
{
  "mappings": {
    "properties": {
      "字段名":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "字段名2":{
        "type": "keyword",
        "index": "false"
      },
      // ...略
    }
  }
}

2)查询索引库

GET /索引库名称

3)修改索引库

索引库一旦创建,无法修改mapping。但是却允许添加新的字段到mapping中,因为不会对倒排索引产生影响。

PUT /索引库名/_mapping
{
  "properties": {
    "新字段名":{
      "type": "integer"
    }
  }
}

4)删除索引库

DELETE /索引库名

三、DSL操作文档

1)新增文档

POST /索引库名/_doc/文档id
{
    "字段1": "值1",
    "字段2": "值2",
    "字段3": {
        "子属性1": "值3",
        "子属性2": "值4"
    }
    // ...
}

2)查询文档

GET /索引库名称/_doc/文档id

3)修改文档

  • 全量修改      - 根据指定的id删除文档
                        - 新增一个相同id的文档
PUT /索引库名/_doc/文档id
{
    "字段1": "值1",
    "字段2": "值2",
    // ... 略
}

  • 增量修改
POST /索引库名/_update/文档id
{
    "doc": {
         "字段名": "新的值",
    }
}

4)删除文档

DELETE /索引库名/_doc/文档id

四、RestAPI

ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。官方文档地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html

其中的Java Rest Client又包括两种: - Java Low Level Rest Client
                                                            - Java High Level Rest Client


1)RestClient操作索引库

初始化RestAPI
在elasticsearch提供的API中,与elasticsearch一切交互都封装在一个名为RestHighLevelClient的类中,必须先完成这个对象的初始化,建立与elasticsearch的连接。

  • 1)引入es的RestHighLevelClient依赖
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
  • 2)SpringBoot默认的ES版本是7.6.2,所以需要覆盖默认的ES版本:
<properties>
    <java.version>1.8</java.version>
    <elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
  • 3)初始化RestHighLevelClient:
public class Test {
    private RestHighLevelClient client;
	// 每一个测试方法执行前初始化客户端
    @BeforeEach
    void setUp() {
        this.client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://ip:9200")
        ));
    }
    // 每一个测试方法执行后初始化客户端
    @AfterEach
    void tearDown() throws IOException {
        this.client.close();
    }
}

创建索引库

  • 1)创建Request对象。因为是创建索引库的操作,因此Request是CreateIndexRequest。
  • 2)添加请求参数,其实就是DSL的JSON参数部分。因为json字符串很长,这里是定义了静态字符串常量MAPPING_TEMPLATE,让代码看起来更加优雅。
  • 3)发送请求,client.indices()方法的返回值是IndicesClient类型,封装了所有与索引库操作有关的方法。
@Test
void createHotelIndex() throws IOException {
    // 1.创建Request对象
    CreateIndexRequest request = new CreateIndexRequest("hotel");
    // 2.准备请求的参数:DSL语句
    request.source(MAPPING_TEMPLATE, XContentType.JSON);
    // 3.发送请求
    client.indices().create(request, RequestOptions.DEFAULT);
}

删除索引库

  • 1)创建Request对象。这次是DeleteIndexRequest对象
  • 2)准备参数。这里是无参
  • 3)发送请求。改用delete方法
@Test
void testDeleteHotelIndex() throws IOException {
    // 1.创建Request对象
    DeleteIndexRequest request = new DeleteIndexRequest("hotel");
    // 2.发送请求
    client.indices().delete(request, RequestOptions.DEFAULT);
}

判断索引库是否存在

  • 1)创建Request对象。这次是GetIndexRequest对象
  • 2)准备参数。这里是无参
  • 3)发送请求。改用exists方法
@Test
void testExistsHotelIndex() throws IOException {
    // 1.创建Request对象
    GetIndexRequest request = new GetIndexRequest("hotel");
    // 2.发送请求
    boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
    // 3.输出
    System.err.println(exists ? "索引库已经存在!" : "索引库不存在!");
}

小结:
索引库操作的基本步骤:

  • 初始化RestHighLevelClient
  • 创建XxxIndexRequest。XXX是Create、Get、Delete
  • 准备DSL( Create时需要,其它是无参)
  • 发送请求。调用RestHighLevelClient#indices().xxx()方法,xxx是create、exists、delete

2)RestClient操作文档

新增文档

  • 1)创建Request对象
  • 2)准备请求参数,也就是DSL中的JSON文档
  • 3)发送请求
@Test
void testAddDocument() throws IOException {
    // 1.根据id查询酒店数据
    Hotel hotel = hotelService.getById(61083L);
    // 2.转换为文档类型
    HotelDoc hotelDoc = new HotelDoc(hotel);
    // 3.将HotelDoc转json
    String json = JSON.toJSONString(hotelDoc);

    // 1.准备Request对象
    IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
    // 2.准备Json文档
    request.source(json, XContentType.JSON);
    // 3.发送请求
    client.index(request, RequestOptions.DEFAULT);
}

查询文档

  • 1)准备Request对象。这次是查询,所以是GetRequest
  • 2)发送请求,得到结果。因为是查询,这里调用client.get()方法
  • 3)解析结果,就是对JSON做反序列化
@Test
void testGetDocumentById() throws IOException {
    // 1.准备Request
    GetRequest request = new GetRequest("hotel", "61082");
    // 2.发送请求,得到响应
    GetResponse response = client.get(request, RequestOptions.DEFAULT);
    // 3.解析响应结果
    String json = response.getSourceAsString();

    HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
    System.out.println(hotelDoc);
}

删除文档

  • 1)准备Request对象,因为是删除,这次是DeleteRequest对象。要指定索引库名和id
  • 2)准备参数,无参
  • 3)发送请求。因为是删除,所以是client.delete()方法
@Test
void testDeleteDocument() throws IOException {
    // 1.准备Request
    DeleteRequest request = new DeleteRequest("索引库名", "id");
    // 2.发送请求
    client.delete(request, RequestOptions.DEFAULT);
}

修改文档

修改的两种方式:

  • 全量修改:本质是先根据id删除,再新增
  • 增量修改:修改文档中的指定字段值

  • 1)准备Request对象。这次是修改,所以是UpdateRequest
  • 2)准备参数。也就是JSON文档,里面包含要修改的字段
  • 3)更新文档。这里调用client.update()方法
@Test
void testUpdateDocument() throws IOException {
    // 1.准备Request
    UpdateRequest request = new UpdateRequest("hotel", "61083");
    // 2.准备请求参数
    request.doc(
        "price", "1000",
        "starName", "五星"
    );
    // 3.发送请求
    client.update(request, RequestOptions.DEFAULT);
}

批量导入文档

批量处理BulkRequest,其本质就是将多个普通的CRUD请求组合在一起发送。

其中提供了一个add方法,用来添加其他请求:


  • 1)创建Request对象。这里是BulkRequest
  • 2)准备参数。批处理的参数,就是其它Request对象,这里就是多个IndexRequest
  • 3)发起请求。这里是批处理,调用的方法为client.bulk()方法
@Test
void testBulkRequest() throws IOException {
    // 批量查询酒店数据
    List<Hotel> hotels = hotelService.list();

    // 1.创建Request
    BulkRequest request = new BulkRequest();
    // 2.准备参数,添加多个新增的Request
    for (Hotel hotel : hotels) {
        // 2.1.转换为文档类型HotelDoc
        HotelDoc hotelDoc = new HotelDoc(hotel);
        // 2.2.创建新增文档的Request对象
        request.add(new IndexRequest("hotel")
                    .id(hotelDoc.getId().toString())
                    .source(JSON.toJSONString(hotelDoc), XContentType.JSON));
    }
    // 3.发送请求
    client.bulk(request, RequestOptions.DEFAULT);
}

  • 小结:
    文档操作的基本步骤:

  • 初始化RestHighLevelClient

  • 创建XxxRequest。XXX是Index、Get、Update、Delete、Bulk

  • 准备参数(Index、Update、Bulk时需要)

  • 发送请求。调用RestHighLevelClient#.xxx()方法,xxx是index、get、update、delete、bulk

  • 解析结果(Get时需要)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值