Elasticsearch实战与原理解析读书笔记


typora-copy-images-to: img
typora-root-url: img

Elasticsearch实战与原理解析读书笔记

Elasticsearch的简介

功能

  • 搜索
  • 分析
  • 存储数据

特点

  • 分布式
  • 零配置
  • 易装易用
  • 自动发现
  • 索引自动分片
  • 索引副本机制
  • RESTful风格接口
  • 多数据源
  • 自动搜索负载

Lucene简介

Lucene默认实现了布尔操作,模糊查询,分组查询

Lucene的模块
  1. Analisis模块:主要负责词法分析及语言处理。
  2. Index模块:主要负责索引的创建工作。
  3. Store模块:主要负责索引的读和写,主要对文件的一些操作,其主要目的是抽象出和平台文件系统无关的存储。
  4. QueryParser模块:主要负责语法分析,把查询语句生成Lucene底层可以识别的条件。
  5. Search模块:主要负责对索引的搜索工作。
  6. Similarity模块:主要负责相关性打分和排序的实现。
Lucene的专业术语
  1. Term
  2. 词典
  3. 倒排表
  4. 正向信息

数据搜索方式

  • 结构化数据

    • 顺序扫描

    • 关键词精确匹配

    • 关键词部分匹配

      • 较为复杂的关键词部分匹配,通常需要借助like关键字来实现,如左匹配关键词“TAL”时需要使用like “TAL%”,右匹配关键词“TAL”时需要使用like “%TAL”,完全模糊匹配关键词“TAL”时需要使用like “%TAL%”
  • 非结构化数据

    • 顺序扫描(效率低)
    • 全文检索

搜索引擎的工作原理分为两个阶段

  1. 网页数据爬取和索引阶段
    • 网络爬虫
    • 数据预处理
    • 数据索引
  2. 搜索阶段
    • 搜索关键词
    • 输入内容预处理
    • 搜索关键词查询

倒排索引

倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件。

倒排索引中的名词介绍

  • 词条:是索引里面最小的存储和查询单元。
  • 词典:是词典的集合
  • 倒排表:

Elasticsearch的核心概念

  1. Node:节点
  2. Cluster:集群
    • Green:所有的主分片和副本分片都可以正常工作
    • Yellow:所有的主分片都可以正常工作,但至少有一个副本分片不可以正常工作
    • Red:至少有一个分片的主分片以及它的全部副本分片都不可正常工作
  3. Shards:默认为一个索引创建5个主分片,并分别为每个主分片创建一个副本
  4. Replicas
  5. Index:索引,由一个或多个分片组成。在使用索引时,需要通过索引名称在集群内进行唯一标识。
  6. Type
  7. Document
  8. Setting
  9. Mapping
  10. Analyzer

Elasticsearch中的索引Index如果对标关系型数据库中的数据库database的话,则表table与类型type对应,一个数据库下面可以有多张表table,就像一个索引index下面有多种类型type一样。

Elasticsearch关系数据库
索引Indices库Databases
类型Types表Tables
文档Documents行Rows
字段Fields列Columns
映射Mappingschema
Put/Post/Delete/Update/Get增删改查

副本的发现策略

  • 中心化方式
  • 去中心化方式

客户端的主要功能

  1. 跨所有可用节点的负载平衡
  2. 在节点故障和特定响应代码时的故障转移
  3. 失败连接的惩罚机制
  4. 持久连接
  5. 请求和响应的跟踪日志记录
  6. 自动发现集群节点,该功能可选

文档索引API

