ElasticSearch(ES)的基本使用与查询原理

1.缘起

为啥想学习es,主要是在工作中会用到,但是因为不了解原理,所以用起来畏手畏脚的,就想了解下es是怎么存储数据,以及es是怎么搜索数据的,和平时的mysql有什么区别,什么情况下用es,什么情况下用mysql。第一次接触es是纠纷有个需求,需要搜索用户组,但是一个用户可能会有多个用户组(需要存储/搜索list),此时mysql虽然可以支持,但是搜索效率很低,就考虑将纠纷接入es。

2.探索

2.1 es的安装与基本使用

2.1.1 es的安装

es的安装-macos

2.1.2 es的基本使用

存入数据

按照官网现存的 elasticsearch 权威指南,索引数据后,提示:

Deprecation: [types removal] Specifying types in document index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, or /{index}/_create/{id}).

指定类型在请求中被废弃了,使用括号里的方式替代

# kibana dev tools里发起请求
PUT /yxfirstindex/employee/3 # 不推荐
{
  "first_name":"Nancy",
  "last_name":"Smith",
  "age":27,
  "interests":[ "music","coding"]
}

PUT /yxfirstindex/_doc/2 # 推荐  PUT /索引名/_doc(关键词)/id(不指定id es会自己生成id)
{
  "first_name":"Jane",
  "last_name":"Smith",
  "age":32,
  "interests":[ "music"]
}

在这里插入图片描述
搜索数据
根据id搜索

GET /yxfirstindex/employee/1 # 废弃 不推荐
GET /yxfirstindex/_doc/1 #推荐

在这里插入图片描述

GET /yxfirstindex/_search # 搜索全部的文档
GET /yxfirstindex/_search?q=first_name:nancy # 搜索名为nancy的文档

在这里插入图片描述

2.1.3 DSL(Domain-specific language)语法
2.1.3.1 一些例子
  1. 基础规则:将查询语句传递给query参数
GET /yxfirstindex/_search 
{
  "query": your_query_statement
}
  1. match_all
    等价于空查询{},匹配所有的文档
{
  "query": {
    "match_all": {}
  }
}
  1. 查询语句的结构
{
  QUERT_NAME:{
  ARGUEMENT:NAME.
  ARGUEMENT:NAME.
  }
}
//针对某个字段
{
  QUERT_NAME:{
    FIELD_NAME:{ //字段名
      ARGUEMENT:NAME.
	  ARGUEMENT:NAME.
    }
  }
}
//eg:查询last_name匹配smith的所有文档。此处match的含义,是包含,还是精确查询?试了一下是包含这个字段的。但是模糊搜索是不支持的,原因是"smi"在分析时候,不会被分析一个倒排索引。
GET /yxfirstindex/_search
{
  "query": {
    "match": {
      "last_name": "smith"
    }
  }
}
  1. 复合查询语句
    单条查询语句可以合并成为更复杂的查询语句,复合语句也可以合并其他任何查询语句,包括复合语句。
{
 "bool":{
  "must":{"match":{"last_name":"smith"}},
  "must_not":{"match":{"intrests":"sports"}},
  "filter":{"range":{"age":{"gt":26}}}
  "should":{"term":{"last_name":"smith"}} //should只会增加满足该语句的文档的得分,只用于修正得分
 }
}
2.1.3.2 语法解析
  • es的查询语言(dsl)可以用于两种情况:查询和过滤
    • 过滤:查询被设置成一个“不评分”/“过滤” 查询,查询的问题是:这篇文章是否匹配。查询结果也只有简单的“是”/否
    • 查询:查询情况时候,查询就变成了一个“评分”的查询,和“过滤”相同,需要判断这篇文档是否匹配,以及匹配度(分数)有多高
  • 举例:
    • last_name字段是否包含smitch 【过滤】
    • age字段是否在26到30之间 【过滤】
    • 包含 “smith”、“sport”这几个词,词之间离得越近,相关性越高。【查询】
  • 性能差异
    • 过滤:简单的检查包含,或者不包含,计算起来速度较快。结果会被缓存到内存中
    • 查询:需要计算得分,计算速度不如过滤,结果不会被缓存到内存中
  • 选择
    需要关注搜索结果中的相关性得分的查询,都需要使用评分查询,其余情况用过滤查询。可以从我们的项目中看到,对列表的筛选过滤,都是用的filter查询,只关注是否包含,不关注相关性。
  • 常用查询语句
