乐趣区

关于elasticsearch:全文检索elasticsearch入门看这篇就够了

一、elasticsearch 介绍

1、背景

在订单管理系统中,订单查问的调用量都十分大,如果间接查询数据库,那数据库的压力可想而知,而且有时须要执行一些简单的查问,sql 并不可能敌对的反对,须要查问很多张表。再比方用户手误输出的关键词错了或存在错别字,那应用 sql 是无奈搜寻到。所以打算应用 Elasticsearch 来承载订单查问的次要压力。

总的来说,应用 elasticsearch,以下简称 es 的几个起因如下

  • 关系型数据库在进行含糊(% 关键字 %)搜寻的时候,会全表扫描,查问十分慢
  • 关系型数据库在关键字搜寻时,并不反对全文分词搜寻,比方用户本打算搜寻:公众号 - 臻大虾,却手误:公号 - 臻大虾,es 能够依据分词的后果搜寻出想要的后果。
  • 在数据分析、日志剖析上用到 es

2、es 基本概念

Elasticsearch 是一个分布式、RESTful 格调的搜寻和数据分析引擎,实用于包含文本、数字、天文空间、结构化和非结构化数据等在内的所有类型的数据。Elasticsearch 在 Apache Lucene 的根底上开发而成,由 Elasticsearch N.V.(即当初的 Elastic)于 2010 年首次公布。

Elasticsearch 是文件存储,Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档,用 JSON 作为文档序列化的格局,比方上面这条用户数据:

{
    "name":"臻大虾",
    "sex":0,
    "age":24
}

3、es 劣势

  • 分布式:横向可扩展性,减少服务器可间接配置在集群中
  • 高可用:提供了复制性能,具备容错机制,能主动发现新的或失败的节点,重组和从新均衡节点数据
  • 实时性:数据进入 es,可达到近实时搜寻
  • Restful api:json 格局的 RESTful 格调
  • 全文检索:基于 lucene 的弱小的全文检索能力

4、应用场景

全文检索

当咱们应用百度搜寻、谷歌搜寻时,输出关键字,就能搜寻到最相干的文章,这就是利用了 es 弱小的全文检索的能力。

用户行为

平时淘宝买货色时,你是否发现举荐的商品跟你最近搜寻的关键词很享受,这就是通过收集用户的行为日志,剖析并建设用户模型,保留在 es 中,并利用 es 弱小的深刻搜寻和聚合的能力,能够更好的剖析和展现用户的行为数据。例如举荐零碎,就是利用用户模型的用户数据,对用户数据穿插查问,剖析出用户细粒度的爱好。

监控零碎

利用 es 高性能查问的个性,收集零碎的监控数据,近实时展示监控数据,同时也不便用户对监控数据进行关键字排查。

日志零碎

罕用的计划是 ELK(elasticsearch+logstash+kibana),利用 logstash 去收集 logback 的日志信息,再通过 es 做存储,最初能够再 kibana 去利用 es api 查看和剖析日志的相干信息。

5、es 的外围概念

索引(index)

索引是 es 最大的数据单元,相似于关系型数据库中的库,是多个类似文档的汇合。每个索引有一个或多个分片,每个分片有多个副片。

文档(document)

一条数据就是一个文档,相似于数据库表中的一条记录,比方:

{
    "name":"臻大虾",
    "sex":0,
    "age":24
}

字段(field)

文档的属性,相似表中的字段,比方如下 json 的健:name

{"name":"臻大虾"}

映射(mapping)

映射是对文档中每个字段类型进行定义,相似表构造,蕴含数据类型,长度之类的,比方:

"mappings": {
        "properties": {
            "name": {"type": "text"},
            "age": {"type": "long"}
        }
    }

分片(Shards)

在创立索引时,能够设置主分片个数和正本个数,相似数据库的分表,将单个索引文件分成多份存储,当申请过去时,通过路由计算找到主分片(hash(字段,比方 id)% 分主片数量)。

益处:

  • 如果一个索引数据量很大,会造成硬盘和搜寻速度的瓶颈,分片能分担压力
  • 分片容许咱们进行程度切分和扩大容量
  • 能够在多个分片上进行分布式的、并行的操作,进步零碎吞吐量

留神:主分片在创立之后是无奈批改的,而正本能够随时批改。那想批改主分片的数量怎么办呢,删除从新建。

"settings": {
    "number_of_shards": 2,// 主分片
    "number_of_replicas": 1// 正本
}

正本(Replicas)

由主分片复制来的,提供 高可用

益处:

  • 高可用,当一个主分片挂了,正本能够代替工作
  • 正本也能够执行搜寻操作,摊派了主分片的压力

集群(Cluster)

一个集群就是由一个或多个节点组织在一起,具备雷同集群名的节点能力组成一个集群。它们独特持有整个的数据,并一起提供索引和搜寻性能。

留神:主分片和正本处于不同节点,这样当主分片的机器挂了,正本因为在不同机器上,不会受到影响,正本变为主分片持续工作。所以 es 最小的高可用配置为两台服务器

