ElasticSearch

ElasticSearch

2019-01-12  本文已影响7人  今有所思

使用elasticsearch

启动 Elasticsearch:

cd elasticsearch-<version>
./bin/elasticsearch

Apache Lucene

全文检索(Full-text Search)

我们生活中的数据总体分为两种:结构化数据和非结构化数据。

非结构化数据又一种叫法叫全文数据。

按照数据的分类,搜索也分为两种:

  1. 顺序扫描法(Serial Scanning):所谓顺序扫描,比如要找内容包含某一个字符串的文件,就是一个文档一个文档的看,对于每一个文档,从头看到尾,如果此文档包含此字符串,则此文档为我们要找的文件,接着看下一个文件,直到扫描完所有的文件。

  2. 全文检索大体分两个过程,索引创建(Indexing)和搜索索引(Search)。

    1. 索引创建:将现实世界中所有的结构化和非结构化数据提取信息,创建索引的过程。
    2. 搜索索引:就是得到用户的查询请求,搜索创建的索引,然后返回结果的过程。

基本概念

倒排索引

倒排索引建立索引中词和文档之间的映射,数据是面向词而不是面向文档。

ElasticSearch

为何elasticsearch是准实时的

MySql与ES概念对比

ElasticSearch MySQL
index(索引,名词) database
doc type(文档类型) table
document(文档) row
field(字段) column
mapping(映射) schema
query DSL(查询语言) SQL

基本概念

1)Cluster:集群

ES可以作为一个独立的单个搜索服务器。不过,为了处理大型数据集,实现容错和高可用性,ES可以运行在许多互相合作的服务器上。这些服务器的集合称为集群。

2)Node:节点

形成集群的每个服务器称为节点。

3)Shard:分片

当有大量的文档时,由于内存的限制、磁盘处理能力不足、无法足够快的响应客户端的请求等,一个节点可能不够。这种情况下,数据可以分为较小的分片。每个分片放到不同的服务器上。
当你查询的索引分布在多个分片上时,ES会把查询发送给每个相关的分片,并将结果组合在一起,而应用程序并不知道分片的存在。即:这个过程对用户来说是透明的。

4)Replia:副本

为提高查询吞吐量或实现高可用性,可以使用分片副本。
副本是一个分片的精确复制,每个分片可以有零个或多个副本。ES中可以有许多相同的分片,其中之一被选择更改索引操作,这种特殊的分片称为主分片。
当主分片丢失时,如:该分片所在的数据不可用时,集群将副本提升为新的主分片。

创建

Elasticsearch数据类型

Elasticsearch自带的数据类型数Lucene索引的依据,也是我们做手动映射调整到依据。

映射中主要就是针对字段设置类型以及类型相关参数。

JSON基础类型如下:

Elasticsearch独有的类型:

注意点:

Elasticsearch映射虽然有idnex和type两层关系,但是实际索引时是以index为基础的。如果同一个index下不同type的字段出现mapping不一致的情况,虽然数据依然可以成功写入并生成并生成各自的mapping,但实际上fielddata中的索引结果却依然是以index内第一个mapping类型来生成的。

精确索引:
字段都有几个基本的映射选项,类型(type)和索引方式(index)。以字符串类型为例,index有三个选项:

mapping

mapping是类似于数据库中的表结构定义,主要作用如下:

Text vs Keyword

数据类型被用来索引长文本,比如说电子邮件的主体部分或者一款产品的介绍。这些文本会被分析,在建立索引前会将这些文本进行分词,转化为词的组合,建立索引。允许 ES来检索这些词语。text 数据类型不能用来排序和聚合。
Keyword 数据类型用来建立电子邮箱地址、姓名、邮政编码和标签等数据,不需要进行分词。可以被用来检索过滤、排序和聚合。keyword 类型字段只能用本身来进行检索。

查询

Apache Lucene评分机制

默认评分机制

TF/IDF(词频/逆文档频率)算法

基本原则

分页和结果集大小

基本查询

过滤 is not null

"exists": {
  "field": "gmtCreate"
}

返回版本值

"version": true

query_string和排序

GET /course_audit_aliased/course_audit_aliased/_search
{
  "query": {
    "query_string": {
      "default_field": "courseName",
      "query": "1"
    }
  },
  "sort": [
    {
      "_score": {
        "order": "desc"
      }
    },
    {
      "lastSubmitReviewTime": {
        "order": "asc"
      }
    }
  ]
}

结构化查询 Query DSL (Domain Specific Language)

term vs match

terms类似mysql的in

"terms" : {
    "price" : [20, 30]
}

multi-match

{
  "dis_max": {
    "queries":  [
      {
        "match": {
          "title": {
            "query": "Quick brown fox",
            "minimum_should_match": "30%"
          }
        }
      },
      {
        "match": {
          "body": {
            "query": "Quick brown fox",
            "minimum_should_match": "30%"
          }
        }
      },
    ],
    "tie_breaker": 0.3
  }
}
{
    "multi_match": {
        "query":                "Quick brown fox",
        "type":                 "best_fields", 
        "fields":               [ "title", "body" ],
        "tie_breaker":          0.3,
        "minimum_should_match": "30%" 
    }
}

bool

Bool查询包括四种子句,must,filter,should, must_not。