//1.match_all查询,匹配所有的文档
{"match_all":{}}
//2.match查询,分为全文查询和对字段的匹配
//全文查询,没有找到全文查询的语法关键词是啥子
{"match":{"full_text_name":"query content"}}
//字段的精确匹配
{"match":{"field_name":"query content"}}
//eg
{"match":{"last_name":"smith"}}
//3.muti_match查询:在多字段上执行相同的match查询
"multi_match": {
      "query":"smith"
      , "fields": ["last_name", "first_name"]
    }
//4.range查询:查询在指定区间的数字或时间,gte(greater than or equal):大于等于,lte(less than or equal):小于等于,gt:大于,lt:小于
"range": {
      "age":{
        "gte":26,
        "lte": 30
      }
    }
//5. term查询,对于某个字段的精确查询
"term": {
  "age":26
}
//6.terms查询,多值匹配,该字段包含了其中任意一个值,都会命中
"terms": {
      "age":[26,30,32]
}
//7.exisits(有值)和missing(无值),和sql里的is null和not is null类似,应用于某个字段有值或者缺失
"exists": {
      "field": "last_name"
    }
//插入一条某个字段无值的文档可以看出来,最终查询结果没有"last_name"为null的这条
PUT /yxfirstindex/_doc/5
{
  "first_name":"Jason",
  "last_name":null,
  "age":27,
  "interests":[ "music","coding"]
}
  • 查询语法验证
GET /yxfirstindex/_validate/query
{
  "query":{
    "field": {
      "exists": "last_name"
    }
  }
}
//结果
{
  "valid" : false
}
//通过explain字段给出更详细的解析
GET /yxfirstindex/_validate/query?explain
//结果
{
  "valid" : false,
  "error" : "ParsingException[unknown query [field]]; nested: NamedObjectNotFoundException[[3:14] unknown field [field]];; org.elasticsearch.common.xcontent.NamedObjectNotFoundException: [3:14] unknown field [field]"
}
//8.指定字段排序
GET /yxfirstindex/_search
{
  "query":{
    "bool": {
      "must": {
        "match": {
            "last_name": "smith"
        }
      }
    }
  },
  "sort":{
      "age":{
        "order":"asc"
      }
  }
}
//返回结果多了 sort字段,值为指定排序字段的值,且_score字段为null。
//9在公司的可视化页面上查询
index_name get方法
空query
可以查到mapping信息等,查到index所包含的字段

2.2 ES的存储&搜索过程

2.2.1 倒排索引(inverted index)

倒排索引,其实从字面意义上很容易理解错,但是看英文就会好理解一些,inverted index,反向的索引。倒排索引,指的是,索引词(关键词)和文档之间的对应关系,即通过一个关键词,可以得到包含这个关键词的所有文档的文档id。有反向索引,就应该有正向的索引,我们可以先来了解下正向索引的数据结构及其使用。

  1. 正向索引(forward index)
    正向索引是搜索过程中也不可缺少的一步,当通过倒排索引查找到相关的文档后,通过正向索引,找到该关键词在文档中的具体位置。
    正向索引的几个关键词
  • LocalId:指文档id.简称(Lid)
  • WordId:索引词的id
  • NHits:索引词在该文档中命中的次数
  • HitList:索引词在文档中的位置,是一个变长的列表
    下面通过一个例子来讲解,正向索引的建立
    文档1:“谷爱凌又夺冠了,我们都爱谷爱凌”,通过分词后:“谷爱凌/又/夺冠/了,我们/都/爱/谷爱凌”,分词后得到这些词语,谷爱凌、又、夺冠、了、我们、都、爱,其中,又、都、了,这些连接词含义不大,我们可以去掉。去掉后得到关键词:谷爱凌、夺冠、我们、爱,分别给这些词加上编号w1、w2、w3、w4。其中“谷爱凌”一词出现了2次,NHits为2;在文档中的位置是1、12。以此类推可以得到另外三个词的NHits和HitList,于是我们可以得到以下这张表