节点(node)

单个 es 实例称为一个节点(node),一个节点是集群中的一个服务器,作为集群的一部分,存储数据。

类型(type)

7.x 移除了 type,8.x 将彻底移出

image-20210920183804825

二、索引原理

es 应用的是倒排索引也叫反向索引,既然有倒排索引,那是不是有正排索引,有的,咱们先介绍下正排索引。

1、正排索引

正排索引是以文档的 ID 为关键字,文档中每个字段的值为 value,次要场景是通过 id 获取文档信息,平时用的 msyql 关系型数据库就是以这种形式查问的。

举个例子

id 内容
1 my name is zhendaxia
2 my name is jack

通过 id 能够很快查问到内容,然而当查问比方 name 的时候,须要应用 like,再加上数据量大的时候,查问的工夫是很久的,无奈满足查问疾速的要求。

2、倒排索引

倒排索引是以字或词为关键字进行索引,记录呈现这个关键词的文档的 ID

比方下面的例子应用倒排索引如下:

content docid
my 1,2
name 1,2
is 1,2
zhendaxia 1
jack 2

倒排索引,通过字或词疾速的找到所有文档的 id,在依据文档 id 能疾速找到内容。因为人类的词汇数量是绝对无限且固定的,所以效率并不会因为日后关键词的增长而受到很大的影响。

三、集群扩容

1、集群衰弱

image-20210920221154778

集群的衰弱状态有三种:绿色 green、黄色 yellow、红色 red

绿色(衰弱):所有的主分片和副分片都失常运行

黄色(亚健康):所有主分片失常运行,但有副分片没失常运行

红色(不衰弱):有主分片没失常运行

2、扩容

扩容个别分为两种,垂直和程度

1)、垂直扩容

降级服务器,买性能更好的服务器替换原有的服务器,不过这种扩容不举荐,毕竟单台机器的性能总是有瓶颈的

2)、程度扩容

程度扩容也叫横向扩容,就是减少服务器数量,多台一般的服务器组织在一起造成弱小的计算能力。俗话说:团结就是力量。

四、浏览器插件

head 插件是 ES 的一个可视化插件,相似于 navicat 和 mysql 的关系。head 插件是一个用来浏览、与 ES 数据进行交互的 web 前端展现插件,是一个用来监督 ES 状态的客户端插件。

以下是插件的一些简略介绍

image-20210920225224119

五、罕用 api

1、创立索引

PUT /index
{
    "settings": {
        "number_of_shards": 2,
        "number_of_replicas": 1
    },
    "mappings": {
        "properties": {
            "text_name": {"type": "text"},
            "keyword_name": {"type": "keyword"},
            "english_name": {
                "type": "text",
                "fields": {
                    "keyword": {"type": "keyword"}
                }
            },
            "age": {"type": "long"},
            "classId": {"type": "long"},
            "score": {"type": "long"},
            "createTime": {"type": "long"}
        }
    }
}

当看到申请体时,仔细的你可能会发现 text\_name、keyword\_name、english\_name 这三个字段都是字符串,但类型如同有些不同,区别是什么呢?是的,这几个类型往往是刚接触 es 的老手常常弄错的中央。

首先,看下 text 和 keyword 的区别

text:能够分词,用户全文搜寻,能够含糊匹配搜寻

keyword:不能分词,关键词搜寻,只能对某个值进行整体搜寻

type 是 text,但有 fields-keyword:这种类型,一种是本人退出的,另一种是在往 es 插入数据的时候,字段 english\_name 还没有创立。

这时 es 会依据数据类型,主动帮你创立一个字段,如果是字符串类型,因为无奈判断你的这个字符串你是用来准确查问还是含糊查问,所以 es 会创立类型是 text,反对含糊查问,同时会创立 fields,type 是 keyword,反对准确查问,所以当你要准确查问的时候,字段名就不是原来的 english\_name,而是要应用 english\_name.keyword

举个例子来阐明下,首先插入了以下数据,关键字 zhen

{
    "text_name": "zhen daxia",
    "keyword_name": "zhen daxia",
    "english_name": "zhen daxia",
    "age": 18,
    "classId": 2,
    "score": 90,
    "createTime": 1629353892784
}
  • 查问 text\_name,因为 text\_name 类型是 text,会讲 zhen daxia 分词为 zhen、daxia,所以当应用 zhen 查问时,能匹配到 zhen,所以会有后果返回.

如何查看 zhen daxia 被分为哪些词语,能够应用 GET 你的索引 /\_doc/ 数据 id/\_termvectors?fields= 字段名,比方我的索引是 test-user, 那语句就是:GET test-user/\_doc/1/\_termvectors?fields=text\_name

GET test-user/_search
{
  "query": {
   "term": {
     "text_name": {"value": "zhen"}
   }
  }
}

image-20210920232241959

  • 查问 keyword\_name,因为 keyword\_name 类型是 keyword,不会分词,所以 zhen 无奈搜寻到数据

