ElasticSearch详解

一、引言

1.1 海量数据

在海量数据中执行搜索功能时,如果使用MySQL,效率太低。

1.2 全文检索

在海量数据中执行搜索功能时,如果使用MySQL,效率太低。

1.3 高亮显示

将搜索关键字,以红色的字体展示。

二、ES概述

2.1 ES的介绍

  • ES是一个使用Java语言并且基于Lucene编写的搜索引擎框架,他提供了分布式的全文搜索功能,提供了一个统一的基于RESTful风格的WEB接口,官方客户端也对多种语言都提供了相应的API。

  • Lucene:Lucene本身就是一个搜索引擎的底层。

  • 分布式:ES主要是为了突出他的横向扩展能力。

  • 全文检索将一段词语进行分词,并且将分出的单个词语统一的放到一个分词库中,在搜索时,根据关键字去分词库中检索,找到匹配的内容。(倒排索引)

  • RESTful风格的WEB接口:操作ES很简单,只需要发送一个HTTP请求,并且根据请求方式的不同,携带参数的同,执行相应的功能。

  • 应用广泛:Github.com,WIKI,Gold Man用ES每天维护将近10TB的数据。

2.2 ES的由来

ES回忆时光

2.3 ES和Solr

  • Solr在查询死数据时,速度相对ES更快一些。但是数据如果是实时改变的,Solr的查询速度会降低很多,ES的查询的效率基本没有变化

  • Solr搭建基于需要依赖Zookeeper来帮助管理。ES本身就支持集群的搭建,不需要第三方的介入。

  • 最开始Solr的社区可以说是非常火爆,针对国内的文档并不是很多。在ES出现之后,ES的社区火爆程度直线上升,ES的文档非常健全。

  • ES对现在云计算和大数据支持的特别好。

2.4 倒排索引

将存放的数据,以一定的方式进行分词,并且将分词的内容存放到一个单独的分词库中。

当用户去查询数据时,会将用户的查询关键字进行分词。

然后去分词库中匹配内容,最终得到数据的id标识。

根据id标识去存放数据的位置拉取到指定的数据。

检索的时候 先将检索的内容分词,然后去分词库匹配 拿到匹配数据的索引 再根据索引去数据存储的位置 拿到匹配的数据

elk elasticsearch (存储分析检索数据) + logstash (采集数据) + kibana (展示数据的图形化界面)

倒排索引

2.5基本概念

2.5.1 Index (索引)

  • 动词 ,相当于Mysql 中的insert
  • 名词, 相当于Mysql 中的 Database

2.5.2 Type (类型)

在Index(索引)中 ,可以定义一个或多个类型。

类似Mysql 中的table ,每一种类型的数据放在一起

2.5.3 Document (文档)

保存在某个索引(index)下,某种类型(type)的一个数据(Document) ,文档是json 格式的,Document 就像是Mysql 中某个table里面的内容。

三、 ElasticSearch安装

3.1 安装ES&Kibana 不建议

1).下载镜像文件

docker pull elasticsearch:7.4.2
docker pull kibana:7.4.2

 2).创建挂载目录

mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
echo "http.host: 0.0.0.0"  >>  /mydata/elasticsearch/config/elasticsearch.yml 

 3).启动容器

docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms512m -Xmx1024m" \
-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
​
#如果 启动elasticsearch  失败,  查看  日期  docker logs 容器id   查看  某容器的日志
#docker logs 容器id
# 需要 给 elasticsearch 目录授权
#chmod -R 777 /mydata/elasticsearch/     给整个目录授权   可读可写可执行
#在启动容器 应该就好了

 4).启动kibana

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

        测试访问

  • http://192.168.5.202:9200     如访问不到 查看日志
  • http://192.168.5.202:5601     如访问不到 查看日志 看容器内 kibana.yml 中 ip 地址是否正确

3.1.1 使用docker-compose的方式安装(推荐)

  • mkdir -p /mydata/elasticsearch/config
  • mkdir -p /mydata/elasticsearch/data

echo "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml

