ElasticSearch基础

1. 介绍

ElasticSearch,简称ES,是java开发的一个开源的、高扩展的、分布式的全文搜索引擎服务器
所以说它是一个单独的软件,可以提供跟百度、谷歌一样的搜索功能
在这里插入图片描述

官网地址:https://www.elastic.co/cn/

1. 为什么使用ES

传统的数据库查询,假如现在有一个商品表
在这里插入图片描述

需求1:从 title 获取包含“手机”的数据
select * from goods where title like ‘%手机%’
这个sql存在的问题:

  • 使用like时,以‘%’开头,会导致索引失效,这时候全表扫描,效率低
    而且,如果表中数据太多,比如:1亿条,假设1秒钟查询十万,也需要1000秒,速度慢

需求2:从 title 获取包含“华为手机”的数据
select * from goods where title like ‘%华为手机%’,这个sql根本就查不出来

总结,使用数据库查询

  • 效率低
  • 功能弱

ES能解决这些的问题

2. 两种索引

假设有一本古诗集,包含

《静夜思》
窗前明月光,疑是地上霜。
举头望明月,低头思故乡。

《水调歌头》
明月几时有?把酒问青天。
不知天上宫阙,今夕是何年?
.....

《月下独酌四首》
花间一壶酒,独酌无相亲。
举杯邀明月,对影成三人。
.....

需求:找出这本书中包含“明月”的古诗

1. 正向索引

搜索逻辑:先看《静夜思》里有没有“明月”,有就记录,再看《水调歌头》里有没有“明月”,有就记录。。。。
就这样一首一首的判断下去,很明显效率很慢

2. 反向索引

又称:倒排索引,按照规则,对文本内容进行分词,拆分出不同的词条(term)
比如:床前明月光,就可能分成:床前、明月、光、月光 等词
所以它最终的索引结构:
在这里插入图片描述

这样就能根据“明月”快速的查询到对应古诗
总结:倒排索引,对文档内容进行分词,最终得出词条和文档唯一标示(文档id)的对应关系

3. ES的存储和搜索原理

在这里插入图片描述

ES中存在文档、索引库着两个概念

  • 文档经过分词后存到索引库
    • ES中的文档是json
  • 索引库存储着词条跟文档的对应关系,并向用户提供搜索服务

倒排索引建立的过程:

idtitle
1华为 电信3G手机
2三星 移动4G手机
  • id=1 的数据第一个存进来,分词后

    • 在这里插入图片描述
  • 然后 id=2 的数据进来,分词后:三星、移动、4G、手机

    • 三星、移动、4G是新增的词条,手机是已存在的词条
    • 在这里插入图片描述

就这样,文档以倒排索引的方式存放到索引库
同时也会对词条进行处理,形成树形结构,有点儿类似数据库中跟某个字段建立索引,目的是提高查询速度

查询时,如果用户输入“华为手机”

  • 先分词为:华为、手机
  • 根据这两个词条分别查询
  • 最后汇总结果,反馈用户

4. ES和Mysql区别

  • mysql有事务,ES没有
  • ES没有外键,如果数据一致性要求高,还是要用Mysql

总结:mysql存储数据,ES搜索数据

2. 安装

ES下载地址:https://www.elastic.co/cn/downloads/past-releases#elasticsearch

在这里插入图片描述

官网下载的慢,华为开源镜像:https://repo.huaweicloud.com/elasticsearch/
在这里插入图片描述

  1. 下载完成后,把压缩包上传到虚拟机,我这里上传到了 /home/soft 目录

在这里插入图片描述

  1. 执行:tar -zxvf elasticsearch-7.8.0-linux-x86_64.tar.gz 进行解压,我这里也是解压到当前目录

在这里插入图片描述

  1. 修改配置,切换到:/home/soft/elasticsearch-7.8.0/config 目录

在这里插入图片描述

编辑 elasticsearch.yml

# ES的集群名称,默认是elasticsearch,建议修改为一个有意义的名字
cluster.name: my-es
# 节点名
node.name: node-1
# 暴露一个IP,供外网访问
network.host: 192.168.56.107
# http访问的端口号
http.port: 9200
# 配置集群时,用来选举master
cluster.initial_master_nodes: ["node-1"]

# 注意:上方配置“:”后都有空格

默认 ES 启动占用 1G 内存,可以通过编辑 config 目录下得 jvm.options 文件修改**(非必须)**

