ElasticSearch 笔记

By | 2022年3月11日

搜索

term 搜索

完全匹配,也就是精确查询,搜索前不会再对搜索词进行分词拆解,此文讲的很详细

GET /megacorp/employee/_search
{
    "query" : {
        "term" : {
            "about" : "rock climbing"
        }
    }
}

match 搜索

与 term 搜索不同,会先将搜索词拆分,拆完后,再去匹配,此文讲的很详细

Elasticsearch 默认按照相关性得分排序,即每个文档跟查询的匹配程度。也就是说你搜索”rock climbing”的话,只有”rock”的也可能被搜出来,只是相关性得分会很低,会排在后面点。例子:

GET /megacorp/employee/_search
{
    "query" : {
        "match" : {
            "about" : "rock climbing"
        }
    }
}

返回结果有两条,第一条:”about”:”I love to go rock climbing“,第二条:”about”:”I like to collect rock albums“。

短语搜索

找出一个属性中的独立单词是没有问题的,但有时候想要精确匹配一系列单词或者短语 。 比如, 我们想执行这样一个查询,仅匹配同时包含“rock” 和 “climbing” ,并且 二者以短语 “rock climbing” 的形式紧挨着的雇员记录。为此对 match 查询稍作调整,使用一个叫做 match_phrase 的查询:

GET /megacorp/employee/_search
{
    "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    }
}

毫无悬念,返回结果仅有 John Smith 的文档。

通配符与正则表达式查询

GET /.ds-logs-generic-device_raw_log-2023.07.12-000001/_search
{
    "query" : {
      "wildcard" : {
            "message" : "*hello*"
        }
    }
}

参考

分析

Elasticsearch 有一个功能叫聚合(aggregations),允许我们基于数据生成一些精细的分析结果。聚合与 SQL 中的 GROUP BY 类似但更强大。这里有个复杂案例:ElasticSearch 嵌套分桶的过滤与排序

histogram 统计(按时间)

histogram 统计能够对字段取值按间隔统计建立直方图(针对数值型和日期型字段),这里以日期型字段 @timestamp 为例讲解:

GET /logs-generic-device_raw_log/_search
{
  "size":0,
  "aggs": {
  	"按年月日统计,再按 host 字段统计": {
		"date_histogram": {
			"field": "@timestamp",
			"fixed_interval": "1d",
			"format": "yyyy-MM-dd"
		},
		"aggregations": {
			"hostAgg": {
				"terms": {
					"field": "host",
					"size": 10,
					"min_doc_count": 1,
					"shard_min_doc_count": 0,
					"show_term_doc_count_error": false
				}
			}
		}
  	}
  }
}

返回值:

{
  "aggregations" : {
    "按年月日统计,再按 host 字段统计" : {
      "buckets" : [
        {
          "key_as_string" : "2023-04-03",
          "key" : 1680480000000,
          "doc_count" : 272,
          "hostAgg" : {
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              {
                "key" : "192.168.1.254",
                "doc_count" : 268
              },
              {
                "key" : "192.168.2.154",
                "doc_count" : 4
              }
            ]
          }
        }
      ]
    }
  }
}

参考:elasticsearch之十九springboot测试高级搜索聚合

(5)分析与 histogram 统计(按时间)