version: "3.1"
services:
 elasticsearch:
  image: daocloud.io/library/elasticsearch:7.4.2
  restart: always
  container_name: elasticsearch
  environment:
     - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
  volumes:
     - /opt/docker_elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
     - /opt/docker_elasticsearch/data:/usr/share/elasticsearch/data
     - /opt/docker_elasticsearch/plugins:/usr/share/elasticsearch/plugins
  ports:
     - 9200:9200
     - 9300:9300
 kibana:
   image: daocloud.io/library/kibana:7.4.2
   restart: always
   container_name: kibana
   ports:
     - 5601:5601
   environment:
     - elasticsearch_url=http://192.168.40.102:9200
   depends_on:
     - elasticsearch

9300 端口 为 es 集群间组件的通信端口,9200 端口为浏览器访问的http 协议resetful 端口

记得 给文件夹授权 授权之后 再docker-compose up

chmod 777 config

chmod 777 data

如果访问 http://192.168.40.102:9200 失败,查看日志是否vm内存配置过小elasticsearch启动时遇到的错误。

问题翻译过来就是:elasticsearch用户拥有的内存权限太小,至少需要262144;

解决办法1:在 /etc/sysctl.conf文件最后添加一行

vm.max_map_count=262144 把宿主机内存配大一些

解决办法2:启动时 指定内存 咱们的安装方法 就是启动时 指定内存

3.2 安装IK分词器

ik 分词器 就是 elasticsearch 的一个插件

注意:

如果在线安装太慢的话,可以先下载下来压缩包然后手动安装

把下载的压缩包解压到挂载目录 plugins 下的 ik 文件夹里,

cd /mydata/elasticsearch/plugins

mkdir ik

cd ik 先把压缩包放在 ik 目录中

unzip elasticsearch-analysis-ik-7.4.2.zip

解压完之后 可以把压缩包删掉:rm -rf *.zip

进入容器内部 bin 目录下 执行elasticsearch-plugin list 查看插件列表有没有 ik

ik_max_wordik 分词器的 一种分词方式还有别的分词方式。

校验IK分词器

四、 ElasticSearch基本操作

4.1 ES的结构 --- 操作es 之前 先了解es 的结构

4.1.1 索引Index,分片和备份

  • ES的服务中,可以创建多个索引。

  • 每一个索引默认被分成5片存储。

  • 每一个分片都会存在至少一个备份分片。

  • 备份分片默认不会帮助检索数据,当ES检索压力特别大的时候,备份分片才会帮助检索数据。

  • 备份的分片必须放在不同的服务器中。

理解:

索引index是es中最大的数据存储单位 ,和mysql 的区别是 一个索引(index)中可以存海量(几亿条)数据 ,如果我们要在几亿条数据中检索出几条想要的数据 效率会很低 所以 es 提供了 一种对索引进行分片的机制 ,ES 天然支持集群,在集群服务器中 ES 把一个索引进行分片 放在不同的服务器上 如下图 例如 有一亿条数据 分成两个分片 每个分片上有5000万条数据 这样做的好处 一是 提高查询速度 二是 提高数据的存储量,另外 为了保证数据的安全 每个主分片会有备份分片 主分片和备份分片在不同的服务器上 , 比如 主分片2 挂掉了 在 ES服务1 上面 还有 主分片2的备份分片 ,这样在一定程度上保证了数据的安全性 避免数据的丢失。但是,如果当前集群中只有一台es服务器,那么,这台服务器上 放的都是主分片,没有备份分片,什么时候扩展了集群中的另一台服务器才会存放备份分片。

索引分片备份

4.1.2 类型 Type

一个索引下,可以创建多个类型。

Ps:根据版本不同,类型的创建也不同。

es7 版本 不推荐使用type ,但是还是能用,到后面的es 版本 就不能再使用type 了

类型

4.1.3 文档 Doc

一个类型下,可以有多个文档。这个文档就类似于MySQL表中的多行数据。

文档

4.1.4 属性 Field

一个文档中,可以包含多个属性。类似于MySQL表中的一行数据存在多个列。

属性

4.2 操作ES的RESTful语法

GET请求:

  • http://ip:port/_cat/nodes:查看所有节点 在kibana 中使用 GET _cat/nodes

  • http://ip:port/_cat/health:查看es 健康状况

  • http://ip:port/_cat/master:查看主节点

  • http://ip:port/_cat/indices:查看所有索引 相当于 show databases;

  • http://ip:port/index:查询索引信息 GET book 相当于查看 数据库表结构

  • http://ip:port/index/type/doc_id:查询指定的文档信息

注意 咱们用的是 es 7 直接使用type 的 话 会给出警告信息 ,咱们使用 _doc 代替 type