在这里插入图片描述

  1. 执行:/home/soft/elasticsearch-7.8.0/bin/ 下的 elasticsearch文件,启动
    在这里插入图片描述

会发现报错,因为安全问题,Elasticsearch 不允许root用户运行,所以要创建一个新的用户
5. Centos7增加新用户

[root@localhost bin]# useradd javasm
[root@localhost bin]# passwd javasm
更改用户 javasm 的密码 。
新的 密码:

给新创建的用户授权,

 # 设置 elasticsearch-7.8.0 文件见的所有者是 fengxiansheng
 chown -R javasm:javasm /home/soft/elasticsearch-7.8.0

结果:
在这里插入图片描述

另外,新用户在系统中可创建的文件、进程数、内存等太小,修改下面的配置文件

vi /etc/security/limits.conf
# 文件末尾加上下面 2 行
javasm soft nofile 65536
javasm hard nofile 65536

vi /etc/security/limits.d/20-nproc.conf
# 文件末尾加上下面 3 行
javasm soft nofile 65536
javasm hard nofile 65536
*          hard    nproc     4096

vi /etc/sysctl.conf
# 文件末尾加上下面 1 行
vm.max_map_count=655360
# 使配置生效,命令行执行:
sysctl -p
  1. 重新启动,注意需要关闭防火墙
查看防火墙状态:	firewall-cmd --state
关闭防火墙:			systemctl stop firewalld
开机禁用防火墙:	systemctl disable firewalld.service

切换到 javasm 用户:su javasm,
切换到 /home/soft/elasticsearch-7.8.0 目录,执行:bin/elasticsearch,重新启动ES
在这里插入图片描述

注意:如果启动不成功,重启虚拟机
浏览器访问:
在这里插入图片描述

后台启动:bin/elasticsearch -d

3. Kibana安装

kibana 是 ES的一个可视化平台,用来搜索、查看存储在ES中的数据,可以通过各种图表进行高级数据分析
下载地址:https://repo.huaweicloud.com/kibana/7.8.0/

  1. 上传到 /home/soft 目录

在这里插入图片描述

  1. 执行:tar -xzf kibana-7.8.0-linux-x86_64.tar.gz,进行解压

在这里插入图片描述

  1. 修改配置:
vi kibana-7.8.0-linux-x86_64/config/kibana.yml

#文件末尾加上
# http 访问端口
server.port: 5601
# 暴露一个IP,供外网访问
server.host: 192.168.56.107
# ES 的访问地址
elasticsearch.hosts: ["http://192.168.56.107:9200"]
# 请求ES的超时时间,单位:毫秒,默认值:30000
elasticsearch.requestTimeout: 99999
  1. 启动 Kibana,切换到:/home/soft/kibana-7.8.0-linux-x86_64 目录,执行:bin/kibana

在这里插入图片描述

提示不建议使用 root 用户启动,如果非要用root,加上 –allow-root 参数
注意:启动会很慢,耐心等待,如果等的时间太长,建议调大虚拟机内存
在这里插入图片描述

浏览器访问:http://192.168.56.107:5601/
在这里插入图片描述
在这里插入图片描述

然后可以看到:
在这里插入图片描述

4. 核心概念

操作ES之前需要了解几个概念

  • 文档,document
    • ES中的最小数据单位,json格式,一个documet相当于数据库,表中的一条数据
  • 映射,mapping
    • 定义每个字段的类型,相当于数据库中的表结构
  • 索引,index
    • ES存储数据的地方,一个index就相当于一个数据库表

5. 使用

1. 操作索引

1. 使用postman

  1. 添加索引,必须使用 put 请求,比如:192.168.56.107:9200/goods_indes

在这里插入图片描述

  1. 查询索引,使用 get 请求,只需要把 put 改成 get 就行

在这里插入图片描述

如果有想查询多个索引,逗号分隔,比如:192.168.56.107:9200/goods_index,goods_index2
_all:查询所有索引
在这里插入图片描述

  1. 删除索引,使用 delete 请求

在这里插入图片描述

  1. 关闭索引,表示不想删除也不想让外界访问,post 请求

使用 _close ,比如:
在这里插入图片描述

  1. 打开索引,也是 post 请求,使用_open

在这里插入图片描述

2. 使用Kibana