四种构建JSON文档的方法

  1. 基于String构建IndexRequest

    •   public void buildIndexRequestWithString(String indexName, String document) {
          // 索引名称
          IndexRequest request = new IndexRequest(indexName);
          // 文档ID
          request.id(document);
          // String类型的文档
          String jsonString = "{" + "\"user\":\"niudong\"," + "\"postDate\":\"2019-07-30\","
              + "\"message\":\"Hello Elasticsearch\"" + "}";
          request.source(jsonString, XContentType.JSON);
        }
      
  2. 基于Map构建IndexRequest

    •   public void buildIndexRequestWithMap(String indexName, String document) {
          Map<String, Object> jsonMap = new HashMap<>();
          jsonMap.put("user", "niudong");
          jsonMap.put("postDate", new Date());
          jsonMap.put("message", "Hello Elasticsearch");
          // 作为自动转换为JSON格式的MAP提供的文档源
          IndexRequest indexRequest = new IndexRequest(indexName).id(document).source(jsonMap);
        }
      
  3. 基于XContentBuilder构建IndexRequest

    • public void buildIndexRequestWithXContentBuilder(String indexName, String document) {
          try {
            XContentBuilder builder = XContentFactory.jsonBuilder();
            builder.startObject();
            {
              builder.field("user", "niudong");
              builder.timeField("postDate", new Date());
              builder.field("message", "Hello Elasticsearch");
            }
            builder.endObject();
            // 作为XContentBuilder对象提供的文档源,ElasticSearch内置的帮助器用于生成JSON内容
            IndexRequest indexRequest = new IndexRequest(indexName).id(document).source(builder);
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      
  4. 基于Key-value 构建IndexRequest

    •   public void buildIndexRequestWithKV(String indexName, String document) {
          // 作为Key-value对提供的文档源,转换为JSON格式
          IndexRequest indexRequest = new IndexRequest(indexName).id(document).source("user", "niudong",
              "postDate", new Date(), "message", "Hello Elasticsearch");
        }
      

构建IndexRequest的其他参数配置

public void buildIndexRequestWithParam(String indexName, String document) {
    // 作为Key-value对提供的文档源,转换为JSON格式
    IndexRequest request = new IndexRequest(indexName).id(document).source("user", "niudong",
        "postDate", new Date(), "message", "Hello Elasticsearch");

    request.routing("routing");// 路由值

    // 设置超时时间
    request.timeout(TimeValue.timeValueSeconds(1));
    request.timeout("1s");

    // 设置超时策略
    request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
    request.setRefreshPolicy("wait_for");

    // 设置版本
    request.version(2);

    // 设置版本类型
    request.versionType(VersionType.EXTERNAL);

    // 设置操作类型
    request.opType(DocWriteRequest.OpType.CREATE);
    request.opType("create");

    // 索引文档之前要执行的接收管道的名称
    request.setPipeline("pipeline");
  }

文档索引请求

  • 同步方式

    • IndexResponse indexResponse = restClient.index(indexRequest, RequestOptions.DEFAULT);
      
    •   public void indexDocuments(String indexName, String document) {
          // 作为Key-value对提供的文档源,转换为JSON格式
          IndexRequest indexRequest = new IndexRequest(indexName).id(document).source("user", "niudong",
              "postDate", new Date(), "message",
              "Hello Elasticsearch!北京时间8月1日凌晨2点,美联储公布7月议息会议结果。一如市场预期,美联储本次降息25个基点,将联邦基金利率的目标范围调至2.00%-2.25%。此次是2007-2008年间美国为应对金融危机启动降息周期后,美联储十年多以来首次降息。美联储公布利率决议后,美股下跌,美元上涨,人民币汇率下跌");
      
          try {
            IndexResponse indexResponse = restClient.index(indexRequest, RequestOptions.DEFAULT);
      
            // 解析索引结果
            processIndexResponse(indexResponse);
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      
  • 异步方式

    • restClient.indexAsync(indexRequest, RequestOptions.DEFAULT, listener);
      
    • public void indexDocumentsAsync(String indexName, String document) {
          // 作为Key-value对提供的文档源,转换为JSON格式
          IndexRequest indexRequest = new IndexRequest(indexName).id(document).source("user", "niudong",
              "postDate", new Date(), "message", "Hello Elasticsearch");
      
          ActionListener listener = new ActionListener<IndexResponse>() {
            @Override
            public void onResponse(IndexResponse indexResponse) {
      
            }
      
            @Override
            public void onFailure(Exception e) {
      
            }
          };
      
          try {
            restClient.indexAsync(indexRequest, RequestOptions.DEFAULT, listener);
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      

解析索引结果IndexResponse

private void processIndexResponse(IndexResponse indexResponse) {
    String index = indexResponse.getIndex();
    String id = indexResponse.getId();
    log.info("index is " + index + ", id is " + id);

    if (indexResponse.getResult() == DocWriteResponse.Result.CREATED) {
      // 文档创建时
      log.info("Document is created!");
    } else if (indexResponse.getResult() == DocWriteResponse.Result.UPDATED) {
      // 文档更新时
      log.info("Document has updated!");
    }
    ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
    if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
      // 处理成功shards 小于总shards 的情况
      log.info("Successed shards are not enough!");
    }
    if (shardInfo.getFailed() > 0) {
      for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
        String reason = failure.reason();
        log.info("Fail reason is " + reason);
      }
    }
  }

文档索引查询 即 Get API

1 构建文档索引请求GetRequest

GetRequest getRequest = new GetRequest(indexName, document);
 public void buildGetRequest(String indexName, String document) {
    GetRequest getRequest = new GetRequest(indexName, document);

    // 可选配置参数
    // 禁用源检索,默认情况下启用
    getRequest.fetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE);

    // 为特定字段配置源包含
    String[] includes = new String[] {"message", "*Date"};
    String[] excludes = Strings.EMPTY_ARRAY;
    FetchSourceContext fetchSourceContext = new FetchSourceContext(true, includes, excludes);
    getRequest.fetchSourceContext(fetchSourceContext);

    // 为特定字段配置源排除
    includes = Strings.EMPTY_ARRAY;
    excludes = new String[] {"message"};
    fetchSourceContext = new FetchSourceContext(true, includes, excludes);
    getRequest.fetchSourceContext(fetchSourceContext);

    getRequest.storedFields("message"); // 为特定存储字段配置检索(要求在映射中单独存储字段)
    try {
      GetResponse getResponse = restClient.get(getRequest, RequestOptions.DEFAULT);
      String message = getResponse.getField("message").getValue();// 检索消息存储字段(要求该字段单独存储在映射中)
      log.info("message is " + message);
    } catch (Exception e) {
      e.printStackTrace();
    }

    // 路由值
    getRequest.routing("routing");

    // 偏好值
    getRequest.preference("preference");

    // 将实时标志设置为假(默认为真)
    getRequest.realtime(false);
    // 在检索文档之前执行刷新(默认为false)
    getRequest.refresh(true);
    // 配置版本号
    getRequest.version(2);
    // 配置版本类型
    getRequest.versionType(VersionType.EXTERNAL);
  }

2 执行文档索引查询请求

  • 同步方式

    •   public void getIndexDocuments(String indexName, String document) {
          GetRequest getRequest = new GetRequest(indexName, document);
          try {
            GetResponse getResponse = restClient.get(getRequest, RequestOptions.DEFAULT);
      
            // 处理GetResponse
            processGetResponse(getResponse);
          } catch (Exception e) {
            e.printStackTrace();
          }
      
          // 关闭ES连接
          closeEs();
        }
      
  • 异步方式

    •  public void getIndexDocumentsAsync(String indexName, String document) {
          GetRequest getRequest = new GetRequest(indexName, document);
      
          ActionListener<GetResponse> listener = new ActionListener<GetResponse>() {
            @Override
            public void onResponse(GetResponse getResponse) {
              String id = getResponse.getId();
              String index = getResponse.getIndex();
              log.info("id is " + id + ", index is " + index);
            }
      
            @Override
            public void onFailure(Exception e) {
      
            }
          };
      
          try {
            restClient.getAsync(getRequest, RequestOptions.DEFAULT, listener);
          } catch (Exception e) {
            e.printStackTrace();
          }
      
          // 关闭ES连接
          closeEs();
        }
      

3 返回结果的解析

  // 处理GetResponse
  private void processGetResponse(GetResponse getResponse) {
    String index = getResponse.getIndex();
    String id = getResponse.getId();
    log.info("id is " + id + ", index is " + index);

    if (getResponse.isExists()) {
      long version = getResponse.getVersion();
      String sourceAsString = getResponse.getSourceAsString(); // 以字符串形式检索文档
      Map<String, Object> sourceAsMap = getResponse.getSourceAsMap(); // 以Map<String, Object>形式检索文档
      byte[] sourceAsBytes = getResponse.getSourceAsBytes(); // 以byte[]形式检索文档

      log.info("version is " + version + ", sourceAsString is " + sourceAsString);
    } else {
      // 找不到文档时在此处处理。请注意,尽管返回的响应具有404状态代码,但返回的是有效的getResponse,而不是引发异常。这样的响应不包含任何源文档,并且其isexists方法返回false。

    }
  }

文档存在性校验

  • 同步方式

    • // 同步方式校验索引文档是否存在
        public void checkExistIndexDocuments(String indexName, String document) {
          GetRequest getRequest = new GetRequest(indexName, document);
          // 禁用提取源
          getRequest.fetchSourceContext(new FetchSourceContext(false));
          // 禁用提取存储字段
          getRequest.storedFields("_none_");
      
          try {
            boolean exists = restClient.exists(getRequest, RequestOptions.DEFAULT);
            log.info("索引" + indexName + "下的" + document + "文档的存在性是" + exists);
          } catch (Exception e) {
            e.printStackTrace();
          }
      
          // 关闭ES连接
          closeEs();
      
        }
      
  • 异步方式

    • // 异步方式校验索引文档是否存在
        public void checkExistIndexDocumentsAsync(String indexName, String document) {
          GetRequest getRequest = new GetRequest(indexName, document);
          // 禁用提取源
          getRequest.fetchSourceContext(new FetchSourceContext(false));
          // 禁用提取存储字段
          getRequest.storedFields("_none_");
      
          // 定义监听器
          ActionListener<Boolean> listener = new ActionListener<Boolean>() {
            @Override
            public void onResponse(Boolean exists) {
              log.info("索引" + indexName + "下的" + document + "文档的存在性是" + exists);
            }
      
            @Override
            public void onFailure(Exception e) {
      
            }
          };
      
          try {
            restClient.existsAsync(getRequest, RequestOptions.DEFAULT, listener);
          } catch (Exception e) {
            e.printStackTrace();
          }
      
          // 关闭ES连接
          closeEs();
      
        }
      

删除文档索引

  • 构建DeleteRequest

    •   public void buildDeleteRequestIndexDocuments(String indexName, String document) {
          DeleteRequest request = new DeleteRequest(indexName, document);
      
          // 设置路由
          request.routing("routing");
          // 设置超时
          request.timeout(TimeValue.timeValueMinutes(2));
          request.timeout("2m");
          // 设置刷新策略
          request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
          request.setRefreshPolicy("wait_for");
          // 设置版本
          request.version(2);
          // 设置版本类型
          request.versionType(VersionType.EXTERNAL);
        }
      
  • 同步方式

    • public void deleteIndexDocuments(String indexName, String document) {
          DeleteRequest request = new DeleteRequest(indexName, document);
          try {
            DeleteResponse deleteResponse = restClient.delete(request, RequestOptions.DEFAULT);
      
            // 处理DeleteResponse
            processDeleteRequest(deleteResponse);
          } catch (Exception e) {
            e.printStackTrace();
          }
      
          // 关闭ES连接
          closeEs();
        }
      
    •   // 处理DeleteResponse
        private void processDeleteRequest(DeleteResponse deleteResponse) {
          String index = deleteResponse.getIndex();
          String id = deleteResponse.getId();
          long version = deleteResponse.getVersion();
          log.info("delete id is " + id + ", index is " + index + ",version is " + version);
      
          ReplicationResponse.ShardInfo shardInfo = deleteResponse.getShardInfo();
          if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
            log.info("Success shards are not enough");
          }
          if (shardInfo.getFailed() > 0) {
            for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
              String reason = failure.reason();
              log.info("Fail reason is " + reason);
            }
          }
        }
      
  • 异步方式

    • public void deleteIndexDocumentsAsync(String indexName, String document) {
          DeleteRequest request = new DeleteRequest(indexName, document);
      
          ActionListener listener = new ActionListener<DeleteResponse>() {
            @Override
            public void onResponse(DeleteResponse deleteResponse) {
              String id = deleteResponse.getId();
              String index = deleteResponse.getIndex();
              long version = deleteResponse.getVersion();
              log.info("delete id is " + id + ", index is " + index + ",version is " + version);
            }
      
            @Override
            public void onFailure(Exception e) {
      
            }
          };
      
          try {
            restClient.deleteAsync(request, RequestOptions.DEFAULT, listener);
          } catch (Exception e) {
            e.printStackTrace();
          }
      
          // 关闭ES连接
          closeEs();
        }
      
  • 结果解析

获取文档索引的词向量

词向量 Term Vectors:关于词的一些统计信息的统称

  1. 构建词向量请求 TermVectorsRequest

    • 索引的名称

    • 检索信息的字段

    • 文档ID

    • public void buildTermVectorsRequest(String indexName,String document,String field){
          TermVectorRequest request = new TermVectorsRequest(indexName,document);
          request.setFields(fields);
      }
      
    • public void buildTermVectorsRequest(String indexName, String document, String field) {
          // 方式1:索引中存在的文档
          TermVectorsRequest request = new TermVectorsRequest(indexName, document);
          request.setFields(field);
      
          // 方式2:索引中不存在的文档, 也可以人工为文档生成词向量
          try {
            XContentBuilder docBuilder = XContentFactory.jsonBuilder();
            docBuilder.startObject().field("user", "niudong").endObject();
            request = new TermVectorsRequest(indexName, docBuilder);
          } catch (Exception e) {
            e.printStackTrace();
          }
      
          /*
           * 可选参数
           */
      
          // 将FieldStatistics设置为false(默认为true)可忽略文档计数、文档频率总和、总术语频率总和。
          request.setFieldStatistics(false);
      
          // 将termstatistics设置为true(默认值为false),以显示术语总频率和文档频率。
          request.setTermStatistics(true);
      
          // 将“位置”设置为“假”(默认为“真”)以忽略位置的输出。
          request.setPositions(false);
      
          // 将“偏移”设置为“假”(默认为“真”)以忽略偏移的输出。
          request.setOffsets(false);
      
          // 将“有效载荷”设置为“假”(默认为“真”)以忽略有效载荷的输出。
          request.setPayloads(false);
      
          Map<String, Integer> filterSettings = new HashMap<>();
          filterSettings.put("max_num_terms", 3);
          filterSettings.put("min_term_freq", 1);
          filterSettings.put("max_term_freq", 10);
          filterSettings.put("min_doc_freq", 1);
          filterSettings.put("max_doc_freq", 100);
          filterSettings.put("min_word_length", 1);
          filterSettings.put("max_word_length", 10);
          // 设置filtersettings,根据tf-idff分数筛选可返回的词条。
          request.setFilterSettings(filterSettings);
      
          Map<String, String> perFieldAnalyzer = new HashMap<>();
          perFieldAnalyzer.put("user", "keyword");
          // 设置PerFieldAnalyzer,指定与字段具有的分析器不同的分析器。
          request.setPerFieldAnalyzer(perFieldAnalyzer);
      
          // 将realtime设置为false(默认值为true)以在realtime附近检索术语向量。
          request.setRealtime(false);
      
          // 设置路由
          request.setRouting("routing");
        }
      

文档的索引过程

写入磁盘的倒排索引是不可变的

  1. 读写操作轻量级,不需要锁
  2. 一旦索引被读入文件系统的内存,它就会一直在哪儿,因为不会改变。当文件系统内存有足够大的空间时,大部分的索引读写操作是可以直接访问内存,而不是磁盘就能实现的,有利于提升Elasticsearch的性能
  3. 当写入单个大的倒排索引时,Elasticsearch可以压缩数据,以减少磁盘I/O和需要存储索引的内存大小

一个per-segment search的工作流程如下所示:

  1. 新的文档首先被写入内存区的索引
  2. 内存中的索引不断被提交,新段不断产生。当新的提交点产生时就将这些新段的数据写入磁盘,包括新段的名称。写入磁盘时文件同步写入的,也就是说,所有的写操作都需要等待文件系统内存的数据同步到磁盘,确保它们可以被物理写入。
  3. 新段被打开,于是它包含的文档就可以被检索到。
  4. 内存被清除,等待接收新的文档。

当一个文档被删除时,它实际上只是在.del文件被标记为删除。在进行文档查询时,被删除的文档依然可以被匹配查询,但是在最终返回之前会从结果中删除

当一个文档被更新时,旧版本的文档会被标记为删除,新版本的文档在新的段中被索引。当对文档进行查询时,该文档的不同版本都会匹配一个查询请求,当时较旧的版本会从结果中被删除。

在段合并过程中,小段被合并成大段,大段再合并成更大的段,在合并段时,被删除的文档不会被合并到大段中。

构建批量请求

  1. 构建批量请求

    • public void buildBulkRequest(String indexName, String field) {
          /*
           * 方式1:添加同型请求
           */
          BulkRequest request = new BulkRequest();
          // 添加第一个IndexRequest
          request.add(new IndexRequest(indexName).id("1").source(XContentType.JSON, field,
              "事实上,自今年年初开始,美联储就已传递出货币政策或将转向的迹象"));
          // 添加第二个IndexRequest
          request.add(new IndexRequest(indexName).id("2").source(XContentType.JSON, field,
              "自6月起,市场对于美联储降息的预期愈发强烈"));
          // 添加第三个IndexRequest
          request.add(new IndexRequest(indexName).id("3").source(XContentType.JSON, field,
              "从此前美联储降息历程来看,美联储降息将打开全球各国央行的降息窗口"));
          /*
           * 方式2:添加异型请求
           */
          // 添加一个 DeleteRequest
          request.add(new DeleteRequest(indexName, "3"));
          // 添加一个 UpdateRequest
          request.add(new UpdateRequest(indexName, "2").doc(XContentType.JSON, field,
              "自今年初美联储暂停加息以来,全球范围内的降息大幕就已拉开,不仅包括新兴经济体,发达经济体也加入降息阵营,仅7月份一个月内,就有6国央行降息"));
          // 添加一个IndexRequest
          request.add(new IndexRequest(indexName).id("4").source(XContentType.JSON, field,
              "在此次美联储降息后,央行或不会立即跟进降息z"));
      
          /*
           * 以下是可选参数的配置
           */
          // 设置超时时间
          request.timeout(TimeValue.timeValueMinutes(2));
          request.timeout("2m");
      
          // 设置数据刷新策略
          request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
          request.setRefreshPolicy("wait_for");
      
          // 设置在继续执行索引/更新/删除操作之前必须处于活动状态的碎片副本数。
          request.waitForActiveShards(2);
          request.waitForActiveShards(ActiveShardCount.ALL);
      
          // 用于所有子请求的全局pipelineid
          request.pipeline("pipelineId");
      
          // 用于所有子请求的全局路由ID
          request.routing("routingId");
        }
      
  2. 执行批量请求

    • 同步方式

    •  // 同步方式执行BulkRequest
        public void executeBulkRequest(String indexName, String field) {
          BulkRequest request = new BulkRequest();
          // 添加第一个IndexRequest
          request.add(new IndexRequest(indexName).id("1").source(XContentType.JSON, field,
              "事实上,自今年年初开始,美联储就已传递出货币政策或将转向的迹象"));
          // 添加第二个IndexRequest
          request.add(new IndexRequest(indexName).id("2").source(XContentType.JSON, field,
              "自6月起,市场对于美联储降息的预期愈发强烈"));
          // 添加第三个IndexRequest
          request.add(new IndexRequest(indexName).id("3").source(XContentType.JSON, field,
              "从此前美联储降息历程来看,美联储降息将打开全球各国央行的降息窗口"));
      
          try {
            BulkResponse bulkResponse = restClient.bulk(request, RequestOptions.DEFAULT);
      
            // 解析BulkResponse
            processBulkResponse(bulkResponse);
          } catch (Exception e) {
            e.printStackTrace();
          }
      
          // 关闭ES连接
          closeEs();
      
        }
      
        // 解析BulkResponse
        private void processBulkResponse(BulkResponse bulkResponse) {
          if (bulkResponse == null) {
            return;
          }
      
          for (BulkItemResponse bulkItemResponse : bulkResponse) {
            DocWriteResponse itemResponse = bulkItemResponse.getResponse();
      
            switch (bulkItemResponse.getOpType()) {
              // 索引状态
              case INDEX:
                // 索引生成
              case CREATE:
                IndexResponse indexResponse = (IndexResponse) itemResponse;
                String index = indexResponse.getIndex();
                String id = indexResponse.getId();
                long version = indexResponse.getVersion();
                log.info("create id is " + id + ", index is " + index + ",version is " + version);
      
                break;
              // 索引更新
              case UPDATE:
                UpdateResponse updateResponse = (UpdateResponse) itemResponse;
                break;
              // 索引删除
              case DELETE:
                DeleteResponse deleteResponse = (DeleteResponse) itemResponse;
            }
          }
        }
      
    • 异步方式

    •  // 异步方式执行BulkRequest
        public void executeBulkRequestAsync(String indexName, String field) {
          BulkRequest request = new BulkRequest();
          // 添加第一个IndexRequest
          request.add(new IndexRequest(indexName).id("1").source(XContentType.JSON, field,
              "事实上,自今年年初开始,美联储就已传递出货币政策或将转向的迹象"));
          // 添加第二个IndexRequest
          request.add(new IndexRequest(indexName).id("2").source(XContentType.JSON, field,
              "自6月起,市场对于美联储降息的预期愈发强烈"));
          // 添加第三个IndexRequest
          request.add(new IndexRequest(indexName).id("3").source(XContentType.JSON, field,
              "从此前美联储降息历程来看,美联储降息将打开全球各国央行的降息窗口"));
      
          // 构建监听器
          ActionListener<BulkResponse> listener = new ActionListener<BulkResponse>() {
            @Override
            public void onResponse(BulkResponse bulkResponse) {
      
            }
      
            @Override
            public void onFailure(Exception e) {
      
            }
          };
      
          try {
            restClient.bulkAsync(request, RequestOptions.DEFAULT, listener);
          } catch (Exception e) {
            e.printStackTrace();
          }
      
          // 关闭ES连接
          closeEs();
      
        }
      
  3. 解析批量请求的响应结果

批量处理器BulkProcessor

BulkProcessor想要依赖如下组件

  • RestHighLevelClient:客户端用于执行BulkRequest和检索BulkResponse
  • BulkProcessor.Listener:在每次执行BulkRequest之前和之后,或者当BulkRequest失败时,都会调用此监听器

Bulk构建代码

// 构建BulkProcessor
  public void buildBulkRequestWithBulkProcessor(String indexName, String field) {
    BulkProcessor.Listener listener = new BulkProcessor.Listener() {
      @Override
      public void beforeBulk(long executionId, BulkRequest request) {
        // 批量处理前的动作
      }

      @Override
      public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {
        // 批量处理后的动作
      }

      @Override
      public void afterBulk(long executionId, BulkRequest request, Throwable failure) {
        // 批量处理后的动作
      }
    };

    BulkProcessor bulkProcessor = BulkProcessor.builder((request, bulkListener) -> restClient
        .bulkAsync(request, RequestOptions.DEFAULT, bulkListener), listener).build();

    /*
     * BulkProcessor的配置
     */
    BulkProcessor.Builder builder = BulkProcessor.builder((request, bulkListener) -> restClient
        .bulkAsync(request, RequestOptions.DEFAULT, bulkListener), listener);

    // 根据当前添加的操作数设置刷新批量请求的时间(默认值为1000,使用-1表示禁用)
    builder.setBulkActions(500);

    // 根据当前添加的操作大小设置刷新批量请求的时间(默认为5MB,使用-1表示禁用)
    builder.setBulkSize(new ByteSizeValue(1L, ByteSizeUnit.MB));

    // 设置允许执行的并发请求数(默认为1,使用0时表示仅允许执行单个请求)
    builder.setConcurrentRequests(0);

    // 设置刷新间隔刷(默认为未设置)
    builder.setFlushInterval(TimeValue.timeValueSeconds(10L));

    // 设置一个恒定的后退策略,该策略最初等待1秒,最多重试3次。
    builder.setBackoffPolicy(BackoffPolicy.constantBackoff(TimeValue.timeValueSeconds(1L), 3));

    /**
     * 添加索引请求
     */
    IndexRequest one = new IndexRequest(indexName).id("6").source(XContentType.JSON, "title",
        "8月1日,中国空军发布强军宣传片《初心伴我去战斗》,通过歼-20、轰-6K等新型战机练兵备战的震撼场景,展现新时代空军发展的新气象,彰显中国空军维护国家主权、保卫国家安全、保障和平发展的意志和能力。");
    IndexRequest two = new IndexRequest(indexName).id("7").source(XContentType.JSON, "title",
        "在2分钟的宣传片中,中国空军现役先进战机悉数亮相,包括歼-20、歼-16、歼-11、歼-10B/C、苏-35、苏-27、轰-6K等机型");
    IndexRequest three = new IndexRequest(indexName).id("8").source(XContentType.JSON, "title",
        "宣传片发布正逢八一建军节,而今年是新中国成立70周年,也是人民空军成立70周年。70年来,中国空军在各领域取得全面发展,战略打击、战略预警、空天防御和战略投送等能力得到显著进步。");

    bulkProcessor.add(one);
    bulkProcessor.add(two);
    bulkProcessor.add(three);
  }

MultiGet批量处理

在单个HTTP请求中并执行多个Get请求

  1. 构建MultiGet批量处理请求

    • // 构建MultiGetRequest
        public void buildMultiGetRequest(String indexName, String[] documentIds) {
          if (documentIds == null || documentIds.length <= 0) {
            return;
          }
      
          MultiGetRequest request = new MultiGetRequest();
          for (String documentId : documentIds) {
            // 添加请求
            request.add(new MultiGetRequest.Item(indexName, documentId));
          }
      
          /*
           * 可选参数使用介绍
           */
          // 禁用源检索, 默认情况下启用
          request.add(new MultiGetRequest.Item(indexName, documentIds[0])
              .fetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE));
      
          // 为特定字段配置 源包含关系
          String[] excludes = Strings.EMPTY_ARRAY;
          String[] includes = {"title", "content"};
          FetchSourceContext fetchSourceContext = new FetchSourceContext(true, includes, excludes);
          request.add(
              new MultiGetRequest.Item(indexName, documentIds[0]).fetchSourceContext(fetchSourceContext));
      
          // 为特定字段配置 源排除关系
          fetchSourceContext = new FetchSourceContext(true, includes, excludes);
          request.add(
              new MultiGetRequest.Item(indexName, documentIds[0]).fetchSourceContext(fetchSourceContext));
      
          // 为特定存储字段配置检索( 要求字段在索引中单独存储字段)
          try {
            request.add(new MultiGetRequest.Item(indexName, documentIds[0]).storedFields("title"));
            MultiGetResponse response = restClient.mget(request, RequestOptions.DEFAULT);
            MultiGetItemResponse item = response.getResponses()[0];
            // 检索title存储字段( 要求该字段单独存储在索引中)
            String value = item.getResponse().getField("title").getValue();
            log.info("value is " + value);
          } catch (Exception e) {
            e.printStackTrace();
          } finally {
            // 关闭ES连接
            closeEs();
          }
      
          // 配置路由
          request.add(new MultiGetRequest.Item(indexName, documentIds[0]).routing("routing"));
      
          // 配置版本和版本类型
          request.add(new MultiGetRequest.Item(indexName, documentIds[0])
              .versionType(VersionType.EXTERNAL).version(10123L));
      
          // 配置偏好值
          request.preference("title");
      
          // 将实时标志设置为假( 默认为真)
          request.realtime(false);
      
          // 在检索文档之前执行刷新(默认为false)
          request.refresh(true);
        }
      
  2. 执行MultiGet批量处理请求

    • 同步方式

    • // 同步执行MultiGetRequest
        public void executeMultiGetRequest(String indexName, String[] documentIds) {
          if (documentIds == null || documentIds.length <= 0) {
            return;
          }
      
          MultiGetRequest request = new MultiGetRequest();
          for (String documentId : documentIds) {
            // 添加请求
            request.add(new MultiGetRequest.Item(indexName, documentId));
          }
      
          try {
            MultiGetResponse response = restClient.mget(request, RequestOptions.DEFAULT);
      
            // 解析MultiGetResponse
            processMultiGetResponse(response);
          } catch (Exception e) {
            e.printStackTrace();
          } finally {
            // 关闭ES连接
            closeEs();
          }
        }
      
        // 解析MultiGetResponse
        private void processMultiGetResponse(MultiGetResponse multiResponse) {
          if (multiResponse == null) {
            return;
          }
      
          MultiGetItemResponse[] responses = multiResponse.getResponses();
          log.info("responses is " + responses.length);
      
          for (MultiGetItemResponse response : responses) {
            GetResponse getResponse = response.getResponse();
            String index = response.getIndex();
            String id = response.getId();
            log.info("index is " + index + ";id is " + id);
      
            if (getResponse.isExists()) {
              long version = getResponse.getVersion();
              // 按字符串方式获取内容
              String sourceAsString = getResponse.getSourceAsString();
              // 按Map方式获取内容
              Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
              // 按字节数组方式获取内容
              byte[] sourceAsBytes = getResponse.getSourceAsBytes();
      
              log.info("version is " + version + ";sourceAsString is " + sourceAsString);
            }
          }
        }
      
    • 异步方式

    • // 异步执行MultiGetRequest
        public void executeMultiGetRequestAsync(String indexName, String[] documentIds) {
          if (documentIds == null || documentIds.length <= 0) {
            return;
          }
      
          MultiGetRequest request = new MultiGetRequest();
          for (String documentId : documentIds) {
            // 添加请求
            request.add(new MultiGetRequest.Item(indexName, documentId));
          }
      
          // 添加ActionListener
          ActionListener listener = new ActionListener<MultiGetResponse>() {
            @Override
            public void onResponse(MultiGetResponse response) {
      
            }
      
            @Override
            public void onFailure(Exception e) {
      
            }
          };
      
          // 执行批量获取
          try {
            MultiGetResponse response = restClient.mget(request, RequestOptions.DEFAULT);
          } catch (Exception e) {
            e.printStackTrace();
          } finally {
            // 关闭ES连接
            closeEs();
          }
        }
      
  3. 解析MultiGet批量处理请求的响应结果

