ElasticSearch

ElasticSearch

一、ES的概述

1. 介绍

    Elasticsearch是一个建立在全文搜索引擎 Apache Lucene™ 基础上的搜索引擎,可以说 Lucene 是当今最先进,最高效的全功能开源搜索引擎框架。它由Shay Banon开发并于2010年发布,现在是由Elasticsearch BV负责维护。Elasticsearch是一个实时分布式和开源的全文搜索和分析引擎。 它可以从RESTful Web服务接口访问,并使用JSON文档来存储数据。它是基于Java编程语言,这使Elasticsearch能够在不同的平台上运行。使用户能够以非常快的速度来搜索非常大的数据量。

2. 优点

  • Elasticsearch是基于Java开发的,这使得它在几乎每个平台上都兼容。
  • Elasticsearch是实时的,换句话说,一秒钟后,添加的文档可以在这个引擎中搜索得到。
  • Elasticsearch是分布式的,这使得它易于在任何大型组织中扩展和集成。
  • 通过使用Elasticsearch中的网关概念,创建完整备份很容易。 与Apache Solr相比,在Elasticsearch中处理多租户非常容易。
  • Elasticsearch提供了RESTful API接口(不管程序用什么语言开发,任何程序都可以访问)
  • Elasticsearch使用JSON对象作为响应,这使得可以使用不同的编程语言调用Elasticsearch服务器。
  • Elasticsearch支持几乎大部分文档类型。

3. 缺点

  • 在需要添加新数据与新字段的时候,如果ElasticSearch进行搜索是可能需要重新修改格式。之前的数据需要重新同步,对数据的管理有很多困难。
  • ES不是数据库,所以如果想要实现联表查询也会变得很麻烦。
  • ES不支持事物。

4. 使用场景

一般被拿来解决一些什么样的问题?

  • 数据库字段太多,查询太慢,索引没有办法再做优化;
  • 数据库一个count就拖死全表; 数据库的limit翻到几十几百万页后实在是太慢;
  • 数据库like实在太慢,每次like整个服务器cpu内存飙高,拖慢整个线上服务;
  • 想要对外/内提供db里的数据的全文检索服务;
  • 提供日志(程序运行)查询功能;

具体的使用场景

  • Stack Overflow(国外的程序异常讨论论坛),IT问题,程序的报错,提交上去,有人会跟你讨论和回答,全文检索,搜索相关问题和答案,程序报错了,就会将报错信息粘贴到里面去,搜索有没有对应的答案;
  • GitHub(开源代码管理),搜索上千亿行代码;
  • 电商网站,检索商品;
  • 日志数据分析,logstash采集日志,ElasticSearch进行复杂的数据分析(ELK技术,elasticsearch+logstash+kibana);

针对多表联查的场景,有两种解决方案:

  • 第一种:确定好要存入和显示的数据,在数据库层面先提前查询好,然后存入ES。例如:有两个表商品表和商品类别表,商品表中的商品类型在数据库中存的是id,往ES中就需要存储一个完整的数据,只需要一个索引即可。这样可以方便查询,两表只需在ES中查询一次即可。
  • 第二种:但是如果存在大批量的修改操作,数据库层面只需要一条sql语句即可修改,而在ES中需要修改多次。针对这种操作,就需要分开存储两个表在ES中(即在ES中开辟两个索引),这样查询数据需要查询两次,修改数据只需要修改一条数据即可。

5. 基本概念

5.1 Index(索引)

    Elastic 会索引所有字段,经过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。
    所以,Elastic 数据管理的顶层单位就叫做 Index(索引)。它是单个数据库的同义词。每个 Index (即数据库)的名字必须是小写。

5.2 Type(类型)

在 Index(索引)中,可以定义一个或多个类型。
类似于 数据库 的 Table,每一种类 型的数据存放在一起。
在这里插入图片描述
保存在某个 Index(索引)下,某种 Type(类型)的一个数据,Document(文档)是JSON格式的,Document 就像是 数据库 中某个 Table 里面每一行的数据,字段就是Document里的属性。

5.3 Document(文档)

保存在某个 Index(索引)下,某种 Type(类型)的一个数据,Document(文档)是JSON格式的,Document 就像是 数据库 中某个 Table 里面每一行的数据,字段就是Document里的属性。

6. 与数据库比较

在这里插入图片描述
在这里插入图片描述

7. 工作原理

一个集群至少有一个节点,而一个节点就是一个 Elasticsearch 进程,节点可以有多个默认索引,如果创建索引,那么索引将会有5个分片(primary shard 又称主分片)构成的,每一个主分片会有一个副本(replica shard 又称复制分片)。
在这里插入图片描述
上图是一个有3个节点的集群,主分片与对应的复制分片都不回在同一个节点内,这样有利于如果某个节点宕机,数据也不至于丢失。

实际上,一个分片就是一个 Lucene 索引,一个包含倒排索引的文件目录,倒排索引的结构使得 Elasticsearch 在不扫描全部文档的情况下,就能检索文档包含的特定关键字。

8. 倒排索引

Elasticsearch 使用的是一种称为倒排索引的结构,采用 Lucene 倒排索引作为底层。

这种结构适用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。

例如,现在有两个文档,每个文档包含如下内容:

# 文档1包含的内容
Study every day, good good up to forever

# 文档2包含的内容
To forever, study every day, good good up

为了创建倒排索引,首先要将每个文档拆分成独立的词(或称为词条或者tokens),然后创建一个包含所有不重复的词条的排序列表,然后列出每个词条出现在哪个文档。
在这里插入图片描述
如果搜索 to forever,只需查看包含每个词条的文档。
在这里插入图片描述
两个文档都匹配,但是第一个文档比第二个文档的匹配程度更高。

如果没有别的条件,这两个包含关键字的文档都将返回。

二、ES的安装部署

我们采用Docker容器来安装ES,首先需要安装Docker。

Docker中每一个容器都是独立运行的,相当于一个独立的linux系统,如果想便捷地修改容器内的文件,我们就需要把容器目录挂载到主机的目录上。容器端口类似,外界无法直接访问容器内部的端口,需要先将容器端口映射到linux主机端口上才能访问。
参考:Docker 安装文档