# 查询所有索引,会返回很多默认索引
GET _all
# 创建索引
PUT person
# 查询索引
GET person
# 关闭索引
POST person/_close
# 打开索引
POST person/_open
# 删除索引
DELETE person

在这里插入图片描述

2. 操作映射–Mapping

之前说过 mapping 中定义字段的类型,所以先了解一下ES中的数据类型

1. 数据类型

  • 字符串
    • text:对内容进行分词,得出多个词条
      • 比如:“华为手机”分词后,得到两个词条:华为、手机
      • 一般存储:网页内容、文章、日志等
    • keyword:不会对内容进行分词,将内容当作一个词条
      • 一般存储:电话、邮编、邮箱
  • 数值
    • 在这里插入图片描述

    • scaled_float 缩放类型的的浮点数

      • ⽐如价格只需要精确到分,缩放因⼦为100,price=57.34,存起来就是5734
  • 布尔,boolean
  • 日期类型,date
  • 数组:[],被一对儿中括号扩起来
  • 对象:{},被一对儿大括号扩起来

2. 操作映射

相当于定义一个表的结构,我们使用Kibana操作

  1. 创建映射
# 1. 创建索引
PUT hero

# 2. 创建mapping, 定义两个字段 
PUT hero/_mapping
{
  "properties":{
    "name":{
      "type":"keyword"
    },
    "skill":{
      "type":"text"
    }
  }
}

查询索引可以看到,添加的 mapping
在这里插入图片描述

也可以直接查询 mapping

GET hero/_mapping

在这里插入图片描述

也可以在创建索引的同时,添加映射
先删除 hero这个索引,然后执行:

# 创建索引,并添加映射
PUT hero
{
  "mappings": {
    "properties": {
      "name":{
        "type":"keyword"
      },
      "skill":{
        "type":"text"
      }
    }
  }
}

在这里插入图片描述

  1. 添加字段
# 添加字段
PUT hero/_mapping
{
  "properties":{
    "skill_num":{
      "type":"integer"
    }
  }
}

在这里插入图片描述

3. 操作文档

  1. 添加文档
# 添加文档
POST hero/_doc
{
  "name":"亚瑟",
  "skill":"从天而降大宝剑",
  "skill_num":3
}

这时候 ID 自动生成
**加粗样式
**
也可以添加时,指定id

# 添加文档
POST hero/_doc/1
{
  "name":"吕布",
  "skill":"心中的黑暗,在无限膨胀",
  "skill_num":3
}

在这里插入图片描述

  1. 查询文档
# 查询单个文档
GET hero/_doc/1

在这里插入图片描述

# 查询一个不存在的文档
GET hero/_doc/2

在这里插入图片描述

查询所有文档

# 查询所有文档
GET hero/_search

在这里插入图片描述

  1. 修改文档
# 修改文档,当id存在时:修改,不存在时:新增
POST hero/_doc/1
{
  "name":"吕布",
  "skill":"心中的黑暗,在无限膨胀,哈哈",
  "skill_num":3
}

在这里插入图片描述

注意:就算只更新一个字段也需要传整个对象,否则不传的字段会当成空处理

# 修改文档,不传 skill_num,再次查询,skill_num 就没有了
POST hero/_doc/1
{
  "name":"吕布",
  "skill":"心中的黑暗,在无限膨胀,哈哈"
}

在这里插入图片描述

当然也可以只修改某个字段

# 只修改指定字段
POST hero/_update/1
{
  "doc": {
    "skill_num":4
  }
}

再次查询:
在这里插入图片描述

  1. 删除文档
# 根据id删除
DELETE hero/_doc/60NWooABIuAyT-o-ys22

在这里插入图片描述

删除不存在的数据返回:not_found

# 删除不存在的数据
DELETE hero/_doc/2

在这里插入图片描述
=done&style=none&taskId=ucef98852-a445-4484-b9fb-bf74fe5f2e0&title=&width=402)

4. 分词器

1. 默认分词器

分词器:将一段文字,按照规则,切分成多个词语的工具,比如:华为手机,分词后:华为、手机
ES内置了很多分词器:

  • standard : 默认分词器,按词切分,小写处理
  • simple: 按照非字母切分(符号被过滤),小写处理,会去除数字
  • whitespace: 按照空格切分,不转小写
  • stop: 小写处理,停用词过滤(the,a,is)
  • keyword: 不分词,直接将输入当作输出