下面的 java 代码不仅包含日期型 histogram 统计,还包含嵌套 FiltersAggregationBuilder 过滤分组:

    public ChartsDataset analyseRawData(String cycleTime, List<String> groupNames, ChartsTypeEnum type, String deviceName) {

        // 1. 创建 queryBuilder

        BoolQueryBuilder myquery = new BoolQueryBuilder();

        if (groupNames.size() > 0) {
            BoolQueryBuilder messageQuery = new BoolQueryBuilder();
            for (String groupName : groupNames) {
                messageQuery.should(QueryBuilders.matchQuery("message", groupName));
            }
            myquery.must(messageQuery);
        }

        if (StringUtils.isNotBlank(deviceName)) {
            String messageQuery = "*" + deviceName + "*";
            WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders
                    .wildcardQuery("message", messageQuery)
                    .caseInsensitive(true);
            myquery.must(wildcardQueryBuilder);
        }


        DateHistogramInterval interval = null;
        String dateFormat = null;
        switch (cycleTime) {
            case "day":
                interval = DateHistogramInterval.DAY;
                dateFormat = "yyyy-MM-dd";
                break;
            case "week":
                interval = DateHistogramInterval.WEEK;
                dateFormat = "yyyy-MM-dd";
                break;
            case "month":
                interval = DateHistogramInterval.MONTH;
                dateFormat = "yyyy-MM";
                break;
        }

        // 1. 创建 FiltersAggregationBuilder

        FiltersAggregator.KeyedFilter[] keyedFilters = new FiltersAggregator.KeyedFilter[groupNames.size()];
        for (int i = 0; i < groupNames.size(); i++) {
            String groupName = groupNames.get(i);
            keyedFilters[i] = new FiltersAggregator.KeyedFilter(groupName, QueryBuilders.termQuery("message", groupName));
        }

        FiltersAggregationBuilder filterAgg = AggregationBuilders
                .filters("subAgg", keyedFilters)
                .otherBucket(false)
                .otherBucketKey("other")
                // 使用 message 字段会报错,此字段不支持聚合的,是 text 类型。
                .subAggregation(AggregationBuilders.count("num").field("_id"));

        // 2. 创建 date_histogram AggregationBuilder
        DateHistogramAggregationBuilder dateHistogramAggBuilder = AggregationBuilders.dateHistogram("myHistogram")//自定义名称
                .calendarInterval(interval)
                //.fixedInterval(interval)   //设置间隔
                .minDocCount(0)           //返回空桶
                .field("@timestamp")
                .format(dateFormat)
                //.offset("+0h")            //时区区间偏移值
                //.extendedBounds(new LongBounds(time.get(0), time.get(1)))//指定时间字段
                .subAggregation(filterAgg);

        // 3. 创建 NativeSearchQuery 进行查询

        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(myquery)
                .addAggregation(dateHistogramAggBuilder)
                .withMaxResults(0)
                .build();
        SearchHits<DeviceRawLog> searchHits = esTemplate.search(searchQuery, DeviceRawLog.class);

        // 4. 获取结果

        ChartsDataset dataset = new ChartsDataset();
        dataset.getDimensions().add("name");

        ParsedDateHistogram dateHistogram = searchHits.getAggregations().get("myHistogram");
        for (Histogram.Bucket agg1Bucket : dateHistogram.getBuckets()) {
            String dateKey = agg1Bucket.getKeyAsString();
            long dateCount = agg1Bucket.getDocCount();

            List<Object> arr = new ArrayList<>();
            arr.add(dateKey);

            if (type.equals(ChartsTypeEnum.PIE)) {
                dataset.getDimensions().add("value");
                arr.add(dateCount);
            }

            if (type.equals(ChartsTypeEnum.BAR)) {
                ParsedFilters parsedFilters = agg1Bucket.getAggregations().get("subAgg");
                for (Filters.Bucket bucket : parsedFilters.getBuckets()) {
                    String agg2Key = bucket.getKeyAsString();
                    long agg2Count = bucket.getDocCount();

                    dataset.getDimensions().add(agg2Key);
                    arr.add(agg2Count);

                    ChartsBarSeries barSeries = new ChartsBarSeries(agg2Key, new ChartsBarEncode("name", agg2Key));
                    dataset.getBarSeries().add(barSeries);
                }
            }

            dataset.getSource().add(arr);
        }

        return dataset;
    }

分词器

analyzer:插入文档时,将text类型的字段做分词然后插入倒排索引。
search_analyzer:查询时,先对要查询的text类型的输入做分词,再去倒排索引中搜索。

如果想要让’索引’和’查询’时使用不同的分词器也是能支持的,只需要在字段上加search_analyzer参数,索引时,只会去看字段有没有定义analyzer,有定义的话就用定义的,没定义就用es预设的。
查询时,会先去看字段有没有定义search_analyzer,如果没有定义,就去看有没有analyzer,再没有定义,才会去使用es预设的。