文档处理过程解析

Elasticsearch文档分片存储

一个索引一般由多个分片构成,当用户执行添加,删除,修改文档操作时,Elasticsearch需要决定把这个文档存储在那个分片上,这个过程就成为数据路由

路由算法:

  • shard=hash(routing) % number_of_primary_shards
    

Elasticsearch的数据分区

  • 基于文档的分区
    • 每个文档只存一个分区,每个分区持有整个文档集的一个子集,这里所说的分区是指一个功能完整的倒排索引。
    • 优点
      • 每个分区都可以独立地处理查询
      • 可以非常副本地添加以文档为单位的索引信息
      • 在搜索过程中网络开销很小,每个节点可以分别独立地执行搜索,执行完之后只需返回文档的ID和评分信息即可。而呈现给用户的结果集是在执行分布式搜索的节点上执行合并操作实现的。
  • 基于词条的分区

搜索

Search API

SortBuilder

  1. FieldSortBuilder
  2. ScoreSortBuilder
  3. GeoDistanceSortBuilder
  4. ScriptSortBuilder

Search Scroll API

滚动搜索有点类似于数据库中的分页查询

Clear Scroll API

Search Template API

Multi-Search-Template API

Multi-Search API

Field Capabilities API

Ranking Evaluation API

Explain API