1. Linux安装Docker

1.1 删除老版本

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

1.2 安装工具包并设置存储库

sudo yum install -y yum-utils

sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

1.3 安装docker引擎

sudo yum install docker-ce docker-ce-cli containerd.io

1.4 启动docker

sudo systemctl start docker

1.5 设置开机启动docker

#1. 检查docker版本
docker -v
#2. 查看docker已有镜像
sudo docker images
#3. 设置docker开机启动
sudo systemctl enable docker

1.6 设置国内镜像仓库

# 创建文件
sudo mkdir -p /etc/docker
# 修改配置, 设置镜像
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://vw9qapdy.mirror.aliyuncs.com"]
}
EOF

# 重启后台线程
sudo systemctl daemon-reload
# 重启docker
sudo systemctl restart docker

2. Docker安装Elasticsearch

2.1 下载镜像文件

# 存储和检索数据
docker pull elasticsearch:7.4.2

2.2 配置挂载数据文件夹

# 创建配置文件目录
mkdir -p /mydata/elasticsearch/config

# 创建数据目录
mkdir -p /mydata/elasticsearch/data

# 将/mydata/elasticsearch/文件夹中文件都可读可写
chmod -R 777 /mydata/elasticsearch/

# 配置任意机器可以访问 elasticsearch
echo "http.host: 0.0.0.0" >/mydata/elasticsearch/config/elasticsearch.yml

2.3. 启动Elasticsearch

命令后面的 \是换行符,注意前面有空格

docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e  "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v  /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2 
  • -p 9200:9200 -p 9300:9300:向外暴露两个端口,9200用于HTTP REST API请求,9300 ES 在分布式集群状态下 ES 之间的通信端口;
  • -e “discovery.type=single-node”:es 以单节点运行
  • -e ES_JAVA_OPTS="-Xms64m -Xmx512m":设置启动占用内存,不设置可能会占用当前系统所有内存
  • -v:挂载容器中的配置文件、数据文件、插件数据到本机的文件夹;
  • -d elasticsearch:7.6.2:指定要启动的镜像
    访问 IP:9200 看到返回的 json 数据说明启动成功。

2.4 设置 Elasticsearch 随Docker启动

# 当前 Docker 开机自启,所以 ES 现在也是开机自启
docker update elasticsearch --restart=always

三、配置图形界面

1. 配置可视化Kibana

1.1 下载镜像文件

# 可视化检索数据
docker pull kibana:7.4.2

1.2 启动可视化Kibana

docker run --name kibana \
-e ELASTICSEARCH_HOSTS=http://192.168.163.131:9200 \
-p 5601:5601 \
-d kibana:7.4.2

-e ELASTICSEARCH_HOSTS=http://192.168.163.131:9200 这里要设置成自己的虚拟机IP地址
浏览器输入192.168.163.131:5601 测试:

在这里插入图片描述

1.3 设置 Kibana 随Docker启动

# 当前 Docker 开机自启,所以 kibana 现在也是开机自启
docker update kibana --restart=always

四、ES使用入门

前面介绍说,Elasticsearch 都是通过 REST API 接口来操作数据的,那么下面接通过几个接口的请求来演示它的使用。

1. _cat 命令

1.1 查看所有节点

GET /_cat/nodes

在这里插入图片描述

1.2 查看ES健康状况

GET /_cat/health

在这里插入图片描述

1.3 查看主节点信息

GET /_cat/master

在这里插入图片描述

1.4 查看所有索引

GET /_cat/indices?v

在这里插入图片描述

2. 索引操作

2.1 查看所有的索引

GET /_cat/indices

在这里插入图片描述

2.2 添加索引

put /ems

在这里插入图片描述

2.3 删除索引

DELETE /ems

在这里插入图片描述

2.4 删除所有的索引(慎用)