比如 查询指定文档信息 GET book/_doc/1 查询 book 索引中 id 为1 的文档信息

GET book/_doc/2

注意 咱们用的是 es 7 直接使用type 的 话 会给出警告信息 ,咱们使用 _doc 代替 type

比如 查询指定文档信息 GET book/_doc/1 查询 book 索引中 id 为1 的文档信息

GET book/_doc/2
  • POST请求:

    • http://ip:port/index/type/doc_id

# 指定文档id的添加操作             
# 如果索引还未创建 还可以创建索引 
POST book/_doc/2 
{  
    "name":"西游记",    
    "author":"吴承恩" 
}

http://ip:port/index/type/_search:查询文档,可以在请求体中添加json字符串来代表查询条件。

    # 查询操作
    POST book/_search
    {
      "query":{
        "match": {
          "name": "西游记"
        }
      }
    }

http://ip:port/index/type/doc_id/_update:修改文档,在请求体中指定json字符串代表修改的具体信息     注意 带 _update 的 修改 json 格式 里需要加 doc 对比本文档下面的案例说明。 

    POST book/_update/1      # 修改操作
    { 
      "doc":{
        "name":"大奉打更2人",    
        "author":"xxxxx2"
      }
    }

PUT请求:

http://ip:port/index:创建一个索引,也可以在请求体中指定索引的信息,类型,结构。

  PUT book2  # 创建一个叫 book2 的索引   执行第二次会报错
​  # 添加或修改文档    第一次是添加(同样 索引不存在 也会创建索引) 后面再执行是修改
  PUT book3/_doc/1
  {
    "name":"java"
  }

http://ip:port/index/type/_mappings:代表创建索引时,指定索引文档存储的属性的信息 

        DELETE请求:

        http://ip:port/index:删除索引

  • DELETE book2     # 删除book2 索引

         http://ip:port/index/type/doc_id:删除指定的文档

  • ​DELETE book/_doc/2      删除索引book中  id 为2 的文档

4.3 索引的操作

4.3.1 创建一个索引

语法如下先创建一个最简单的先不指定他的结构化数据。

# 创建一个索引
PUT /person
{
  "settings": {
    "number_of_shards": 5,      # 分片 数5   
    "number_of_replicas": 1    # 备份
  }
}

 4.3.2 查看索引信息

语法如下:去management 中查看索引信息

Primaries 意思是分片

Replicas 意思是备份

Health健康状态黄色表示不太健康,因为现在es 集群中只有一台服务器备份分片没有地方存放,所以是黄色的健康状态,如果集群中有多台服务器,备份分片就可以存储在别的服务器上,避免这台服务器挂掉数据丢失问题。

点索引的名字可以查看索引的详细信息。

# 查看索引信息
GET /person

查看信息

4.3.3 删除索引

语法如下

# 删除索引
DELETE /person

4.4 ES中Field可以指定的类型

 字符串类型

  • text:一把被用于全文检索。 将当前Field进行分词。

  • keyword:当前Field不会被分词。

数值类型

  • scaled_float:根据一个long和scaled来表达一个浮点型,long-345,scaled-100 -> 3.45

  • half_float:精度比float小一半。

  • float:3.402823e+38 ~ 1.401298e-45(e+38表示是乘以10的38次方,e-45表示乘以10的负45次方),占用4个字节

  • double:1.797693e+308~ 4.9000000e-324 (e+308表示是乘以10的308次方,e-324表示乘以10的负324次方)占用8个字节

  • byte:取值范围为-128~127(-2的7次方到2的7次方-1),占用1个字节

  • short:取值范围为-32768~32767(-2的15次方到2的15次方-1),占用2个字节

  • integer:取值范围为-2147483648~2147483647(-2的31次方到2的31次方-1),占用4个字节

  • long:取值范围为-9223372036854774808~922337203685477480(-2的63次方到2的63次方-1),占用8个字节

时间类型

  • date类型,针对时间类型指定具体的格式

布尔类型

  • boolean类型,表达true和false

二进制类型

  • binary类型暂时支持Base64 encode string

范围类型

  • long_range:赋值时,无需指定具体的内容,只需要存储一个范围即可,指定gt,lt,gte,lte
  • integer_range:同上
  • double_range:同上
  • float_range:同上
  • date_range:同上
  • ip_range:同上