LocalIdWordIdNHitsHitList
s1w121,12
w215
w318
w4110
null

最后通过null来表示结束。
从上面的文档可以得出,正向索引提供的是在一篇文档里,索引词出现的次数、位置等信息,不能满足我们通过一个关键词得到所包含该关键词文档的需求。

  1. 倒排索引
    倒排索引分为两个部分,第一部分是词典,包含索引词、统计信息(文档数量等)以及对应的文档在记录文件中的偏移量(地址),第二部分是记录文件,包含文档id、NHits、HitList等信息。
    下图是第一部分词典和第二部分记录文件等对应关系。
    在这里插入图片描述

比如我们要搜索关键词w1,通过词典我们可以查到有三个文档包含这个关键词,再通过偏移量找到第一个对应的文档Doc1,通过记录表可以得到该关键词命中的次数NHits,以及在Doc1中出现的位置。同理也可以得到该关键词在Doc2、Doc3中的信息。

关于记录表中,文档的排名有几种方式

  1. 通过文档id排序
  2. 通过NHits降序
  3. 记录表分块,块内按照文档id正序,块间按照pageRank降序
    对于方案一,可以对DocId进行压缩,降低存储成本
    对于方案二,文档中关键词出现的次数越多,可以一定程度上反映该文档和关键词的相关性越高,但是会被有些网页的作弊者利用
    对于方案三,兼顾了方案一的DocId压缩带来的有点,也能够体现出文档相关性的顺序,使得重要的文档被优先检索到(此处存疑,问题在于块是如何划分的)

总结:本质上,存在两个空间:文档空间和索引词空间,正向索引是文档空间到索引词空间的映射关系,可以通过文档id得到该文档里的一组索引词的信息。而反向索引是索引词空间到文档空间的映射关系,可以通过索引词id得到包含该索引词的所有文档的id。

2.2.2 索引(建立)过程
2.2.2.1分析

分析模块能够把一个string类型的域转化为单独的项目。这些项目是被加入到倒排索引中以便使这个文档能被搜索到,二是被使用在高级的搜索中如match query中来产生搜索项。

  • 索引分片分配:这个模块提供每一个索引的设置来控制节点中分片的分配。
  • 分片分配过滤:控制哪个分片被分配到哪个节点上
  • 延迟分配:延迟分配由节点的离开导致未分配分片的分配
  • 每个节点的所有分片:相同索引每个节点上的分片数量会有严格的控制
  • 数据层粉看:控制数据层的索引分配
    2.1 索引级别的分片分配过滤
    可以用分片过来控制es为每一个特定的索引分配分片。索引的生存周期
    2.2 当一个节点离开时的延迟分配:
    当一个集群中的节点不管因为什么原因离开时,故意的或者其他,集群的主节点会进行如下操作:
  • 把一个副本分片提升为主要的分片来代替(离开的)节点上的任意主分片
  • 分配副本分片来代替消失的副本分片(假设有足够的节点)
  • 重新平衡剩下的分片通过剩下的节点
    通过尽快地复制副本分片来保护集群数据的以免丢失
    即使我们在节点级别和集群级别限制了并发恢复,这种分片洗牌仍然会带来一些额外的不必要的负载,如果这个消失的节点可能很快🉐️回来。现象这种假设
  • node5失去了网络连接
  • 主节点将副本分片提升为主要分片,为node5上的所有的主要分片
  • 主节点将分配新的副本给集群中的其他节点
  • 每一个副本分片会复制一整份主要分片的数据通过网络
  • 更多的分片被移到不同的节点来平衡集群
  • node5在几分钟后回归了
  • master重新平衡集群,通过重新分配节点5的分片
    如果主分片等一会,消失的分片能够被重新分配到node5上以更小的网络拥挤。
    副本分片的分配可以被延迟,通过设置index.unassigned.node_left.delayed_timeout,默认是一分钟。