DELETE /*

2.5 创建索引以及添加类型

es7去除了文档类型,默认是_doc,创建 ems/emp{name,age,birth}

PUT /ems
{
  "mappings": {
    "properties": {
      "id":{
        "type": "integer"
      },
      "name":{
        "type": "keyword"
      },
      "age":{
        "type": "integer"
      },
      "birth":{
        "type": "date"
      }
    }
  }
}

在这里插入图片描述

2.6 查看创建索引以及索引类型的映射

GET /ems  #可以查看到ems索引的所有信息
GET /ems/_mapping  #可以查看ems索引的映射信息

在这里插入图片描述

3. 文档操作

3.1 插入一条文档

插入一条文档put /索引/_doc/唯一id es7默认数据类型是doc

  • PUT添加文档需要携带id,如果存在该id则会进行修改操作,不存在则进行添加。
  • POST添加文档如果携带id会使用你带的id,不携带id则他会生成一个随机的id。id不存在则进行添加操作,否则进行修改操作。
PUT /ems/_doc/1
{
  "name":"张三",
  "age":24,
  "birth":"2021-04-26"
}

在这里插入图片描述

POST /ems/_doc/
{
  "name":"张三",
  "age":24,
  "birth":"2021-04-26"
}

在这里插入图片描述

3.2 查询文档中的一条记录

get/索引/_doc/文档id

GET /ems/_doc/1

在这里插入图片描述

3.3 根据id删除文档中一条记录

delete /索引/_doc/文档id

DELETE /ems/_doc/1

在这里插入图片描述

3.4 更新文档操作

delete /索引/_doc/文档id

#这种会删除原有数据,重新再次添加,会丢失数据。
POST /ems/_doc/1
{
  "name":"李四"
}

POST /ems/_doc/1
{
  "doc":{
    "name":"李四02"
  }
}

PUT /ems/_doc/1
{
  "doc":{
    "name":"李四03"
  }
}
#这种不会丢失数据,直接在原有的数据上修改
POST /ems/_update/1
{
  "doc":{
    "name":"李四"
  }
}

五、ES检索进阶

下面的请求都是在Kibana dev-tools 操作

1. DSL高级查询

1.1 查询所有的记录

GET /goods/_search
{
  "query": {
    "match_all": {}
  }
}

在这里插入图片描述

1.2 查询所有的记录并排序

GET /goods/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    }
  ]
}

在这里插入图片描述

1.3 分页查询,每页记录数5个

GET /goods/_search
{
  "query": {
    "match_all": {}
  },
  "size": 5,
  "from": 0,
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    }
  ]
}

在这里插入图片描述

1.4 指定查询结果中返回指定字段

GET /goods/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["typeName","id"]
}

在这里插入图片描述

1.5 term 查询(基于关键字进行查询)

注意:text类型会进行分词,es默认使用的对于中文是单字分词器
例如小黑会拆分成小和黑,对于英文是按单词分.keywod、integer、date 不分词

1.5.1 查询出所有typeName包含java

GET /goods/_search
{
  "query": {
    "term": {
      "typeName": {
        "value": "java"
      }
    }
  }
}

在这里插入图片描述

1.6 范围查询价格大于等于100,小于等于200的商品

GET /goods/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 100,
        "lte": 200
      }
    }
  }
}

在这里插入图片描述

1.7 基于关键字的前缀查询,priex ,英文小写匹配大写,汉字只能单字匹配

GET /goods/_search
{
  "query": {
    "prefix": {
      "describe": {
        "value": "effective"
      }
    }
  }
}

在这里插入图片描述

1.8 通配符匹配? 用来匹配一个字符, * 匹配多个字符

GET /goods/_search
{
  "query": {
    "wildcard": {
      "describe": {
        "value": "*java*"
      }
    }
  }
}

在这里插入图片描述

1.9 基于多个id查询

GET /goods/_search
{
  "query": {
    "ids": {
      "values": [1386524724004339714,1386524723681378306]
    }
  }
}

在这里插入图片描述

1.10 模糊查询 fuzzy

GET /goods/_search
{
  "query": {
    "fuzzy": {
      "typeName": "cava"
    }
  }
}

在这里插入图片描述

模糊查询 fuzzy 做大模糊的错误必须在0-2之间
注意:搜索关键词长度为2,不允许存在模糊;如果你的长度在3-5之间允许一次模糊;如果你的长度大于5,最多允许的模糊在0-2之间

1.11 布尔查询 可以组合进行查询

#must:意味着条件都必须满足
#should:满足一个条件即可
#not must:必须不满足任何一个条件

GET /goods/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "typeName": {
              "value": "java"
            }
          }
        },
        {
          "term": {
            "describe": {
              "value": "零基础"
            }
          }
        }
      ]
    }
  }
}

1.12 多字段查询

#1)如果搜索的字段分词,他会对query进行先分词后搜索
#2).如果搜索的字段不分词,他会直接使用query整体进行搜索
#3).建议fields里放入可分词的字段
GET /goods/_search
{
  "query": {
    "multi_match": {
      "query": "java",
      "fields": ["typeName","describe"]
    }
  }
}

在这里插入图片描述

1.13 多字段分词查询

GET /goods/_search
{
  "query": {
    "query_string": {
      "default_field": "describe",
      "query": "冰箱"
    }
  }
}

在这里插入图片描述

1.14 过滤存在指定字段的文档

GET /goods/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match_all": {}
        }
      ],
      "filter": {
        "exists": {
          "field": "describe"
        }
      }
    }
  }
}

在这里插入图片描述

1.15 精确查询-基本数据类型(非文本)

# 查找匹配 price 为 98.0 的数据 非文本推荐使用 term
GET /goods/_search
{
  "query": {
    "match": {
      "price": "98.0"
    }
  }
}

在这里插入图片描述

1.16 模糊查询-文本字符串

# 查找匹配 typeName 包含 二手 的数据
#match即全文检索,对检索字段进行分词匹配,
#会按照响应的评分 _score 排序,原理是倒排索引。
GET /goods/_search
{
  "query": {
    "match": {
      "typeName": "二手"
    }
  }
}

在这里插入图片描述

1.17 精确匹配-文本字符串

# 查找 typeName 为 二手笔记本 的数据。
GET /goods/_search
{
  "query": {
    "match": {
      "typeName": "二手笔记本"
    }
  }
}

在这里插入图片描述

1.18 match_phrase-短语匹配

#将需要匹配的值当成一整个单词(不分词)进行检索
# 这里会检索 typeName 匹配包含短语 笔记本 的数据
GET /goods/_search
{
  "query": {
    "match_phrase": {
      "typeName": "笔记本"
    }
  }
}

在这里插入图片描述

1.19 term-精确检索

#精确查询typeName为java的
GET /goods/_search
{
  "query": {
    "term": {
      "typeName": {
        "value": "java"
      }
    }
  }
}

在这里插入图片描述

term用于精确检索(避免使用 term 查询文本字段) 非文本字段精确匹配推荐用term;

1.20 聚合操作

#聚合操作,搜索typeName中包含空调的所有商品的价格分布以及平均价格
#txt没法聚合 必须加.keyword精确替代
GET /goods/_search
{
  "query": {
    "match": {
      "typeName": "空调"
    }
  },
  "aggs": {
    "priceAgg": {
      "terms": {
        "field": "price",
        "size": 10
      }
    },
    "priceAvg":{
      "avg": {
        "field": "price"
      }
    }
  },
  "size": 0
}

#"aggs": {                               ---聚合关键字
#    "priceAgg": {                       ---聚合名为priceAgg(自定义)
#      "terms": {                        ---聚合类型为term
#        "field": "price",               ---聚合字段为price
#        "size": 10                      ---取聚合后前十个数据
#      }
#    },
#    "priceAvg":{                        ---聚合名为priceAvg(自定义)
#      "avg": {                          ---聚合类型为avg 求平均值
#        "field": "price"                ---聚合字段为price
#      }
#    }
#  },
#  "size": 0                             ---不显示命中结果,只看聚合信息

在这里插入图片描述

1.21 映射操作

1.21.1 创建索引映射
#创建索引并指定属性的映射规则(相当于新建表并指定字段和字段类型)
PUT /my_index
{
  "mappings": {
    "properties": {
      "age":{
        "type": "integer"
      },
      "email": {
        "type": "keyword"
      },
      "name": {
        "type": "text"
      }
    }
  }
}
1.21.2 给已有映射增加字段
#2.给已有映射增加字段
# 这里的 "index": false,表明新增的字段不能被检索。默认是true
PUT /my_index/_mapping
{
  "properties": {
    "employee-id": {
      "type": "keyword",
      "index": false
    }
  }
}

1.21.3 查看映射
#3.查看映射
GET /my_index/_mapping

#查看某一个字段的映射----查看email的映射
GET /my_index/_mapping/field/email

在这里插入图片描述

1.21.4 更新映射

对于已经存在的字段映射,我们不能更新。更新必须创建新的索引,进行数据迁移。
数据迁移方式:迁移方式分为两种,一种是7和7之后去掉type的情况,一种是包含type 迁移的情况。
对于我们的测试数据,是包含 type 的索引 my_index。
现在我们创建新的索引 newindex 并修改一些字段的类型来演示当需要更新映射时的数据迁移操作。

#1. 创建新的映射规则
PUT /newindex
{
  "mappings": {
    "properties": {
      "age":{
        "type": "integer"
      },
      "email": {
        "type": "text"
      },
      "name": {
        "type": "text"
      },
      "gender": {
        "type": "keyword"
      }
    }
  }
}
#2. 进行数据迁移
POST _reindex
{
  "source": {
    "index": "my_index"
  },
  "dest": {
    "index": "newindex"
  }
}

1.22 高亮搜索结果

#查询包含Java编程思想关键字的进行高亮显示
GET /goods/_search
{
  "query": {
    "match_phrase": {
      "describe": "Java编程思想"
    }
  },
  "highlight": {
    "pre_tags": ["<font color='red'>"],
    "post_tags": ["</font>"],
    "fields": {
      "describe": {}
    }
  }
  
}

在这里插入图片描述

1.23 批量查询并修改某个字段的值

#利用script脚本批量进行修改
POST /goods/_doc/_update_by_query
{
  "query": {
    "match_all": {}
  },
  "script": {
        "inline": "ctx._source['typeName'] = 'javc'"
  }
}

在这里插入图片描述

六、ES的分词器

1.1 为什么要是用分词器

Analyzer(分词器)的作用是把一段文本中的词按一定规则进行切分。默认的分词器一般都是针对于英文,

例如:whitespace tokenizer遇到空白字符时分割文本。它会将文本“Quick brown fox!”分割为[Quick,brown,fox!]。

对于中文我们需要安装额外的分词器来进行分词。否则中文会一个字一个字的进行拆分,非常不友好。

1.2 安装IK分词器

1.2.1 下载

IK 分词器属于 Elasticsearch 的插件,所以 IK 分词器的安装目录是 Elasticsearch 的 plugins 目录,在我们使用Docker启动 Elasticsearch 时,已经将该目录挂载到主机的 /mydata/elasticsearch/plugins 目录。
IK 分词器的版本需要跟 Elasticsearch 的版本对应,当前选择的版本为 7.4.2,下载地址为:Github Release 或访问:镜像地址

# 进入挂载的插件目录 /mydata/elasticsearch/plugins
cd /mydata/elasticsearch/plugins

# 安装 wget 下载工具
 yum install -y wget

# 下载对应版本的 IK 分词器(这里是7.4.2)
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip

这里已经在挂载的 plugins 目录安装好了 IK分词器。现在我们进入到 es 容器内部检查是否成功安装。

# 进入容器内部
docker exec -it elasticsearch /bin/bash

# 查看 es 插件目录
ls /usr/share/elasticsearch/plugins

# 可以看到 elasticsearch-analysis-ik-7.4.2.zip

所以我们之后只需要在挂载的目录/mydata/elasticsearch/plugins下进行操作即可。

1.2.2 解压

# 进入到 es 的插件目录
cd /mydata/elasticsearch/plugins

# 解压到 plugins 目录下的 ik 目录
unzip elasticsearch-analysis-ik-7.4.2.zip -d ik

# 删除下载的压缩包
 rm -f elasticsearch-analysis-ik-7.4.2.zip 

# 修改文件夹访问权限`在这里插入代码片`
chmod -R 777 ik/

1.2.3 查看安装的ik插件

# 进入 es 容器内部
docker exec -it elasticsearch /bin/bash

# 进入 es bin 目录
cd /usr/share/elasticsearch/bin

# 执行查看命令  显示 ik
elasticsearch-plugin list

# 退出容器
exit

# 重启 Elasticsearch
docker restart elasticsearch

#重启docker  
systemctl restart docker

1.2.4 测试ik分词器

首先测试默认的分词器

GET /_analyze
{
  "text": "我是王贝塔"
}

在这里插入图片描述
测试ik分词器的效果

GET _analyze
{
   "analyzer": "ik_smart", 
   "text":"我是王贝塔"
}

在这里插入图片描述
这里对于默认词库中没有的词,不会有词语的组合,所以我们可以通过配置自定义词库或远程词库来实现对词库的扩展。

1.2 自定义扩展分词库

现有ik分词器无法将这个词切分成一个关键词,但是又希望某个词成为关键词。例如:我想让王贝塔三个字不分开。
在这里插入图片描述

1.2.1 配置扩展词典

#进入ik的配置目录
cd /mydata/elasticsearch/plugins/ik/config

查看目录结构
在这里插入图片描述

#打开IKAnalyzer.cfg.xml 配置文件
vim IKAnalyzer.cfg.xml

在这里插入图片描述
我们将扩展文件写在ext_dict位置
在这里插入图片描述
因为需要utf-8编码,我们只需要拷贝一个当前文件夹下的文件然后进行修改

cp stopword.dic ext.dic

在这里插入图片描述

#然后在里面添加你需要分词的词语即可
vim ext.dic 

在这里插入图片描述

#之后重启ES服务
docker restart elasticsearch 

再次分词,结果如下:
在这里插入图片描述

同理不需要分词的内容,我们只需按照上面配置配置到stopext.dic中即可。
可在此处配置远程项目地址,实现动态修改。

七、相关配置

1. ES集群配置

在单台ES服务器节点上,随着业务量的发展索引文件慢慢增多,会影响到效率和内存存储问题等。

我们可以采用ES集群,将单个索引的分片到多个不同分布式物理机器上存储,从而可以实现高可用、容错性等。

ES集群中索引可能由多个分片构成,并且每个分片可以拥有多个副本。通过将一个单独的索引分为多个分片,我们可以处理不能在一个单一的服务器上面运行的大型索引,简单的说就是索引的大小过大,导致效率问题。不能运行的原因可能是内存也可能是存储。由于每个分片可以有多个副本,通过将副本分配到多个服务器,可以提高查询的负载能力。

此处我们以三台机器来搭建集群环境。

1.1 设置Elasticsearch挂载目录

#存放配置文件的文件夹
sudo mkdir -p /mydata/elasticsearch/node-1/config
sudo mkdir -p /mydata/elasticsearch/node-2/config
sudo mkdir -p /mydata/elasticsearch/node-3/config

#存放数据的文件夹
sudo mkdir -p /mydata/elasticsearch/node-1/data
sudo mkdir -p /mydata/elasticsearch/node-2/data
sudo mkdir -p /mydata/elasticsearch/node-3/data

#存放运行日志的文件夹
sudo mkdir -p /mydata/elasticsearch/node-1/log
sudo mkdir -p /mydata/elasticsearch/node-2/log
sudo mkdir -p /mydata/elasticsearch/node-3/log
#存放IK分词插件的文件夹
sudo mkdir -p /mydata/elasticsearch/node-1/plugins
sudo mkdir -p /mydata/elasticsearch/node-2/plugins
sudo mkdir -p /mydata/elasticsearch/node-3/plugins
#授予权限
sudo chmod 777 /mydata/elasticsearch/node-1/data
sudo chmod 777 /mydata/elasticsearch/node-2/data
sudo chmod 777 /mydata/elasticsearch/node-3/data

sudo chmod 777 /mydata/elasticsearch/node-1/plugins
sudo chmod 777 /mydata/elasticsearch/node-2/plugins
sudo chmod 777 /mydata/elasticsearch/node-3/plugins

sudo chmod 777 /mydata/elasticsearch/node-1/log
sudo chmod 777 /mydata/elasticsearch/node-2/log
sudo chmod 777 /mydata/elasticsearch/node-3/log

sudo chmod 777 /mydata/elasticsearch/node-1/config
sudo chmod 777 /mydata/elasticsearch/node-2/config
sudo chmod 777 /mydata/elasticsearch/node-3/config

1.2 生成目录结构

.
├── node-1
│   ├── config
│   │   └── elasticsearch.yml
│   ├── data
│   │   └── nodes
│   │       └── 0
│   │           ├── node.lock
│   │           └── _state
│   │               ├── manifest-3.st
│   │               ├── node-3.st
│   │               ├── _o.cfe
│   │               ├── _o.cfs
│   │               ├── _o.si
│   │               ├── segments_11
│   │               └── write.lock
│   ├── log
│   └── plugins
├── node-2
│   ├── config
│   │   └── elasticsearch.yml
│   ├── data
│   │   └── nodes
│   │       └── 0
│   │           ├── node.lock
│   │           └── _state
│   │               ├── manifest-4.st
│   │               ├── node-4.st
│   │               ├── _r.cfe
│   │               ├── _r.cfs
│   │               ├── _r.si
│   │               ├── segments_11
│   │               └── write.lock
│   ├── log
│   └── plugins
└── node-3
    ├── config
    │   └── elasticsearch.yml
    ├── data
    │   └── nodes
    │       └── 0
    │           ├── node.lock
    │           └── _state
    │               ├── manifest-1.st
    │               ├── _n.cfe
    │               ├── _n.cfs
    │               ├── node-1.st
    │               ├── _n.si
    │               ├── segments_11
    │               └── write.lock
    ├── log
    └── plugins


1.3 编写elasticsearch.yml配置文件

#进入es的node-1配置文件中
cd /mydata/elasticsearch/node-1/config/

#创建elasticsearch.yml配置文件
vi elasticsearch.yml

将下列配置添加到elasticsearch.yml中

#集群名称
cluster.name: my-es
#当前该节点的名称
node.name: node-1
#是不是有资格竞选主节点
node.master: true
#是否存储数据
node.data: true
#最大集群节点数
node.max_local_storage_nodes: 3
#给当前节点自定义属性(可以省略)
#node.attr.rack: r1
#数据存档位置
path.data: /usr/share/elasticsearch/data
#日志存放位置
path.logs: /usr/share/elasticsearch/log
#是否开启时锁定内存(默认为是)
#bootstrap.memory_lock: true
#设置网关地址,我是被这个坑死了,这个地址我原先填写了自己的实际物理IP地址,
#然后启动一直报无效的IP地址,无法注入9300端口,这里只需要填写0.0.0.0
network.host: 0.0.0.0
#设置其它结点和该结点交互的ip地址,如果不设置它会自动判断,值必须是个真实的ip地址,设置当前物理机地址,
#如果是docker安装节点的IP将会是配置的IP而不是docker网管ip
network.publish_host: 10.64.57.162
#设置映射端口
http.port: 9200
#内部节点之间沟通端口
transport.tcp.port: 9300
#集群发现默认值为127.0.0.1:9300,如果要在其他主机上形成包含节点的群集,如果搭建集群则需要填写
#es7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点,也就是说把所有的节点都写上
discovery.seed_hosts: ["10.64.57.162:9300","10.64.57.162:9301","10.64.57.162:9302"]
#当你在搭建集群的时候,选出合格的节点集群,有些人说的太官方了,
#其实就是,让你选择比较好的几个节点,在你节点启动时,在这些节点中选一个做领导者,
#如果你不设置呢,elasticsearch就会自己选举,这里我们把三个节点都写上
cluster.initial_master_nodes: ["node-1","node-2","node-3"]
#在群集完全重新启动后阻止初始恢复,直到启动N个节点
#简单点说在集群启动后,至少复活多少个节点以上,那么这个服务才可以被使用,否则不可以被使用,
gateway.recover_after_nodes: 2
#删除索引是是否需要显示其名称,默认为显示
#action.destructive_requires_name: true
#进入es的node-2配置文件中
cd /mydata/elasticsearch/node-2/config

#创建elasticsearch.yml配置文件
vi elasticsearch.yml

将下列配置添加到elasticsearch.yml中

#集群名称
cluster.name: my-es
#当前该节点的名称
node.name: node-2
#是不是有资格竞选主节点
node.master: true
#是否存储数据
node.data: true
#最大集群节点数
node.max_local_storage_nodes: 3
#给当前节点自定义属性(可以省略)
#node.attr.rack: r1
#数据存档位置
path.data: /usr/share/elasticsearch/data
#日志存放位置
path.logs: /usr/share/elasticsearch/log
#是否开启时锁定内存(默认为是)
#bootstrap.memory_lock: true
#设置网关地址,我是被这个坑死了,这个地址我原先填写了自己的实际物理IP地址,
#然后启动一直报无效的IP地址,无法注入9300端口,这里只需要填写0.0.0.0
network.host: 0.0.0.0
#设置其它结点和该结点交互的ip地址,如果不设置它会自动判断,值必须是个真实的ip地址,设置当前物理机地址,
#如果是docker安装节点的IP将会是配置的IP而不是docker网管ip
network.publish_host: 10.64.57.162
#设置映射端口
http.port: 9201
#内部节点之间沟通端口
transport.tcp.port: 9301
#集群发现默认值为127.0.0.1:9300,如果要在其他主机上形成包含节点的群集,如果搭建集群则需要填写
#es7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点,也就是说把所有的节点都写上
discovery.seed_hosts: ["10.64.57.162:9300","10.64.57.162:9301","10.64.57.162:9302"]
#当你在搭建集群的时候,选出合格的节点集群,有些人说的太官方了,
#其实就是,让你选择比较好的几个节点,在你节点启动时,在这些节点中选一个做领导者,
#如果你不设置呢,elasticsearch就会自己选举,这里我们把三个节点都写上
cluster.initial_master_nodes: ["node-1","node-2","node-3"]
#在群集完全重新启动后阻止初始恢复,直到启动N个节点
#简单点说在集群启动后,至少复活多少个节点以上,那么这个服务才可以被使用,否则不可以被使用,
gateway.recover_after_nodes: 2
#删除索引是是否需要显示其名称,默认为显示
#action.destructive_requires_name: true
#进入es的node-2配置文件中
cd /mydata/elasticsearch/node-3/config

#创建elasticsearch.yml配置文件
vi elasticsearch.yml

将下列配置添加到elasticsearch.yml中


#集群名称
cluster.name: my-es
#当前该节点的名称
node.name: node-3
#是不是有资格竞选主节点
node.master: true
#是否存储数据
node.data: true
#最大集群节点数
node.max_local_storage_nodes: 3
#给当前节点自定义属性(可以省略)
#node.attr.rack: r1
#数据存档位置
path.data: /usr/share/elasticsearch/data
#日志存放位置
path.logs: /usr/share/elasticsearch/log
#是否开启时锁定内存(默认为是)
#bootstrap.memory_lock: true
#设置网关地址,我是被这个坑死了,这个地址我原先填写了自己的实际物理IP地址,
#然后启动一直报无效的IP地址,无法注入9300端口,这里只需要填写0.0.0.0
network.host: 0.0.0.0
#设置其它结点和该结点交互的ip地址,如果不设置它会自动判断,值必须是个真实的ip地址,设置当前物理机地址,
#如果是docker安装节点的IP将会是配置的IP而不是docker网管ip
network.publish_host: 10.64.57.162
#设置映射端口
http.port: 9202
#内部节点之间沟通端口
transport.tcp.port: 9302
#集群发现默认值为127.0.0.1:9300,如果要在其他主机上形成包含节点的群集,如果搭建集群则需要填写
#es7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点,也就是说把所有的节点都写上
discovery.seed_hosts: ["10.64.57.162:9300","10.64.57.162:9301","10.64.57.162:9302"]
#当你在搭建集群的时候,选出合格的节点集群,有些人说的太官方了,
#其实就是,让你选择比较好的几个节点,在你节点启动时,在这些节点中选一个做领导者,
#如果你不设置呢,elasticsearch就会自己选举,这里我们把三个节点都写上
cluster.initial_master_nodes: ["node-1","node-2","node-3"]
#在群集完全重新启动后阻止初始恢复,直到启动N个节点
#简单点说在集群启动后,至少复活多少个节点以上,那么这个服务才可以被使用,否则不可以被使用,
gateway.recover_after_nodes: 2
#删除索引是是否需要显示其名称,默认为显示
#action.destructive_requires_name: true

1.4 创建镜像

#创建镜像
docker run -e ES_JAVA_OPTS="-Xms512m -Xmx512m" -d -p 9200:9200 -p 9300:9300 -v /mydata/elasticsearch/node-1/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /mydata/elasticsearch/node-1/plugins:/usr/share/elasticsearch/plugins -v /mydata/elasticsearch/node-1/data:/usr/share/elasticsearch/data -v /mydata/elasticsearch/node-1/log:/usr/share/elasticsearch/log --name es-node-1 elasticsearch:7.4.2



docker run -e ES_JAVA_OPTS="-Xms512m -Xmx512m" -d -p 9201:9201 -p 9301:9301  -v /mydata/elasticsearch/node-2/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml  -v /mydata/elasticsearch/node-2/plugins:/usr/share/elasticsearch/plugins   -v /mydata/elasticsearch/node-2/data:/usr/share/elasticsearch/data -v /home/elasticsearch/node-2/log:/usr/share/elasticsearch/log --name es-node-2 elasticsearch:7.4.2




docker run -e ES_JAVA_OPTS="-Xms512m -Xmx512m" -d -p 9202:9202 -p 9302:9302  -v /mydata/elasticsearch/node-3/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /mydata/elasticsearch/node-3/plugins:/usr/share/elasticsearch/plugins -v /mydata/elasticsearch/node-3/data:/usr/share/elasticsearch/data/ -v /mydata/elasticsearch/node-3/log:/usr/share/elasticsearch/log --name es-node-3 elasticsearch:7.4.2

1.5 验证集群结果

http://10.64.57.162:9201/_cat/nodes?pretty

在这里插入图片描述

注意:如果需要配置IK中文分词器,需要在每个节点的plugs文件夹下下载一个IK分词器,请参照IK分词器配置过程。

2. ElasticSearch分页搜索时会出现一下异常信息

Caused by: ElasticsearchException[Elasticsearch exception [type=illegal_argument_exception, reason=Result window is too large, from + size must be less than or equal to: [10000] but was [10285]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting.]] 

这是因为ES默认支持的最大条数就是10000条,深度分页导致总条数超过10000,就会报这个错。我们可以根据业务进行相关调整。

###########配置ES默认支持的最大条数为15000############
PUT goods/_settings
{
  "index.max_result_window":15000
}

3. 配置集群中遇到的问题

OpenJDK 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.
{"type": "deprecation", "timestamp": "2021-04-28T02:26:18,981Z", "level": "WARN", "component": "o.e.d.c.s.Settings", "cluster.name": "my-es", "node.name": "node-1", "message"                                    : "[transport.tcp.port] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version." }
{"type": "deprecation", "timestamp": "2021-04-28T02:26:19,056Z", "level": "WARN", "component": "o.e.d.c.s.Settings", "cluster.name": "my-es", "node.name": "node-1", "message"                                    : "[node.max_local_storage_nodes] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major v                                    ersion." }
{"type": "server", "timestamp": "2021-04-28T02:26:19,185Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "node name                                     [node-1], node ID [hmFzzrNjRLu4kdFhMNYYgg], cluster name [my-es]" }
{"type": "server", "timestamp": "2021-04-28T02:26:19,185Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "version[7.                                    4.2], pid[1], build[default/docker/2f90bbf7b93631e52bafb59b3b049cb44ec25e96/2019-10-28T20:40:44.881551Z], OS[Linux/4.18.0-240.22.1.el8_3.x86_64/amd64], JVM[AdoptOpenJDK/OpenJ                                    DK 64-Bit Server VM/13.0.1/13.0.1+9]" }
{"type": "server", "timestamp": "2021-04-28T02:26:19,186Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "JVM home [                                    /usr/share/elasticsearch/jdk]" }
{"type": "server", "timestamp": "2021-04-28T02:26:19,186Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "JVM argume                                    nts [-Xms1g, -Xmx1g, -XX:+UseConcMarkSweepGC, -XX:CMSInitiatingOccupancyFraction=75, -XX:+UseCMSInitiatingOccupancyOnly, -Des.networkaddress.cache.ttl=60, -Des.networkaddress                                    .cache.negative.ttl=10, -XX:+AlwaysPreTouch, -Xss1m, -Djava.awt.headless=true, -Dfile.encoding=UTF-8, -Djna.nosys=true, -XX:-OmitStackTraceInFastThrow, -Dio.netty.noUnsafe=tr                                    ue, -Dio.netty.noKeySetOptimization=true, -Dio.netty.recycler.maxCapacityPerThread=0, -Dio.netty.allocator.numDirectArenas=0, -Dlog4j.shutdownHookEnabled=false, -Dlog4j2.disa                                    ble.jmx=true, -Djava.io.tmpdir=/tmp/elasticsearch-5133258163291923947, -XX:+HeapDumpOnOutOfMemoryError, -XX:HeapDumpPath=data, -XX:ErrorFile=logs/hs_err_pid%p.log, -Xlog:gc*,                                    gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m, -Djava.locale.providers=COMPAT, -Des.cgroups.hierarchy.override=/, -Xms512m, -Xmx512m, -Di                                    o.netty.allocator.type=unpooled, -XX:MaxDirectMemorySize=268435456, -Des.path.home=/usr/share/elasticsearch, -Des.path.conf=/usr/share/elasticsearch/config, -Des.distribution                                    .flavor=default, -Des.distribution.type=docker, -Des.bundled_jdk=true]" }

{"type": "server", "timestamp": "2021-04-28T02:26:24,126Z", "level": "INFO", "component": "o.e.x.s.a.s.FileRolesStore", "cluster.name": "my-es", "node.name": "node-1", "messa                                    ge": "parsed [0] roles from file [/usr/share/elasticsearch/config/roles.yml]" }
{"type": "server", "timestamp": "2021-04-28T02:26:24,543Z", "level": "INFO", "component": "o.e.x.m.p.l.CppLogMessageHandler", "cluster.name": "my-es", "node.name": "node-1",                                     "message": "[controller/105] [Main.cc@110] controller (64 bit): Version 7.4.2 (Build 473f61b8a5238b) Copyright (c) 2019 Elasticsearch BV" }
{"type": "server", "timestamp": "2021-04-28T02:26:24,909Z", "level": "DEBUG", "component": "o.e.a.ActionModule", "cluster.name": "my-es", "node.name": "node-1", "message": "U                                    sing REST wrapper from plugin org.elasticsearch.xpack.security.Security" }
{"type": "server", "timestamp": "2021-04-28T02:26:25,321Z", "level": "INFO", "component": "o.e.d.DiscoveryModule", "cluster.name": "my-es", "node.name": "node-1", "message":                                     "using discovery type [zen] and seed hosts providers [settings]" }
{"type": "server", "timestamp": "2021-04-28T02:26:25,928Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "initialize                                    d" }
{"type": "server", "timestamp": "2021-04-28T02:26:25,928Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "starting .                                    .." }
{"type": "server", "timestamp": "2021-04-28T02:26:26,023Z", "level": "INFO", "component": "o.e.t.TransportService", "cluster.name": "my-es", "node.name": "node-1", "message":                                     "publish_address {10.64.57.162:9300}, bound_addresses {0.0.0.0:9300}" }
{"type": "server", "timestamp": "2021-04-28T02:26:26,029Z", "level": "INFO", "component": "o.e.b.BootstrapChecks", "cluster.name": "my-es", "node.name": "node-1", "message":                                     "bound or publishing to a non-loopback address, enforcing bootstrap checks" }
ERROR: [1] bootstrap checks failed
[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
{"type": "server", "timestamp": "2021-04-28T02:26:26,035Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "stopping .                                    .." }
{"type": "server", "timestamp": "2021-04-28T02:26:26,047Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "stopped" }
{"type": "server", "timestamp": "2021-04-28T02:26:26,047Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "closing ..                                    ." }
{"type": "server", "timestamp": "2021-04-28T02:26:26,058Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "closed" }
{"type": "server", "timestamp": "2021-04-28T02:26:26,060Z", "level": "INFO", "component": "o.e.x.m.p.NativeController", "cluster.name": "my-es", "node.name": "node-1", "messa                                    ge": "Native controller process has stopped - no new native processes can be started" }

解决办法:通过查看日志docker logs es-node-1 找到了

max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

这句话的意思是虚拟内存空间太小了,至少需要262144这么多,意思是elasticsearch拥有的内存权限太小,得扩大

#切换到root用户修改配置sysctl.conf
vi /etc/sysctl.conf


#添加下面配置:
vm.max_map_count=655360
#并执行命令
sysctl -p

然后重启ES的服务即可。

4. ES中的对象数组类型处理问题

对于对象以及数组类型的测试,ES中不会识别,会默认会将其进行扁平化处理。可能会造成数据查询的差异。

4.1 创建映射规则以及添加数据

PUT my-index-000001/_doc/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

4.2 构建查询条件测试

下面这个查询需要搜索Alice Smith 很显然 我们两个条件都不满足,应该查询不出来数据,但是ES不认识数组和对象类型,会将其进行扁平化处理。

GET my-index-000001/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "user.first": "Alice" }},
        { "match": { "user.last":  "Smith" }}
      ]
    }
  }
}

在这里插入图片描述

4.3 解决办法

#先删除映射
DELETE my-index-000001

#重新添加映射
PUT my-index-000001
{
  "mappings": {
    "properties": {
      "user": {
        "type": "nested" 
      }
    }
  }
}

再次添加数据进行测试

#添加数据
PUT my-index-000001/_doc/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}
#查询first为Alice last为Smith的用户
GET my-index-000001/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "user.first": "Alice" }},
        { "match": { "user.last":  "Smith" }}
      ]
    }
  }
}

在这里插入图片描述
此时就不会查询出结果了。

注意:数组和对象类型必须用nested类型,否则ES会将其进行扁平化处理,会造成查询的数据错乱。

八、测试与传统数据库检索对比

前提:事先准备好1W条数据表方便进行测试,本次测试数据13000条。

1. Oracle的全文索引

1.1 设置Oracle的全文索引

--设置词法分析器
--BEGIN
--ctx_ddl.create_preference ('goods_chinese_lexer', 'chinese_lexer');
--END;


--全文索引创建格式
--CREATE INDEX "indexname" ON "tablename" ("fieldname")
--INDEXTYPE IS "CTXSYS"."CONTEXT" PARAMETERS ('lexer cms_chinese_lexer sync(on commit)');

--为数据库表GOODS创建全文索引
--CREATE INDEX "GOODS_DESCRIBE_INDEX" ON "GOODS" ("DESCRIBE")
--INDEXTYPE IS "CTXSYS"."CONTEXT" PARAMETERS ('lexer cms_chinese_lexer sync(on commit)');

1.2 查询描述中包含java关键字的数据

select * from Goods where contains(describe,'java')>0

在这里插入图片描述

测试结果:最快11ms,最慢350ms。

2. ES的全文检索

前提:需要先将Oracle中的数据导入到ES中,13000条数据大概需要20多秒
在这里插入图片描述

2.1 查询描述中包含java关键字的数据

GET /goods/_search
{
  "query": {
    "match_phrase": {
      "describe": "java"
    }
  }
}

在这里插入图片描述

测试结果:最快0ms,最慢2ms。

代码测试删除1.3w数据 时间 3313ms 3s左右
代码测试添加1.3w数据 时间 60881ms 60s左右
代码测试更新1.3w数据 时间 2876ms 2s左右

九、ES数据同步方案

1. 同步双写

这是一种最为简单的方式,在将数据写到数据库时,同时将数据写到ES,实现数据的双写。

  • 优点:业务逻辑简单,好实现。
  • 缺点:硬编码,有需要写入数据库的地方都需要添加写入ES的代码;业务强耦合;存在双写失败丢数据风险;性能较差:本来数据库的性能就不是很高,再加写一个ES,系统的性能必然会下降。

说明:双写失败风险,包括以下3种:
ES系统不可用;应用系统和ES之间的网络故障;应用系统重启,导致系统来不及写入ES等。针对这种情况,有数据强一致性要求的,就必须双写放到事物中来处理,但是一旦用上事物,则性能下降更加明显。

2. 异步双写(MQ方式)

针对第一种同步双写的性能和数据丢失问题,可以考虑引入MQ,从而形成了异步双写的方案:由于MQ的性能基本比oracle高出一个数量级,所以性能可以得到显著的提高。

  • 优点:性能高;不存在丢数据问题。
  • 缺点:依然存在业务强耦合:依然存在复杂度增加:系统中增加了mq的代码,;可能存在时延问题:程序的写入性能提高了,但是由于MQ的消费可能由于网络或其它原因导致用户写入的数据不一定可以马上看到,造成延时。

3. 异步双写(Worker方式)

上面两种方案中都存在硬编码问题,也就是有任何对mysq进行增删改查的地方要么植入ES代码,要么替换为MQ代码,代码的侵入性太强。

如果对实时性要求不高的情况下,可以考虑用定时器来处理,具体步骤如下:

数据库的相关表中增加一个字段为timestamp的字段,任何crud操作都会导致该字段的时间发生变化;原来程序中的CURD操作不做任何变化;增加一个定时器程序,让该程序按一定的时间周期扫描指定的表,把该时间段内发生变化的数据提取出来;逐条写入到ES中。

  • 优点:不改变原来代码,没有侵入性、没有硬编码;没有业务强耦合;不改变原来程序的性能;Worker代码编写简单不需要考虑增删改查。
  • 缺点:时效性较差,由于定时器工作周期不可能设在秒级,所以实时性没有上面2中好;对数据库有一定的轮询压力,一种改进方法是将轮询放到压力不大的重库上。

4. 线程池实现异步双写

可以利用Completefuture来实现异步任务,让数据库的crud操作和ES操作异步执行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值