写点什么

ES 入门指南

发布于: 刚刚
ES入门指南

前言

  1. 本文章适用于未接触 ES 或接触较少的中高级开发工程师,以较低的学习成本,快速学习 ES 并在生产中应用为核心目的

  2. 本文章主要以实战维度展开,在不影响数据安全以及基本的性能危机的前提下,不会过多的涉及深层次的底层原理(但也会涉及一些基本的原理,防止出现类似功能分辨不清导致系统性能受到极大的损耗的生产事故)

  3. 本文章借鉴了多个 ES 相关文档以及书籍《ES 权威指南》,同时也进行了很多加工处理,例如: 横向 DB 对比、生涩知识点拆解为更通俗易懂的语言、以实战为主的知识点汇总、使用不当导致的风险点预知、涉及的编程思想等。希望能对大家在 ES 的学习上起到一定帮助。

环境

  1. jdk - v1.8

  2. ES - v7.x

目录

  1. 使用场景

  2. 存储结构

  3. 常用语句

  4. API

  5. 索引

  6. 映射

  7. 更新

  8. 查询

  9. API 查询

  10. DSL 查询


使用场景

  1. 数据库 ES、Mysql、Mongo 等,核心是围绕 CRUD。其中各 DB 的核心特性又取决于其存储结构。这里通过类比的方式来阐述 ES 的使用场景

  2. Mysql 核心存储结构是 B+树,擅长范围查询(因 B+树数据存储在叶子节点并用链表的方式相互关联,有利于做范围类查询操作)

  3. Mongo 核心存储结构是 B 树,擅长单数据查询(因 B 树任意节点都可以存储数据,这样在根据条件查询一条数据时,就会比 mysql 的随机 IO 次数平均值小)

  4. ES 核心存储结构是倒排索引(倒排索引原理后续会讲到),由于是属于重度空间换时间结构,所以在合理使用的前提下(这里特指结构化查询和过滤),范围、单数据等查询都具有不错的性能表现,但是相对的更新数据成本会更高。

  5. 综上所述,ES 更适合写少读多的场景。并且对各种复杂查询都有良好的支持。

存储结构

  1. ES 使用的是倒排索引,和正排索引通过 key 查询 value 不同,倒排索引是通过 value 反向查询 key。

  2. 例如,当进行关键词查询时,如果是正向索引需要扫描索引库中的所有文档,找到所有包含关键词的文档。很显然这样性能会成为问题。而倒排索引则是根据关键词去寻找对应文档。每个关键词都对应这一系列文档,这样只要建立了该关键词的索引,就可以避免全索引库扫描的方式来进行关键词查询。

常用语句(基于 kibana)

API
  1. Java Api

  2. 基于 HTTP 协议,以 JSON 为数据交互格式的 RESTful API

索引(index)
  1. ES 中的索引(index)类比关系型数据库中 table 的概念(5.0 版本是以 type 对应关系型数据库 table 的概念,7.0 后取消了多 type 概念,最多一个,建立 index 时如果不设置会默认创建一个 type 且最多一个 type)

  2. 建立索引: PUT /{index_name} (不可以有大写)

映射(mapping)
  1. ES 中的映射能力主要是为了给字段设定类型。虽然 ES 可以在索引有新字段的文档时会自动基于 json 类型规则生成映射,但是为了防止查询混乱的情况,我们一般会选择手动建立映射。例如: 当索引一个带有引号的数字”22”,这时他将会被映射为 string 类型。如果这时字段已经被映射为 long 类型,es 将会尝试转换字符串为 long,并在失败时会抛出异常。

  2. 建立映射语句:


      PUT /{index_name}      {        "mappings": {          "properties": {            "name": {              "type": "text"            },            "age": {              "type": "integer"            }          }        }      }
复制代码


  字段释义:    mappings, 映射    properties, 特性定义    type, 字段类型
PS: 此处还可设置分词器插件等相关属性
复制代码


更新

添加文档: POST