经纬度类型

  • geo_point:用来存储经纬度的

ip类型

  • ip:可以存储IPV4或者IPV6

其他的数据类型参考官网:Field datatypes | Elasticsearch Guide [7.6] | Elastic

4.5 创建索引并指定数据结构

语法如下

# 创建索引,指定数据结构
PUT /book
{
  "settings": {
    # 分片数
    "number_of_shards": 5,
    # 备份数
    "number_of_replicas": 1
  },
  # 指定数据结构
  "mappings": {
    # 类型 Type       es 7  可以把这个删了
    "novel": {
      # 文档存储的Field
      "properties": {
        # Field属性名
        "name": {
            # 类型
          "type": "text",
            # 指定分词器 # 在对这个属性做分词的时候 使用 ik分词器
          "analyzer": "ik_max_word",
            # 指定当前Field可以被作为查询的条件 如果为false 则不能作为查询条件
          "index": true ,
            # 当前field是否需要额外存储    一般设置为false 即可  不需要额外存储
          "store": false 
        },
        "author": {
            # keyword 也算是字符串类型 
          "type": "keyword"
        },
        "count": {
          "type": "long"
        },
        "on-sale": {
          "type": "date",
           # 时间类型的格式化方式 
          "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
        },
        "descr": {
          "type": "text",
            # 在对这个属性做分词的时候 使用 ik分词器
          "analyzer": "ik_max_word"
        }
      }
    }
  }
}

4.6 文档的操作

文档在ES服务中的唯一标识,_index_type_id三个内容为组合,锁定一个文档,操作是添加还是修改。

4.6.1 新建文档

自动生成_id

# 添加文档,自动生成id      不推荐这种自动生成的id 
POST /book/_doc
{
  "name": "盘龙",
  "author": "我吃西红柿",
  "count": 100000,
  "on-sale": "2000-01-01",
  "descr": "山重水复疑无路,柳暗花明又一村"
}

手动指定_id

# 添加文档,手动指定id     推荐使用
PUT /book/_doc/1
{
  "name": "红楼梦",
  "author": "曹雪芹",
  "count": 10000000,
  "on-sale": "1985-01-01",
  "descr": "一个是阆苑仙葩,一个是美玉无瑕"
}

4.6.2 修改文档

覆盖式修改

# 修改文档   覆盖式修改  如果没有指定某个属性 这个属性会被覆盖掉  覆盖没了
PUT /book/novel/1
{
  "name": "红楼梦",
  "author": "曹雪芹",
  "count": 4353453,
  "on-sale": "1985-01-01",
  "descr": "一个是阆苑仙葩,一个是美玉无瑕"
}

doc修改方式

# 修改文档,基于doc方式       不会覆盖之前的内容 指定哪一个属性 修改哪一个属性
POST /book/novel/1/_update      # 7 之前的写法
{
  "doc": {
     # 指定上需要修改的field和对应的值
    "count": "1234565"
  }
}


# 现在 都这样写
POST book/_update/1
{
  "doc":{
    "name": "斗破苍穹"
  }
}

4.6.3 删除文档

根据id删除

# 根据id删除文档
DELETE book/_doc/1      # 删除id 为1 的文档

4.6.4 补充

在kibana 可视化界面中可以看到 创建的索引信息

五、Java操作ElasticSearch

5.1 Java连接ES

创建springboot工程

导入依赖

这里注意 springboot版本 默认了es 的 一些版本,需要咱们自己统一定义 要不然会有版本冲突 所以 在pom 文件中 统一 es 版本

     <properties>
        <java.version>1.8</java.version>
        <elasticsearch.version>7.4.2</elasticsearch.version>
    </properties>
​
<dependencies>
     <!-- elasticsearch -->
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>7.4.2</version>
        </dependency>
    
     <!--    elasticsearch 高阶API-->
       <dependency>
            <!--导入 es  的 高阶 api  来 操作 es
                要进行配置
                如果使用spsringdata 操作es  配置会比较简单 只需要在配置文件指定es 的地址就好了
                我们是自己配的 所以自己对es 做配置
            -->
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.4.2</version>
        </dependency>
​
    <!--        3. junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
​
    <!--        4. lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.22</version>
        </dependency>
</dependencies>

创建配置类,测试连接ES