index.unassigned.node_left.delayed_timeout
## 设置
PUT _all/_settings
{
  "settings": {
    "index.unassigned.node_left.delayed_timeout": "5m"
  }
}

当延迟被设置后,假设就会变成下面这样

  • node5失去网络连接
  • 主节点将为node5中的主分片,提升对应的其他节点中的副本分片为主分片
  • 主节点记录一个消息,未被分配的节点被延迟分配了,延迟时间是多久
  • cluster状态为黄色,因为存在未被分配的副本分片
  • node5不一会回来了,在设置的timeout时间内
  • 缺失的副本被重新分配给node5
    这个设置不会影响副本分片到主分片提升,也不会影响之前未分配的副本分片的分配。
    分片重定位的取消
    如果延迟超时了,主节点分配缺失的分片到另一个开始恢复的节点上,如果这个消失的节点重新加入了集群,且它的分片还和主分片有同样的id,分片重定位会被取消,而且这个同步的分片会被用来恢复。

还有两节都是将分片的,和主题关系不大(分片是为了保证分布式高可用)而我想知道为什么快。搜索的原理。
索引块(跳过)

2.2.2.2 映射器

映射器模块的功能:充当创建索引/使用更新api更新索引时的 类型映射定义 的注册表。还处理对没有预定义显式映射类型的动态映射支持。
是定义文档及其包含的字段如果存储和索引的过程。每个文档都是字段的集合,每个字段都有其数据类型,映射数据时候,会创建一个映射定义,其中包含与文档相关的字段列表。
可以使用动态映射和显示映射来定义数据。
显示的映射允许你精确地选择如何让定义映射的定义。如

  • 哪些string字段应该被当作全文字段对待
  • 哪些字段包含数字、日期或者地理位置
  • 日期的格式
    映射太多/太大会导致内存爆炸,可以通过设置来限制

动态映射
一个es重要的特征,为了索引一个文档,你不需要首先创建一个索引,定义一个映射类型、定义你的字段。你能够只索引一个文档,而这个索引、类型和字段都会自动的展示。这段话我理解,就是不需要先定义一个索引、字段类型,就可以直接创建,而且能够正常的显示。

put data/_doc/1
{
  "count":5
}

动态的字段映射
当es检测到文档中有一个新增的字段,会动态地为这个字段添加类型的映射。动态的参数控制他的行为(可以通过动态参数选择是否需要动态映射)
json data type
null:对应的就不会有字段被添加
true or false 被映射成boolean
double float
long long
object object
array 取决于array中的第一个非空的值
date类型和数字类型的string,在date detection和numeric detection开启时,被影射成对应的date、float
未被检测为date/数组 会被影射成text

正排索引和倒排索引

2.3 es的搜索过程

  • 不同的查询语句,对应的搜索过程有什么区别:match、filter、term。
  • 模糊查询的实现,性能。
  • 分词后多term条件如何处理
    其他同事给的一些链接:主要原因是wildcard查询会构建一个状态机,这个状态机如果state过多,超过10000就会报错,即使没有报错,性能也相当的差。特别是query为长query时
    https://www.cnblogs.com/johnvwan/p/15645014.html
    http://xiaobaoqiu.github.io/blog/2015/07/16/cratefu-wu-loadbiao-gao/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值