Count API

当用多个查询条件进行搜索或查询时,需要注意多个查询条件间的匹配方式。

  • must子句:文档必须匹配must查询条件,相当于“=”
  • should子句:文档应该匹配should子句查询的一个或多个条件
  • must_not子句:文档不能匹配该查询条件,相当于"!="

match方法常见的匹配方式还有 term ,text,range等

  1. term精确匹配
  2. wildcard通配符匹配
  3. prefix前缀匹配
  4. range区间查询
http.cors.enabled:true
http.cors.allow-origin: "*"

附录一:pom.xml

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<!-- 指定仓库位置,依次为aliyun、cloudera和jboss仓库 -->
	<repositories>
		<repository>
			<id>aliyun</id>
			<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
		</repository>
		<repository>
			<id>cloudera</id>
			<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
		</repository>
		<repository>
			<id>jboss</id>
			<url>http://repository.jboss.com/nexus/content/groups/public</url>
		</repository>
	</repositories>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- 添加elasticsearch依赖 -->
		<dependency>
			<groupId>org.elasticsearch.client</groupId>
			<artifactId>elasticsearch-rest-client</artifactId>
			<version>7.2.0</version>
		</dependency>
		<!-- 添加elasticsearch的依赖 begin -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpasyncclient</artifactId>
			<version>4.1.4</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpcore-nio</artifactId>
			<version>4.4.11</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.9</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpcore</artifactId>
			<version>4.4.11</version>
		</dependency>
		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
			<version>1.12</version>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.2</version>
		</dependency>
		<!-- 添加elasticsearch的依赖 end -->
		<!-- 添加elasticsearch的嗅探器 begin -->
		<dependency>
			<groupId>org.elasticsearch.client</groupId>
			<artifactId>elasticsearch-rest-client-sniffer</artifactId>
			<version>7.2.1</version>
		</dependency>
		<!-- 添加elasticsearch的嗅探器 end -->
		<!-- 添加elasticsearch的高级客户端 begin -->
		<dependency>
			<groupId>org.elasticsearch.client</groupId>
			<artifactId>elasticsearch-rest-high-level-client</artifactId>
			<version>7.2.1</version>
		</dependency>
		<!-- 添加elasticsearch的高级客户端 end -->
		<!-- 添加elasticsearch的高级客户端的依赖 begin -->
		<dependency>
			<groupId>org.elasticsearch</groupId>
			<artifactId>elasticsearch</artifactId>
			<version>7.2.1</version>
		</dependency>
		<!-- 添加elasticsearch的高级客户端的依赖 end -->
		<!-- 添加guava的依赖 begin -->
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>28.0-jre</version>
		</dependency>
		<!-- 添加guava的依赖 end -->
		<!-- 中文分词组件 begin -->
		<dependency>
			<groupId>org.ansj</groupId>
			<artifactId>ansj_seg</artifactId>
			<version>5.1.6</version>
		</dependency>
		<dependency>
			<groupId>org.nlpcn</groupId>
			<artifactId>nlp-lang</artifactId>
			<version>1.7.6</version>
		</dependency>
		<!-- 中文分词组件 end -->
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
前言 第1章 Elasticsearch入门 1 1.1 Elasticsearch是什么 1 1.1.1 Elasticsearch的历史 2 1.1.2 相关产品 3 1.2 全文搜索 3 1.2.1 Lucene介绍 4 1.2.2 Lucene倒排索引 4 1.3 基础知识 6 1.3.1 Elasticsearch术语及概念 6 1.3.2 JSON介绍 10 1.4 安装配置 12 1.4.1 安装Java 12 1.4.2 安装Elasticsearch 12 1.4.3 配置 13 1.4.4 运行 15 1.4.5 停止 17 1.4.6 作为服务 17 1.4.7 版本升级 19 1.5 对外接口 21 1.5.1 API约定 22 1.5 .2 REST介绍 25 1.5.3 Head插件安装 26 1.5.4 创建库 27 1.5.5 插入数据 28 1.5.6 修改文档 28 1.5.7 查询文档 29 1.5.8 删除文档 29 1.5.9 删除库 30 1.6 Java接口 30 1.6.1 Java接口说明 30 1.6.2 创建索引文档 33 1.6.3 增加文档 34 1.6.4 修改文档 35 1.6.5 查询文档 35 1.6.6 删除文档 35 1.7 小结 36 第2章 索引 37 2.1 索引管理 37 2.1.1 创建索引 37 2.1.2 删除索引 39 2.1.3 获取索引 39 2.1.4 打开/关闭索引 40 2.2 索引映射管理 41 2.2.1 增加映射 41 2.2.2 获取映射 44 2.2.3 获取字段映射 45 2.2.4 判断类型是否存在 46 2.3 索引别名 46 2.4 索引配置 51 2.4.1 更新索引配置 51 2.4.2 获取配置 52 2.4.3 索引分析 52 2.4.4 索引模板 54 2.4.5 复制配置 55 2.4.6 重建索引 56 2.5 索引监控 60 2.5.1 索引统计 60 2.5.2 索引分片 62 2.5.3 索引恢复 63 2.5.4 索引分片存储 64 2.6 状态管理 64 2.6.1 清除缓存 64 2.6.2 索引刷新 64 2.6.3 冲洗 65 2.6.4 合并索引 65 2.7 文档管理 66 2.7.1 增加文档 66 2.7.2 更新删除文档 69 2.7.3 查询文档 73 2.7.4 多文档操作 76 2.7.5 索引词频率 80 2.7.6 查询更新接口 83 2.8 小结 87 第3章 映射 88 3.1 概念 88 3.2 字段数据类型 90 3.2.1 核心数据类型 91 3.2.2 复杂数据类型 96 3.2.3 地理数据类型 100 3.2.4 专门数据类型 106 3.3 元字段 108 3.3.1 _all字段 109 3.3.2 _field_names字段 109 3.3.3 _id字段 110 3.3.4 _index字段 110 3.3.5 _meta字段 111 3.3.6 _parent字段 111 3.3.7 _routing字段 112 3.3.8 _source字段 114 3.3.9 _type字段 115 3.3.10 _uid字段 115 3.4 映射参数 116 3.4.1 analyzer参数 116 3.4.2 boost参数 118 3.4.3 coerce参数 119 3.4.4 copy_to参数 120 3.4.5 doc_values参数 121 3.4.6 dynamic参数 122 3.4.7 enabled参数 122 3.4.8 fielddata参数 123 3.4.9 format参数 126 3.4.10 geohash参数 128 3.4.11 geohash_precision参数 129 3.4.12 geohash_prefix参数 130 3.4.13 ignore_above参数 131 3.4.14 ignore_malformed参数 131 3.4.15 include_in_all参数 132 3.4.16 index参数 133 3.4.17 index_options参数 133 3.4.18 lat_lon参数 134 3.4.19 fields参数 135 3.4.20 norms参数 136 3.4.21 null_value参数 137 3.4.22 position_increment_gap参数 137 3.4.23 precision_step参数 138 3.4.24 properties参数 138 3.4.25 search_analyzer参数 139 3.4.26 similarity参数 140 3.4.27 store参数 141 3.4.28 term_vector参数 141 3.5 动态映射 142 3.5.1 概念 142 3.5.2 _default_映射 143 3.5.3 动态字段映射 143 3.5.4 动态模板 145 3.5.5 重写默认模板 148 3.6 小结 148 第4章 搜索 149 4.1 深入搜索 149 4.1.1 搜索方式 149 4.1.2 重新评分 153 4.1.3 滚动查询请求 155 4.1.4 隐藏内容查询 158 4.1.5 搜索相关函数 161 4.1.6 搜索模板 164 4.2 查询DSL 167 4.2.1 查询和过滤的区别 167 4.2.2 全文搜索 168 4.2.3 字段查询 179 4.2.4 复合查询 183 4.2.5 连接查询 188 4.2.6 地理查询 190 4.2.7 跨度查询 197 4.2.8 高亮显示 200 4.3 简化查询 203 4.4 小结 206 第5章 聚合 207 5.1 聚合的分类 207 5.2 度量聚合 209 5.2.1 平均值聚合 209 5.2.2 基数聚合 211 5.2.3 最大值聚合 213 5.2.4 最小值聚合 214 5.2.5 和聚合 214 5.2.6 值计数聚合 215 5.2.7 统计聚合 215 5.2.8 百分比聚合 215 5.2.9 百分比分级聚合 216 5.2.10 最高命中排行聚合 217 5.2.11 脚本度量聚合 217 5.2.12 地理边界聚合 221 5.2.13 地理重心聚合 222 5.3 分组聚合 223 5.3.1 子聚合 224 5.3.2 直方图聚合 226 5.3.3 日期直方图聚合 230 5.3.4 时间范围聚合 233 5.3.5 范围聚合 234 5.3.6 过滤聚合 235 5.3.7 多重过滤聚合 236 5.3.8 空值聚合 238 5.3.9 嵌套聚合 239 5.3.10 采样聚合 240 5.3.11 重要索引词聚合 242 5.3.12 索引词聚合 245 5.3.13 总体聚合 251 5.3.14 地理点距离聚合 251 5.3.15 地理散列网格聚合 253 5.3.16 IPv4范围聚合 255 5.4 管道聚合 257 5.4.1 平均分组聚合 259 5.4.2 移动平均聚合 261 5.4.3 总和分组聚合 262 5.4.4 总和累计聚合 262 5.4.5 最大分组聚合 264 5.4.6 最小分组聚合 265 5.4.7 统计分组聚合 266 5.4.8 百分位分组聚合 268 5.4.9 差值聚合 269 5.4.10 分组脚本聚合 273 5.4.11 串行差分聚合 275 5.4.12 分组选择器聚合 276 5.5 小结 277 第6章 集群管理 278 6.1 集群节点监控 278 6.1.1 集群健康值 278 6.1.2 集群状态 279 6.1.3 集群统计 280 6.1.4 集群任务管理 280 6.1.5 待定集群任务 281 6.1.6 节点信息 281 6.1.7 节点统计 282 6.2 集群分片迁移 283 6.3 集群节点配置 284 6.3.1 主节点 285 6.3.2 数据节点 286 6.3.3 客户端节点 286 6.3.4 部落节点 287 6.4 节点发现 287 6.4.1 主节点选举 288 6.4.2 故障检测 288 6.5 集群平衡配置 289 6.5.1 分片分配设置 289 6.5.2 基于磁盘的配置 290 6.5.3 分片智能分配 291 6.5.4 分片配置过滤 292 6.5.5 其他集群配置 293 6.6 小结 293 第7章 索引分词器 294 7.1 分词器的概念 294 7.2 中文分词器 298 7.3 插件 300 7.3.1 插件管理 301 7.3.2 插件安装 301 7.3.3 插件清单 302 7.4 小结 304 第8章 高级配置 305 8.1 网络相关配置 305 8.1.1 本地网关配置 305 8.1.2 HTTP配置 306 8.1.3 网络配置 307 8.1.4 传输配置 308 8.2 脚本配置 310 8.2.1 脚本使用 311 8.2.2 脚本配置 313 8.3 快照和恢复配置 318 8.4 线程池配置 324 8.5 索引配置 326 8.5.1 缓存配置 326 8.5.2 索引碎片分配 329 8.5.3 合并 332 8.5.4 相似模块 332 8.5.5 响应慢日志监控 333 8.5.6 存储 335 8.5.7 事务日志 336 8.6 小结 337 第9章 告警、监控和权限管理 338 9.1 告警 338 9.1.1 安装 338 9.1.2 结构 339 9.1.3 示例 352 9.1.4 告警输出配置 354 9.1.5 告警管理 355 9.2 监控 356 9.2.1 安装 356 9.2.2 配置 357 9.3 权限管理 360 9.3.1 工作原理 361 9.3.2 用户认证 361 9.3.3 角色管理 366 9.3.4 综合示例 368 9.4 小结 369 第10章 ELK应用 370 10.1 Logstash 370 10.1.1 配置 371 10.1.2 插件管理 374 10.2 Kibana配置 377 10.2.1 Discover 379 10.2.2 Visualize 381 10.2.3 Dashboard 383 10.2.4 Settings 386 10.3 综合示例 387 10.4 小结 390 附录 Elasticsearch 5.0的特性与改进 391
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值