package com.ymk.esdemo.config;
​
import org.apache.http.HttpHost;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
@Configuration
public class ElasticSearchConfig {
    //RequestOptions   这个类 主要封装了 访问 ES 的  一些头信息   一些 设置信息
​
    public static final RequestOptions COMMON_OPTIONS;
    static {
        // 请求设置项
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
​
//        builder.addHeader("Authorization","Bearer"+TOKEN);
//        builder.setHttpAsyncResponseConsumerFactory(new HttpAsyncResponseConsumerFactory
//                .HeapBufferedResponseConsumerFactory(30*1024*1024*1024));
​
        COMMON_OPTIONS = builder.build();
    }
​
​
    @Bean
    public RestHighLevelClient esRestClient(){
        RestClientBuilder builder = null;
​
        builder = RestClient.builder(new HttpHost("192.168.5.205",9200,"http"));
​
        RestHighLevelClient client = new RestHighLevelClient(builder);
        //RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.56.10", 9200, "http")));
​
        return client;
    }
}
​
​
// 测试 能不能得到 RestHighLevelClient
    @SpringBootTest
class EsdemoApplicationTests {
    @Resource
    private RestHighLevelClient client;
​
    @Test
    void contextLoads() {
        System.out.println(client);
    }
}    

5.2 Java操作索引

5.2.1 创建索引

代码如下

   
    /**
     * 索引的创建
     *
     * */
        /**
         * {
         *  "properties":{
         *      "name":{
         *          "type":"text"
         *      },
         *      "age":{
         *          "type":"integer"
         *      },
         *      "birthday":{
         *          "type":"date",
         *          "format":"yyyy-MM-dd"
         *      }
         *  }
         *
         * }
         *
         * */
    @Test
    public void demo1() throws IOException {
        // 创建索引
        String index = "person";
        //1. 准备关于索引的settings
        Settings.Builder settings = Settings.builder()
                .put("number_of_shards", 3)
                .put("number_of_replicas", 1);
        //2. 准备关于索引的结构mappings
        XContentBuilder mappings = JsonXContent.contentBuilder()
                .startObject()        // 和 endObject 成对出现
                .startObject("properties") 
                .startObject("name")
                .field("type","text")
                .endObject()
                .startObject("age")
                .field("type","integer")
                .endObject()
                .startObject("birthday")
                .field("type","date")
                .field("format","yyyy-MM-dd")
                .endObject()
                .endObject()
                .endObject();
​
​
        //3. 将settings和mappings封装到一个Request对象
        // 不同的操作 准备的request 对象不一样 与下文对比
        CreateIndexRequest request = new CreateIndexRequest(index)
                .settings(settings)
                .mapping(mappings);//  如果是6版本 还需要在这个方法指定type                      //.mapping(type,mappings)
​
        //4. 通过client对象去连接ES并执行创建索引
        // 通过client 对象 把上面准备的 request 对象 发到es执行  
        CreateIndexResponse resp = client.indices().create(request, RequestOptions.DEFAULT);
​
        //5. 输出
        System.out.println("resp:" + resp.toString());
​
    }

5.2.2 检查索引是否存在

代码如下

     /**
     * 判断 索引是否存在
     * */
    @Test
    public void demo2() throws IOException {
        //1. 准备request对象
        String index = "person";
        GetIndexRequest request = new GetIndexRequest(index);
        request.indices();
​
        //2. 通过client去操作
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
​
​
        //3. 输出
        System.out.println(exists);
    }

5.2.3 删除索引

代码如下

@Test
public void delete() throws IOException {
    //1. 准备request对象
    DeleteIndexRequest request = new DeleteIndexRequest();
    request.indices(index);
​
    //2. 通过client对象执行
    AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
​
    //3. 获取返回结果
    System.out.println(delete.isAcknowledged());
}

5.3 Java操作文档

5.3.1 添加文档操作

代码如下

添加fastjson 依赖,  jackson 也可以 用法大致一样 

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.31</version>
        </dependency>

     
// 创建实体类
 @Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Person {
​
    private Integer id;
​
    private String name;
​
    private Integer age;
​
    @JSONField(format = "yyyy-MM-dd")     // fastjson 转换对象时  对日期类型字段的 格式转换
    private Date birthday;
​
}
// 测试方法    
 /**
     * 添加文档
     *
     * */
    @Test
    public void demo4() throws IOException {
        String index = "person";
​
            //1. 准备一个json数据
            Person person = new Person(1,"张三"
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值