_index、_type、_id 三者唯一确定一个文档,所以想要保证文档是新加入的,最简单的方式是使用 POST 方法让 ES 自动生成唯一_id(type 7.0 后默认为_doc)

         POST /{index_name}/_doc         {           "studentName": "小明",           "age": 22         }
复制代码


如果想自定义 ID 则可以使用_create 关键字(只有在 index+type+id 均不存在时才会执行成功)

         PUT /{index_name}/_doc/{id}/_create         {           "studentName": "小红",           "age": 22         }
复制代码


  1. 更新文档:

全局更新: PUT, 在 ES 中,文档不能通常意义上的局部修改,只可以通过重建索引或者替换来实现文档更新,实现过程如下:

  1. 从旧文档中检索 JSON

  2. 修改它

  3. 删除旧文档

  4. 索引新文档


         PUT /{index_name}/_doc/{id}         {           "studentName": "小白",           "age": 12         }
复制代码


局部更新: POST, 在使用层次 ES 提供了局部更新的能力,但是在 ES 内部中,依然遵循着上述的四个步骤,不过即便是这样,也一定程度上节约了网络 IO 和解决了并发问题(换句话说可以理解为 ES 将局部更新做了原子化处理)。因为如果没有这个能力,我们想要实现局部更新就不得不先将文档查询出来,然后将修改好的数据再通过 PUT API 请求发送到 ES。

         POST /user/_doc/1/_update         {           "doc": {             "name": "李四"           }         }         PS: doc为type值,由于7.0后取消了多type机制,所以当没有设置type时,这里默认为doc
复制代码


查询

HTTP 查询语句部分:

根据索引名称、类型和文档 ID 查询:

GET /{index_name}/_doc/{id}
1. index_name: 索引名称2. type: 类型(7.0版本后与index_name为1对1关系)3. id: 文档id
复制代码


查询一个索引下的所有数据:

GET /{index_name}/_doc/_searchPS: _search:标记为查询操作的关键字,添加此关键字后,可进行更细粒度的查询操作。
复制代码


条件查询

在请求中依旧使用 _search 关键字,然后将查询语句传递给参数 q= 。这样就可以得到所有 field_name 为 field_value 的结果.

GET /{index_name}/_doc/_search?q={field_name}:{field_value}
复制代码


结构化查询和结构化过滤(DSL 语句):

  1. 结构化查询:查询能力更强,具有相关性分析能力,整体查询原理更为动态,但是因为会实时计算相关性、分析等行为,无法进行缓存操作,导致性能相对较慢

  2. 结构化过滤:进行精确相等性匹配过滤然后存入内存,因为是完全基于索引并且使用了缓存,所以在大量级场景下具有稳定的性能。

  3. 使用建议:原则上来说,使用查询语句做全文本搜索或其他需要进行相关性评分的时候,剩下的全部用过滤语句


常用查询语句和过滤语句:

query: _search 关键字中 DSL 语句最外层关键字,且_search 下只能有 query

         GET /{index_name}/_doc/_search         {           "query":{             ...           }         }
复制代码


term - 过滤类语句:主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型)

         GET /{index_name}/_doc/_search         {           "query":{             {               "term": {                 "age": 26               }             }           }         }
复制代码


terms - 过滤类语句: terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配:

         GET /{index_name}/_doc/_search         {           "query": {             "terms": {               "age":[26, 33]             }           }         }
复制代码


range - 过滤类语句: 允许我们按照指定范围查找一批数据

         GET /{index_name}/_doc/_search         {           "query": {              "range": {                "age": {                  "gte": 20,                  "lt": 30                }              }           }         }         PS:范围操作符包含:          gt: 大于         gte: 大于等于         lt: 小于         lte: 小于等于
复制代码


exists 和 missing - 过滤类语句: 可以用于查找文档中是否包含指定字段或没有某个字段,类似于 SQL 语句中的 IS_NULL 条件

         GET /{index_name}/_doc/_search         {           "query": {             "exists": {               "field": "name"             }           }         }         PS: 这两个过滤只是针对已经查出一批数据来,但是想区分出某个字段是否存在的时候使用。
