全文检索elasticsearch入门,看这篇就够了

一、es 介绍

1、背景

在订单管理系统中,订单查询的调用量都非常大,如果直接查询数据库,那数据库的压力可想而知,而且有时需要执行一些复杂的查询,sql 并不能够友好的支持,需要查询很多张表。再比如用户手误输入的关键词错了或存在错别字,那使用 sql 是无法搜索到。所以打算使用 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.png

二、索引原理

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.png

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

绿色(健康):所有的主分片和副分片都正常运行

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

红色(不健康):有主分片没正常运行

2、扩容

扩容一般分为两种,垂直和水平

1)、垂直扩容

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

2)、水平扩容

水平扩容也叫横向扩容,就是增加服务器数量,多台普通的服务器组织在一起形成强大的计算能力。俗话说:团结就是力量。

四、浏览器插件

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

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

image-20210920225224119.png

五、常用 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.png
  • 查询 keyword_name,由于 keyword_name 类型是 keyword,不会分词,所以 zhen 无法搜索到数据
image-20210920232950219.png
  • 查询 english_name,同 text_name,可以搜到
image-20210920233814208.png
  • 查询 english_name.keyword,同 keyword_name,无法搜索到结果
image-20210920233927960.png

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后端干货,咱们下期见

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容