数据流 – Data Stream

数据流可以跨多个索引存储,需要有个索引模板(index template),模板中包含映射(mappings 和 索引设置(settings)。基于滚动索引,可按时间间隔、文档数、分片大小进行滚动。
数据流相对实体索引,有点“抽象层“的概念,其核心数据还是存储在.ds前缀的后备索引中。

数据流需要指定一个匹配的索引模板。在大多数情况下,可以使用一个或多个组件模板(component template)来组成此索引模板。通常使用单独的组件模板进行映射和索引设置。这样可以重用多个索引模板中的组件模板。

在使用数据流时,当前的数据流必须匹配一个索引模板,用于给数据流创建索引。数据流当前索引是无法被删除的,要删除必须滚动数据流,以便创建新的写入索引。然后才可使用删除索引 API 删除以前的写入索引,参考。不过可以直接删除数据流,此时数据流当前索引也被一起删除了。

Change mappings and settings for a data stream

此外经测,索引模板 mapping 字段发生变化后(比如修改分词器),数据流中数据会在 Kibina中显示不出,且新加入的数据也显示不出。删除数据流,重新加入新数据才可以展示出数据!

索引模板发生变化后,数据流的当前索引是不会变的,新创建的索引才会用变化后的模板来创建,要立即生效最快的办法就是删除数据流,此时数据流下的当前索引会一起删除!

如果新数据流或索引与多个索引模板匹配,则使用优先级最高的索引模板。

数据流特点:

  • data stream 是时序索引的上层抽象。
  • data stream 是模板、索引、settings、mappings、ilm policy等综合体的概念。
  • data stream 相当于抛头露面的带头大哥,离不开看似隐身、默默无闻、实际埋头苦干的后备索引的支撑。
  • data stream 不适合频繁删除、更新的业务场景,更适合仅追加时序数据场景。
  • 只能基于 data stream 写入数据,不能基于后备索引写入数据。
  • data stream 不支持单条删除或单条更新数据,只支持:update_by_query 以及 delete_by_query。

Data Stream官方教程

Kibana

使用Kibana 可对Elasticsearch 索引中的数据进行搜索、查看、交互操作。

(1)使用 docker run 启动

docker pull kibana:7.13.4
docker run --name kib --rm -p 5601:5601 kibana:7.13.4

(2)使用 Swarm 配置启动( mykibana.yaml)

version: '3.2'

services:
  mykibana:
    image: kibana:7.13.4
    ports:
      - "5601:5601"
    environment:
      ELASTICSEARCH_HOSTS: '["http://elasticsearch:9200"]'
    depends_on:
      - elasticsearch
    networks:
      - mynet
    deploy:
      placement:
        constraints:
          - node.labels.storm == true

networks:
  mynet:
    external:
        name: policy_mynet

容器起来后需要先创建索引模式:

集群状态

集群黄色状态解读:

bootstrap checks failed 错误解决

(1)max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]

解决办法:

第一步:切换到root用户,修改 /etc/security/limits.conf 添加如下内容:

     * soft nofile 65536
     * hard nofile 131072
     * soft nproc 4096
     * hard nproc 4096

第二步:切换到root用户,修改 /etc/security/limits.d/20-nproc.conf 添加如下内容:

     * soft nproc 1024
     #修改为
     * soft nproc 4096

(2)max number of threads [1024] for user [admin] is too low, increase to at least [4096]

解决办法和上面(1)一样。

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

解决办法:

切换到root用户,修改 /etc/sysctl.conf ,添加:vm.max_map_count=655360
并执行命令:sudo sysctl -p

注意:如果是 docker-compose 部署,改的是宿主主机。这个设置在安装前就应该先设置。

(4)system call filters failed to install; check the logs and fix your configuration or disable system call filters at your own risk

解决办法:

修改 elasticsearch.yml,添加:

 bootstrap.memory_lock: false
 bootstrap.system_call_filter: false

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注