复制代码


bool - 过滤类语句: 过滤可以用来合并多个过滤条件查询结果的布尔逻辑,它包含以下操作符:

1. must:多个查询条件的完全匹配,相当于 and

2. must_not:多个查询条件的相反匹配,相当于 not 。

3. should:至少有一个查询条件匹配, 相当于 or 。

         GET /{index_name}/_doc/_search         {           "query": {             "bool": {               "must": {"term": {"studentName": "张三"}},               "nust_not": {"term": {"age": 22}},               "should": [                 {"term": {"hasMan": true}},                 {"term": {"hasMonitor": false}}               ]             }           }         }
复制代码


match_all - 查询类语句: 可以查询到所有文档,是没有查询条件下的默认语句

GET /{index_name}/_doc/_search{  "query": {    "match_all": {          }  }}
复制代码


match - 查询类语句: 匹配查询,全文(Fulltext)查询

不同于词条查询 ES 在处理全文查询时,会先对查询条件进行分析,然后根据分词插件做分词处理,最终以分词结果进行查询并返回查询结果。例如:

{  "query": {    "match": {      "msg": "Hello world"    }  }}
复制代码

如上所述, 在执行"hello world"查询条件时,会先进行分析器进行分词得到小写的 hello 和 world,然后默认会执行字段“msg”的值,在 hello 和 world 中包含任意一个,则返回的逻辑。当然,这个是“or”逻辑就也可以设置为“and”逻辑。

PS: 此处包含有三种类型的匹配查询(默认为布尔,上述就是布尔匹配查询类型的运行逻辑,由于此处展开讲内容量较大,涉及较多机制设定、原理等问题,入门指南不做讲解,后续进阶系列会详细描述):

布尔

短语

短语前缀

PS:需要注意的是,如果是做精确查询,因为过滤类语句会进行缓存,所以最好使用过滤类语句。


multi_match - 查询类语句: 允许你做 match 查询的基础上同时搜索多个字段

         {           "query": {             "multi_match": {               "query": "123",               "fields": [                 "name",                 "age"               ]             }           }         }
复制代码


bool - 查询类语句: bool 查询与 bool 过滤相似,用于合并多个查询子句。不同的是, bool 过滤可以直接给出是否匹配成功, 而 bool 查询 要计算每一个查询子句的 _score (相关性分值)。(此处摘自: ES 权威指南)

1. must:查询指定文档一定要被包含。

2. must_not:查询指定文档一定不要被包含。

3. should:查询指定文档,有则可以为文档相关性加分。

4. 以下查询将会找到 title 字段中包含 "how to make millions",并且 "tag" 字段没有被标为 spam。如果有标识为 "starred",或者发布日期为 2014 年之前,那么这些匹配的文档将比同类网站等级高

{  "bool": {    "must": {      "match": {        "title": "how to make millions"      }    },    "must_not": {      "match": {        "tag": "spam"      }    },    "should": [      {        "match": {          "tag": "starred"        }      },      {        "range": {          "date": {            "gte": "2014-01-01"          }        }      }    ]  }}PS: 如果 bool 查询下没有 must 子句,那至少应该有一个 should 子句。但是 如果有 must 子句,那么没 有 should 子句也可以进行查询。
复制代码


查询与过滤条件的合并:

因为 search API 中只能包含 query 语句,所以我们需要用 filtered 来同时包含 query 和 filter 子句:

GET /{index_name}/_doc/_search{  "query": {    "filtered": {      "query": {        "match": {          "email": "business opportunity"        }      },      "filter": {        "term": {          "folder": "inbox"        }      }    }  }}PS: 在混合使用时需要注意的是,我们很少用到的过滤语句中包含查询,保留这种用法只是为了语法的完整性。 只有在过滤中用到全文本匹配的时候才会使用这种结构。
复制代码


发布于: 刚刚阅读数: 3
用户头像

还未添加个人签名 2018.04.26 加入

还未添加个人简介

评论

发布
暂无评论
ES入门指南