但是这些我们不用,只做了解,因为它们对中文很不友好,比如:

# 默认分词器
POST _analyze
{
  "analyzer": "standard",
  "text": ["中华任命共和国"]
}

在这里插入图片描述

2. IK分词器

ik分词器是java语言开发的一个中文分词器
下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.8.0
注意:下载的包一定要和 ElasticSearch 的版本一致
在这里插入图片描述

下载后,上传到虚拟机的 /home/soft/elasticsearch-7.8.0/plugins/ik 目录(需要先创建好)
然后执行:** unzip elasticsearch-analysis-ik-7.8.0.zip**
在这里插入图片描述

将 config 目录下的文件 复制到 /home/soft/elasticsearch-7.8.0/config/ 目录
执行:cp -R config/ /home/soft/elasticsearch-7.8.0/config/*
最后重启 ES 和 Kibana
在这里插入图片描述

3. 基础使用

使用IK有两种方式:ik_smart、ik_max_word

# ik_smart,粗粒度划分,结果会少一些
GET _analyze
{
  "analyzer": "ik_smart",
  "text": ["中华人民共和国"]
}

在这里插入图片描述

# ik_max_word,细粒度划分,结果会多一些
GET _analyze
{
  "analyzer": "ik_max_word",
  "text": ["中华人民共和国"]
}

在这里插入图片描述

4. 操作文档

  1. 删除之前创建的 hero,重新创建,因为之前用的是默认的 standard 分词器
# 创建索引,指定使用IK分词器
PUT hero
{
  "mappings": {
    "properties": {
      "name":{
        "type":"keyword"
      },
      "skill":{
        "type":"text",
        "analyzer": "ik_max_word"
      }
    }
  }
}
  1. 添加文档
# 添加3个文档
PUT hero/_doc/1
{
  "name":"亚瑟",
  "skill":"王者背负,王者审判,王者不可阻挡!"
}
PUT hero/_doc/2
{
  "name":"妲己",
  "skill":"羁绊是什么东西"
}
PUT hero/_doc/3
{
  "name":"吕布",
  "skill":"无法得到那就将他彻底毁掉"
}
  1. 查询文档

查询分为两种:对关键字分词、不对关键字分词
不对关键字分词,使用“term”

# 通过数据的关键字查询文档,不对关键字分词,相当于数据库查询的“=”
GET hero/_search
{
  "query": {
    "term": {
      "name": {
        "value": "妲己"
      }
    }
  }
}

在这里插入图片描述

但是查询 skill 字段
在这里插入图片描述

可以看到是没有结果了,原因有两个:

  1. 没有对查询的关键字进行分词
  2. 我们存储的数据中没有“羁绊是什么东西”这个词条

这时候把查询关键字修改为“羁绊”就能看到结果
在这里插入图片描述

对关键字分词,使用“match”

# 对关键分词后再查询
GET hero/_search
{
  "query": {
    "match": {
      "skill": "羁绊是什么"
    }
  }
}

在这里插入图片描述

可以看到查出来了,这是因为“羁绊是什么”分词后的结果是:
在这里插入图片描述

5. Java API

1. 操作索引

创建一个普通的Maven项目
在这里插入图片描述

pom.xml

<!-- elasticsearch API -->
<dependency>
  <groupId>org.elasticsearch</groupId>
  <artifactId>elasticsearch</artifactId>
  <version>7.8.0</version>
</dependency>
<!-- elasticsearch 的客户端 -->
<dependency>
  <groupId>org.elasticsearch.client</groupId>
  <artifactId>elasticsearch-rest-high-level-client</artifactId>
  <version>7.8.0</version>
</dependency>
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.60</version>
</dependency>
  1. 创建索引
public static void main(String[] args) throws IOException {
    //1. 创建ES连接客户端
    RestHighLevelClient client = new RestHighLevelClient(
        RestClient.builder(new HttpHost("192.168.56.109",9200,"http")));
    //2. 创建索引请求
    CreateIndexRequest request = new CreateIndexRequest("wangzhe");//fengxiansheng是索引名
    //2.1 mapping
    String mapping = "{\n" +
        "  \"properties\":{\n" +
        "    \"name\":{\n" +
        "      \"type\":\"keyword\"\n" +
        "    },\n" +
        "    \"skill\":{\n" +
        "      \"type\":\"text\"\n" +
        "    }\n" +
        "  }\n" +
        "}";
    //设置 mapping 的格式是json
    request.mapping(mapping, XContentType.JSON);
    //2.2 使用client创建索引
    CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
    //3. 返回true,表示创建成功
    System.out.println(response.isAcknowledged());
    //4. 关闭客户端
    client.close();
}

运行后,在 Kibana 上查看结果
在这里插入图片描述

  1. 查询索引
private static void getIndex() throws IOException {
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        new HttpHost("192.168.56.109",9200,"http")));
    GetIndexRequest request = new GetIndexRequest("wangzhe");//wangzhe 是索引名
    GetIndexResponse response = client.indices().get(request, RequestOptions.DEFAULT);
    //获取结果
    Map<String, MappingMetadata> mappings = response.getMappings();
    for(String key : mappings.keySet()){
        System.out.println(key + "----" + mappings.get(key).sourceAsMap());
    }
    client.close();
}

结果
在这里插入图片描述

  1. 删除索引
private static void deleteIndex() throws IOException {
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        new HttpHost("192.168.56.109",9200,"http")));
    DeleteIndexRequest request = new DeleteIndexRequest("wangzhe");//wangzhe 是索引名
    AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
    System.out.println(response.isAcknowledged());
    client.close();
}
  1. 判断索引是否存在
private static void existIndex() throws IOException {
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        new HttpHost("192.168.56.109",9200,"http")));
    GetIndexRequest request = new GetIndexRequest("wangzhe");//fengxiansheng 是索引名
    boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
    // true:存在,false:不存在
    System.out.println(exists);
    client.close();
}

2. 操作文档

创建一个 Hero 类

public class Hero {
    private String name;

    private String skill;

    //get、set 省略
}
  1. 把对象,存到ES中
private static void createDoc() throws IOException {
    Hero hero = new Hero();
    hero.setName("张飞");
    hero.setSkill("英雄比普通人更不正常");

    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        new HttpHost("192.168.56.109",9200,"http")));

    //组装添加文档的请求
    IndexRequest request = new IndexRequest("hero")//hero 是索引名
        .id("4")//指定id
        .source(JSONObject.toJSONString(hero), XContentType.JSON);//数据内容json格式
    //执行添加文档的命令
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);
    //返回结果
    System.out.println(response);
    client.close();
}

结果:
在这里插入图片描述

Kibana 上查询:
在这里插入图片描述

  1. 修改文档
    修改文档跟添加文档的代码一样,id存在就更新,id不存在就添加
private static void updateDOc() throws IOException {
    Hero hero = new Hero();
    hero.setName("张飞");
    hero.setSkill("英雄比普通人更不正常66666");

    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        new HttpHost("192.168.56.109",9200,"http")));

    //组装添加文档的请求
    IndexRequest request = new IndexRequest("hero")//hero 是索引名
        .id("4")//指定id
        .source(JSONObject.toJSONString(hero), XContentType.JSON);//数据内容json格式
    //执行添加文档的命令
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);

    //		  使用UpdateRequest,更新指定字段
    //        Map<String,Object> map = new HashMap<String, Object>();
    //        map.put("skill","英雄比普通人更不正常哈哈哈哈");
    //        UpdateRequest updateRequest = new UpdateRequest("hero","4");
    //        updateRequest.doc(map);
    //        UpdateResponse response = client.update(updateRequest, RequestOptions.DEFAULT);

    //返回结果
    System.out.println(response);
    client.close();
}

Kibana 上查询:
在这里插入图片描述

  1. 根据ID查询
private static void getById() throws IOException {
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        new HttpHost("192.168.56.109",9200,"http")));

    //创建请求对象,两个参数,分别是:索引名、文档ID
    GetRequest request = new GetRequest("hero","1");
    GetResponse response = client.get(request, RequestOptions.DEFAULT);
    System.out.println(response);
    System.out.println(response.getSourceAsMap());
    client.close();
}

结果:
在这里插入图片描述

  1. 删除文档
private static void deleteDoc() throws IOException {
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        new HttpHost("192.168.56.107",9200,"http")));
    //创建请求对象,两个参数,分别是:索引名、文档ID
    DeleteRequest request = new DeleteRequest("person","5");
    DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
    System.out.println(response);
    client.close();
}

结果:
在这里插入图片描述

  • 12
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值