image-20210920232950219

  • 查问 english\_name,同 text\_name,能够搜到

image-20210920233814208

  • 查问 english\_name.keyword,同 keyword\_name,无奈搜寻到后果

image-20210920233927960

2、减少映射字段

PUT /index/_mapping
{
    "properties":{
        "keyword-name":{"type":"keyword"}
    }
}

3、查问

GET test-user/_search

3.1 match(全文检索)

全文检索,会分词,含糊查问,比方关键字 zhen daxia,会被拆为 zhen、daxia

{
  "query": {
    "match": {"text_name": "zhen daxia"}
  }
}

spring boot 办法

boolQueryBuilder.filter(QueryBuilders.matchQuery("text_name", "zhen daxia"));

3.2 term(准确查问)

准确查问, 不会拆词,比方关键字 zhen daxia,会间接应用 zhen daxia 搜寻

{
  "query": {
    "term": {
      "keyword_name": {"value": "zhen daxia"}
    }
  }
}

spring boot 办法

QueryBuilders.termQuery("keyword_name", "zhen daxia");

3.3 terms(多值匹配)

和 term 查问一样,但它容许你指定多值进行匹配,如果这个字段蕴含了指定值中的任何一个值,那么这个文档就算是满足条件。相似 mysql 的 in

{
  "query": {
   "terms": {
     "keyword_name": [
       "zhen",
       "daxia"
     ]
   }
  }
}

spring boot 办法

QueryBuilders.termsQuery("keyword_name", Lists.newArrayList("zhen","daxia"));

3.4 range(范畴查问)

范畴查问,比方搜寻大于等于 20 且小于等于 30 的数据

{
  "query": {
    "range": {
      "age": {
        "gte": 20,   # 大于等于  大于用 gt
        "lte": 30    # 小于等于  小于用 lt
      }
    }
  }
}

spring boot 办法

RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age");
rangeQueryBuilder.gte(20);
rangeQueryBuilder.lte(30);

3.5 prefix(前缀查问)

前缀查问,比方搜寻 zhen,则前缀是 zhen 的都会被搜寻进去

{
  "query": {
    "prefix": {
      "keyword_name": {"value": "zhen"}
    }
  }
}

spring boot 办法

QueryBuilders.prefixQuery("keyword_name","zhen");

3.6 wildcard(通配符含糊查问)

通配符含糊查问,相似 mysql 的 like,? 匹配一个字符,* 匹配 0~n 个字符

{
  "query": {
    "wildcard": {
      "keyword_name": {"value": "* 大虾"}
    }
  }
}

spring boot 办法

QueryBuilders.wildcardQuery("keyword_name","* 大虾")

3.7 fuzzy(含糊查问,不准确查问)

不同于 mysql 的 like,它能够谬误一些字, 比方搜寻 mock,能够搜寻出 mick

{
  "query": {
    "fuzzy": {"keyword_name": "mock"}
  }
}

spring boot 办法

QueryBuilders.fuzzyQuery("keyword_name","mock");

3.8 must、must not、should

//must:必须
boolQueryBuilder.must(QueryBuilders.termQuery("keyword_name","mick"));

//must not:非
boolQueryBuilder.mustNot(QueryBuilders.termQuery("keyword_name","mick"));

//should:相似 mysql 的或
boolQueryBuilder.should(QueryBuilders.termQuery("keyword_name","jack"));
boolQueryBuilder.should(QueryBuilders.termQuery("keyword_name","mick"));

3.9 match all(查问全副)

查问全副,默认 10 条

SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
sourceBuilder.query(matchAllQueryBuilder);
sourceBuilder.size(10);

3.10 match\_phrase

  • 分词后,待查问的字段同时匹配分词后的所有关键词
  • 程序也是一样

比方有以下数据:

1. keyword_name:zhen daxia
2. keyword_name:daxia zhen
3. keyword_name:I am zhen daxia
4. keyword_name:daxia haha

查问 zhen daxia,则返回 1 和 3,2:程序不对,4:没有匹配到全副分词

可通过 slp 调节因子,比方 1,少匹配一个也满足

{
  "query": {
    "match_phrase": {
     "keyword_name": {
       "query": "zhen daxia",
       "slop": 1
     }
    }
  }
}

3.11 multi\_match(多字段匹配)

多字段匹配,有一个字段匹配,就满足,keyword\_name=jack, 或 english\_name=jack, 就算满足

{
  "query": {
   "multi_match": {
     "query": "jack",
     "fields": ["keyword_name","english_name"]
   }
  }
}j

3.12 filter 和 must(过滤)

filter 与 must 是属于同一个级别的查问形式,都能够作为 query->bool 的属性 filter:不计算评分,查问效率高;有缓存(举荐)must:要计算评分,查问效率低;无缓存

3.13 聚合查问(聚合)

依据名字分组

builder.aggregation(AggregationBuilders.terms("agg").field("keyword_name").size(10));

关注公众号:臻大虾,分享更多 java 后端干货

你的反对是对我一直创作的极大激励,咱们下期见。

退出移动版