filter快在两个方面:

  1. 对结果进行缓存
  2. 避免计算分值
{
    "bool" : {
        "must" : {
            "term" : { "user" : "kimchy" }
        },
        "filter": {
            "term" : { "tag" : "tech" }
        },
        "must_not" : {
            "range" : {
                "age" : { "from" : 10, "to" : 20 }
            }
        },
        "should" : [
            {
                "term" : { "tag" : "wow" }
            },
            {
                "term" : { "tag" : "elasticsearch" }
            }
        ]
    }
}

range

"query": {
    "range": {
      "status": {
        "gte": 3,
        "lte": 20
      }
    }
}
"query": {
    "bool": {
      "must": [
        {
          "term": {
            "courseName": {
              "value": "1"
            }
          }
        },
        {
          "range": {
            "status": {
              "lte": 1
            }
          }
        }
      ]
    }
}

高亮

高亮器

    public Page<CourseAuditESDoc> pageByStatus(Integer status, Integer liveFlag, String courseName, String ownerName, PaginationBaseQuery paginationBaseQuery, Sort sort) {
        int pageIndex = paginationBaseQuery.getPageIndex();
        int pageSize = paginationBaseQuery.getPageSize();
        if (pageIndex <= 0) {
            pageIndex = 1;
        }
        if (pageSize <= 0) {
            pageSize = 20;
        }
        // 高亮
        String preTag = "<font color='#dd4b39'>";
        String postTag = "</font>";
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (Objects.nonNull(status)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("status", status));
        }
        if (Objects.nonNull(liveFlag)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("liveFlag", liveFlag));
        }
        nativeSearchQueryBuilder.withFilter(boolQueryBuilder);
        int count = 0;
        boolean hasCourseName = StringUtils.isNotBlank(courseName);
        boolean hasOwnerName = StringUtils.isNotBlank(ownerName);
        if (hasCourseName) {
            count++;
        }
        if (hasOwnerName) {
            count++;
        }
        if (count > 0) {
            sort.and(new Sort(Sort.Direction.DESC, "_score"));
            HighlightBuilder.Field[] fields = new HighlightBuilder.Field[count];
            BoolQueryBuilder queryBuilders = QueryBuilders.boolQuery();
            int index = 0;
            if (hasCourseName) {
                queryBuilders.must(QueryBuilders.matchQuery("courseName", courseName));
                fields[index] = new HighlightBuilder.Field("courseName").preTags(preTag).postTags(postTag).highlighterType("unified");
                index++;
            }
            if (hasOwnerName) {
                queryBuilders.must(QueryBuilders.matchQuery("ownerName", ownerName));
                fields[index] = new HighlightBuilder.Field("ownerName").preTags(preTag).postTags(postTag).highlighterType("unified");
                index++;
            }
            nativeSearchQueryBuilder.withQuery(queryBuilders);
            nativeSearchQueryBuilder.withHighlightFields(fields);
        }

        // 设置分页。Spring data的pageIndex和我们内部的差1
        nativeSearchQueryBuilder.withPageable(PageRequest.of(pageIndex - 1, pageSize, sort));
        Page<CourseAuditESDoc> result = elasticsearchOperations.queryForPage(nativeSearchQueryBuilder.build(),
                CourseAuditESDoc.class, new SearchResultMapper() {
                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                        List<CourseAuditESDoc> docList = new ArrayList<>();
                        for (SearchHit searchHit : response.getHits()) {
                            Map<String, Object> result = searchHit.getSource();
                            Gson gson = new Gson();
                            JsonElement jsonElement = gson.toJsonTree(result);
                            CourseAuditESDoc doc = gson.fromJson(jsonElement, CourseAuditESDoc.class);
                            doc.setId(searchHit.getId());
                            Map<String, HighlightField> map = searchHit.getHighlightFields();
                            if (MapUtils.isNotEmpty(map)) {
                                HighlightField courseNameFiled = map.get("courseName");
                                if (Objects.nonNull(courseNameFiled)) {
                                    doc.setCourseName(courseNameFiled.fragments()[0].toString());
                                }
                                HighlightField ownerNameFiled = map.get("ownerName");
                                if (Objects.nonNull(ownerNameFiled)) {
                                    doc.setOwnerName(ownerNameFiled.fragments()[0].toString());
                                }
                            }
                            docList.add(doc);
                        }
                        if (docList.size() > 0) {
                            return new AggregatedPageImpl<>((List<T>) docList, pageable, response.getHits().totalHits);
                        }
                        return null;
                    }

        });
        return result;
    }
{
  "query": {
    "match": {
      "courseName": "1"
    }
  },
  "highlight": {
    "pre_tags": [
      "<b>"
    ],
    "post_tags": [
      "</b>"
    ],
    "fields": {
      "courseName": {}
    }
  }
}

删除文档

POST /course_audit_aliased/course_audit_aliased/_delete_by_query
{
  "query": {
    "match_all": {}
  }
}

分词

分词器比较

ik分词器

基于_version进行乐观锁并发控制

准实时

要把数据写到磁盘,需要调用 fsync,但是fsync十分耗资源,无法频繁的调用,在这种情况下,Elasticsearch 利用了filesystem cache,新文档先写到in-memory buffer,然后写入到 filesystem cache,过一段时间后,再将segment写到磁盘。在这个过程中,只要文档写到filesystem cache,就可以被搜索到了。

ELK

上一篇下一篇

猜你喜欢

热点阅读