ElasticSearch
ES的分布式架构:
不同的集群通过不同的名字来区分,默认是“elasticsearch”
每个节点都有名字,通过配置文件来配置
每个节点启动后默认是Master eligible节点,可参加选主流程,成为master节点
-
只有master节点才能修改集群的状态信息,但是每个节点都保存了集群的状态
- 集群状态维护了所有的节点信息、所有的索引和其他相关的Mapping和Setting信息、分片的路由信息
- 为一个集群设置多个Master节点/每个节点只承担Master的单一角色
-
节点类型:
- Data Node:可以保存数据的节点,负责保存分片数据,用来做水平扩展
- Coordinating Node:负责接收Client请求,将请求分发到合适的节点,最终把结果汇集到一起,每个节点默认起到了Coordination Node的职责
- Hot&Warm Node 降低部署成本,实现Hot&Warm架构。不同的机器配置
- hot节点新文档写入,ssd
- warm节点保存老数据,hdd
-
分片:
- 主分片:用来解决水平扩展问题,通过主分片可以将数据分布到集群内的所有节点上。一个分片是一个运行的Lucene的实例,且主分片数在索引创建时指定,后续不能修改。除非Reindex
- 副本分片,用以解决数据高可用的问题,副本分片是主分片的拷贝,副本分片数可以动态调整,增加副本数可以提高读取的吞吐。
- 7.0开始主分片默认设置成5->1,解决over-sharding的问题(每个分片都是完整的Lucene索引,它需要为索引的每个分段创建一些文件描述符,增加相应的内存开销,且影响搜索结果的相关性打分)应该通过切分成不同的索引来解决数据量过大的问题。
创建/删除索引的请求,只能被Master节点处理。
-
Master Eligible Node&选主流程
- 一个集群,支持配置多个Master Eligible节点,这些节点可以在必要时(如Master节点宕机)参与选主流程,成为Master节点
- 每个节点启动后,默认就是一个Master Eligible节点
- 当集群内第一个Master Eligible节点启动的时候,会将自己选举成Master节点
- 互相ping对方,Node Id低的会称为被选举的节点
- 脑裂问题:Node1网络断开,node2、3选举出一个master后,node1恢复网络,继续当做master。
- 如何避免脑裂:限定一个选举条件,设置quorum(仲裁),只有在Master eligible节点数大于quorum时才能进行选举。7.0开始无需这个配置,让es自己选择可以形成仲裁的节点。https://www.jianshu.com/p/2ed01f0eea9c
分片与集群的故障转移
- 通过主分片,将数据分布在所有节点上
- 将一份索引的数据,分散在多个node上
- 数量在创建索引的时候指定,后续默认不能修改,需要修改则需要重建索引
- 主分片丢失,副本分片可以Promote成主分片,副本分片数量可以动态调整,每个节点上都有完备的数据,数量增加可以提高读取的吞吐,但是会降低写入性能。
- 节点挂掉,节点上主分片的副本分片会变成主分片,同时会重新分片副本分片。
文档到分片的路由
- hash算法确保文档均匀分散到分片中
- 默认的_routing值是文档id
- 可以自行指定rount数值
- 设置index settings后,primary数,不能随意修改
倒排索引
正排:书本的目录/文档id到文档内容和单词的关联
倒排:索引页,列出每一个文章标题再那一页/单词到文档ID的关系
倒排索引包含两部分:
- 单词词典:记录所有文档的单词,记录单词到倒排列表的关联关系,一般较大,可以通过B+数或者哈希拉链法实现
- 倒排列表:记录了单词对应的文档结合,由倒排索引项组成
- 倒排索引项:
- 文档ID
- 词频 TF,该单词在文档中出现的次数
- 位置position,单词在文档中分词的位置
- 偏移offset,记录单词的开始结束位置
- 倒排索引项:
这就是elasticsearch这个单词的倒排列表
es的JSON文档中的每个字段,都有自己的倒排索引,可以指定字段不做索引
- 在Lucene中,单个倒排索引文件被称为Segment,segment是不可变更的,多个Segments汇总在一起,称为Lucene的Index,对应es中的Shard
- 新文档写入时,会生成新Segment,查询时会查询所有Segments,并进行汇总,Commit Point会记录所有Segments的信息
- 将Index buffer写入Segment的过程叫做Refresh,每秒发生一次,所以一秒后es就可以搜索到数据了。Refersh时会将Segment写入缓存以开放查询。
- 如果有大量数据写入,会产生很多的Segment
- 为了保证数据不丢失,所以在Index文档时,同时写Transaction Log,默认落盘,每个分片有一个Transaction Log。refresh时,index buffer会被清空,transaction log不会。
- Flush,会调用Refresh并清空Index Buffer
- 调用fsync,将Segments写磁盘
- 清空Transaction Log
- 默认30分钟调用一次
- 或者Transaction Log满(512M)
分词
Character Fileter+Tokenizer+Token Filter
URI Search
GET /movies/_search?q=2012&df=title&sort=year:desc&from=0&size=10&timeout=1s
- q指定查询语句,使用Query String Syntax
- df默认字段,不指定时会对所有字段进行查询
- sort 排序 / from和size用于分页
- profile 可以查看查询是如何被执行的
Term v.s. Phrase
- Beautiful Mind等效于Beautiful OR Mind
- q=title:Beautiful Mind, 其中Beautiful是指定title字段的查询,Mind是泛查询
- q=title:(Beautiful Mind)
- “Beautiful Mind”,等效于Beautiful AND Mind。是Phrase查询,还要求前后顺序保持一致
分组与引号
- title:(Beautiful Mind),title中有Beautiful或有Mind
- title:“Beautiful Mind”,Phrase查询,title中有Beautiful Mind,而且顺序必须一致
布尔操作
- AND / OR / NOT 或 && / || / !
- 必须大写
- title:(matrix NOT reloaded)
分组
- +表示must
- -表示must_not
- title:(+matrix -reloaded)
范围查询
- 区间表示:[]闭区间,{}开区间
- year:{2019 TO 2018}
- year:[* TO 2018]
- kibana中 } 会导致dev tool出错,用%7D代替
- 算数符号
- year:>2010
- year:(>2010 && <=2018)
- year:(+>2010 && +<=2018)
通配符查询
- ?代表1个字符,*代表0个或多个字符
- title:mi?d
- title:be*
正则表达
- title:[bt]oy
模糊匹配与近似查询
- title:beautifl~1
- title:“lord rings”~2
Request Body & Query DSL
- 将查询语句通过HTTP Request Body发送给Elasticsearch
- Query DSL
POST /movies,404_idx/_search?ignore_unavailable=true
{
"profile":true,
"from":10,
"size":20,//分页,获取靠后的翻页成本高
"sort":[{"order_date":"desc"}],//排序
"query":{
"match_all":{}
}
}
- 最好在“数字型”与“日期型”字段上排序
- 因为对于多值类型或分析过的字段排序,系统会选一个值,无法得知该值
source filtering
#Last或Christmas出现
GET /comments/_doc/_search
{
"query":{
"match":{
"comment":"Last Christmas"
}
}
}
#Last和Christmas同时出现
GET /comments/_doc/_search
{
"query":{
"match":{
"comment":{
"query":"Last Christmas",
"operator":"AND"
}
}
}
}
#slop中间可以有一个其它的字符进入
GET /comments/_doc/_search
{
"query":{
"match_phrase":{
"comment":{
"query":"Song Last Christmas",
"slop":1
}
}
}
}
Mapping
定义索引中的字段的名称
最好为Mapping加入Meta信息,进行版本管理
定义字段的数据类型,例如字符串,数字,布尔
字段,倒排索引的相关配置
Mapping会把JSON文档映射出Lucene所需要的扁平格式
-
一个Mapping属于一个索引的Type
- 每个文档都属于一个Type
- 一个Type有一个Mapping敌营
- 7.0开始无需指定type信息
-
Mapping的简单类型:
- Text/Keyword :full-text 表示字段内容会被分析,而 keywords 表示字段值只能作为一个精确值查询。
- text适用于全文本字段,会被分词,默认不支持聚合分析及排序
- keyword使用与id,枚举及不需要分词的文本,比如电话号码,性别等需要精确匹配的,可以sorting和aggregations
- 默认会喂文本类型设置成text,并且设置一个keywords的子字段。
- Date
- Integer/Floating
- Boolean
- IPV4&IPV6
- Text/Keyword :full-text 表示字段内容会被分析,而 keywords 表示字段值只能作为一个精确值查询。
Dynamic Mapping
当索引不存在时,会自动创建索引,如果没有定义Mappings,会根据文档信息,推算出字段的类型
当设置为true,一旦有新字段写入,会同时更新Mapping
设置为false,mapping不会更新,也无法被索引,信息会出现在_source中
设置为Strict,文档写入失败
已有的字段,一旦已经有数据写入了,就不能修改字段定义,除非Reindex重建索引
mappings下的某个字段设置index为false,则不被索引,无法搜索。
Index Template
- 帮助你设定Mappings和Settings,并按照一定的规则,自动匹配到新创建的索引之上
- 模板仅在一个索引被新创建时,才会产生作用,修改模板不会影响已创建的索引
- 可以设定多个索引模板,这些设置会被merge
- 可以指定order的数值,控制merging的过程,order高的会覆盖低的设置,优先级最高的是用户指定的Settings和Mappings
ES聚合分析
- 通过聚合,我们会得到一个数据的概览,是分析和总结全套的数据,而不是寻找单个文档
- 西湖区和拱墅区的客房数量
- 不同的价格区间,可预订的经济酒店和5星级酒店的数量
- 高性能,只要一条语句,就可以从es得到分析结果,无需客户端自行分析。
- 分类
- bucket aggregation:
一些列满足特定条件的文档集合, 类似于SQL的group - metric aggregation:一些满足特定条件的文档集合。(最大/最小/平均值),类似于SQL的COUNT.
- pipeline aggregation 对其他的聚合结果进行二次聚合
- matrix aggregration 支持对多个字段的操作并提供一个结果矩阵
- bucket aggregation:
相关性和相关性算分
- 根据文档和查询语句的匹配程度
- 打分的本质是排序,需要把最符合用户需求的文档排在前面。ES5之前,默认的相关性算分采用TF-IDF,现在采用BM25
- 多个shard下,如果每个shard包含指定搜索条件的document数量不均匀的情况下,会导致在某个shard上document数量少的时候,计算该指定搜索条件的document的相关性评分要虚高。导致该document比实际真正想要返回的document的评分要高。
- 词频TF
- 逆文档频率IDF:搜索的词在所有文档中出现的次数越少,相关性越高
- 搜索的词占文档总长度比例越大,相关性越高
- 匹配字段越多,得分越高。
bool 查询
一个或者多个查询子句的组合
Bool查询对应Lucene中的BooleanQuery,它由一个或者多个子句组成,每个子句都有特定的类型。
-
must
返回的文档必须满足must子句的条件,并且参与计算分值
-
filter
返回的文档必须满足filter子句的条件。但是不会像Must一样,参与计算分值
-
should
返回的文档可能满足should子句的条件。在一个Bool查询中,如果没有must或者filter,有一个或者多个should子句,那么只要满足一个就可以返回。
minimum_should_match
参数定义了至少满足几个子句。 -
must_nout
返回的文档必须不满足must_not定义的条件。
Index Alias
为索引定一个别名,操作都通过别名来完成
搜索建议 Suggester API
自动补全或者纠错,提高匹配程度
- 将输入的文本分解为Token,然后在索引的字典里查找相似的Term并返回
- 类别:
- Term Suggester,对每个分词进行分析,改动多少字符就可以和另外一个词一致,提供一个分数
- Missing -如索引中已经存在,就不提供建议
- Popular - 推荐出现频率更高的词
- Always - 无论是否存在都提供建议
- Phrase Suggester,新增了一些参数,比如Max Errors,最多可以拼错的terms数
- Complete Suggester,自动补全,每输入一个字符就要查询,比较耗性能
- Context Suggester 自动补全的扩展
- Term Suggester,对每个分词进行分析,改动多少字符就可以和另外一个词一致,提供一个分数
跨集群搜索
痛点:单集群水平扩展时,节点数不能无限增加,集群的元信息(节点、索引、集群状态)会在每个节点都存储,导致更新压力变大。
跨集群搜索——Cross Cluster Search
- 允许任何节点扮演federated节点,以轻量的方式,将搜索请求进行代理
- 不需要以Client Node的形式加入其它集群
- 每个集群配置相同的索引名
分布式搜索
- 先到Coordination节点,随机选择分片,发送请求,每个分片进行查询排序,返回排序后的文档id
- Coordination节点会把每个分片的id列表重新排序然后分页,再根据id去相应的分片获取详细的文档数据。
避免深度分页Search After
- 可以实时获取下一页文档信息
- 不指定特定页数(From)
- 只能往下翻
- 第一步搜索指定sort,并且保证值是唯一的(加入id)
- 然后使用上一次,最后一个文档的sort值进行查询
- Scroll API:适合需要全部文档的时候
- 创建一个快照,有新的数据写入的话无法被查到
- 每次查询后,输入上一次的Scroll Id
- Regular:需要实时获取顶部的部分文档的场景
- Pagination:需要深度分页,则选用Search After
并发读写
es采用的是乐观并发控制,类似cas。
父子文档
Reindex 重建索引
API
- 查看集群健康状态:/_cluster/health
- 查看节点信息:/_cat/nodes?v
- 获取一个文档:GET /users/_doc/1
- Index/Create:index如果已存在,则删除老的再新增,Create如果已存在则返回错误
- _bulk:多个操作合并
- _mget:批量读取
- _msearch:批量查询
- /_search:集群上所有的索引
- /index1/_serarch:index的索引
LSM Tree
https://www.jianshu.com/p/5c846e205f5f