es为什么那么快
- 主要通过将确定无环状态转换器(Deterministic acyclic finite state transducer, FST是倒排索引的term index)、倒排索引(Inverted index)和跳跃表(skip list)
- fst举例,FST与字典树有两大不同,一个是共享后缀,另一个就是支持Key-Value存储。
FST以字节的方式存储所有的term,这种压缩方式可以有效的缩减存储空间,使得term index(倒排索引中term字典前面)足以放进内存,但这种方式也会导致查找时需要更多的CPU资源。 -
lucence FST构建字典,时间和空间的平衡
image.png
mappingj举例
{
"mapping": {
"_doc": {
"dynamic": "false",
"properties": {
"@timestamp": {
"type": "date"
}
// object没关联inflat相当于
"invalidRoomInfo": {
"properties": {
"rateId": {
"type": "keyword"
},
"rateName": {
"type": "keyword"
}
}
}
// nested
"mainsystemapidata": {
"properties": {
"apiMethodName": {
"type": "keyword"
},
"isSuccess": {
"type": "keyword"
},
"nested": {
"type": "nested",
"properties": {
"apiMethodName": {
"type": "keyword"
},
"timeSpan": {
"type": "long"
}
}
},
"timeSpan": {
"type": "long"
}
}
},
"market": {
"type": "keyword"
}
}
}
}
}
倒排索引
- 每个字段都会设置对应的倒排索引,一般是从id到字段的映射,倒排索引是把所有数据字段值映射到相应的id。
-
一个segment是一个完整的倒序索引的子集, commit point维护了segment集合
年龄到id的映射.png -
倒排索引存储结构, Mysql只有term dictionary这一层,是以b +tree排序的方式存储在磁盘上的,从term index查到对应的term dictionary的block位置之后,再去磁盘上找term,大大减少了磁盘的random access次数。
结构.png
如何联合索引查询
- 如果有两个条件,根据字段相应的posting list做交集,对于mysql而言需要需要根据最具有选择性索引再一个个条件过滤。
- Elasticsearch支持以上两种的联合索引方式,如果查询的filter缓存到了内存中(以bitset的形式),那么合并就是两个bitset的AND。如果查询的filter没有缓存,那么就用skip list的方式去遍历两个on disk的posting list
交集算法一之skiplist
-
三个posting list
三个posting list.png - 整个过程
Next -> 2
Advance(2) -> 13
Advance(13) -> 13
Already on 13
Advance(13) -> 13 MATCH!!!
Next -> 17
Advance(17) -> 22
Advance(22) -> 98
Advance(98) -> 98
Advance(98) -> 98 MATCH!!!
-
posting list的advance需要借助skiplist实现(redis的zset数据少时用ziplist,数据量大用hash加skiplist), skiplist基于二分加链表的思想,即符合插入高效又符合查找高效.
跳表.png
交集算法二之bitset
- 算法思想基于bitmap(布隆过滤器结构)的简化实现
- posting list为[1,3,4,7,10], 则bitset为[1,0,1,1,0,0,1,0,0,1],一共十个,1表示有数,0表示数据不存在。1byte就能表示八个数据,能节省空间。
- 三个posting list的bitset可以做and操作就能挑出交集。
如何减少文档数
- 利用nested结构存储。将公共部分提取出来。
{timestamp:12:05:01, idc:sz, value1:10,value2:11}
{timestamp:12:05:02, idc:sz, value1:9,value2:9}
{timestamp:12:05:02, idc:sz, value1:18,value:17}
{
max_timestamp: 12: 05: 02,
min_timestamp: 1205: 01,
idc: sz,
records: [{
timestamp: 12: 05: 01,
value1: 10,
value2: 11
} {
timestamp: 12: 05: 02,
value1: 9,
value2: 9
} {
timestamp: 12: 05: 02,
value1: 18,
value: 17
}]
}
参考文献
es深入crud
查询
- 查询除了倒排索引,还有分词,相关性分析等知识。
参考文章
分布式集群配置
- Elasticsearch作为典型的重IO应用,IO对ElasticSearch的性能影响非常大。根据我们的使用经验,Elasticsearch集群硬件的重要性:SSD > 内存 > CPU
- ElasticSearch官方文档推荐将服务器一半的内存分配给ElasticSearch作为JAVA堆内存,另一半将作为Lucene的缓存(堆外内存)。Lucene缓存由操作系统内核来管理,在Linux中称为Page Cache。预留充足的内存,操作系统可以显著弥补读磁盘较慢的问题。
es聚合原理
es分词
es先关性分析
es优化
时间类型
- 如无必要不要选择毫秒记录时间,选择秒记录时间跟毫秒记录时间性能上有差别。
字符串类型
- Text性能较慢,支持分词。
- Keywork性能好,不支持分词。但是支持模糊查询
返回时使用size,设置慢查询日志
聚合查询优化
- 对于嵌套聚合查询,EleasticSearch提供了深度优先(默认)和广度优先两种聚合方式。深度优先适合大多数场景。
- 广度优先适用于每个组的聚合数量远远小于当前总组数的情况下
模糊查询
- 前缀模糊查询的效率较低,并且IO负载较高,不推荐使用。
- 如果需要模糊查询,请使用text类型分词后进行查询
将不再写入的index进行强制merge
- 对于只读的index,可以通过_forcemerge API,将一个index内的多个segment合并成较少的segment。从而提高查询性能
es7新特性
es7为啥不支持多type
- 同一个index下,不同的mapping type下的同一个⫿段名不是独立的,必须有同样的字段类型
- 同一个index下,不同的mapping type,如果具有很少或没有相同字段的不同实体会导致数据稀疏并干扰Lucene有效压缩文档的能力。
常用作聚合的数据可设置eager_global_ordinals
es一对多关系设计
使用auto generated ID
- 在索引文档的时候,尽量避免显式地指定的ID (比如在数据同步场景,使用其他数据库的主键作为id),因为Elasticsearch需要检查具有相同ID的文档是否已在于同一分片中,这是一项昂贵的操作,并且随着索引的增长而变得更加昂贵。通过使用自动生成的ID,Elasticsearch可以跳过此检查,这使索引速度更快
数据预处理 - Script
- Script query 通常是非常消耗内的。 通常用于探索和分析数据,不建议用于生产
• 使用预计算代替scriptquery,以空间换时间
• 如果必须要用script,则应首选painless和expressions引擎 - ES 数据预处理 Ingest Node/Pipeline
算法 - Query vs Filter
在使用filter context时,ElasticSearch 不需要做相关性的计算(打分)
• Filter的搜索结果可以被缓存
算法 – prefix、RegExp、wildcard
• 如果满足你的需求,前缀匹配是优于wildcard和regexp
• 根据提供的正则表达式,regexp查询的性能可能有所不同。要提高性能,请避免使用一个以通配符开头的模式(比如,foo或者正则表达式: .foo),wildcard同样需要避免
• 尽量避免让用户直接使用正则表达式
• 特定工具去检查正则表达式的步长:https://regex101.com/
搜索尽可能少的⫿段
-
query_string或 multi_match查询目标的字段越多,它的速度就越慢。提高多个字段搜索速度的常用技术是在索引阶段将其值复制到单个字段中,然后在搜索时使用此⫿段。这可以通过copy-to映射指令自动执行,而不必更改文档的来源
copy-to.png
深度分页
• 数据量很大的时候,fromsize会有严重的效率问题
• 深度翻页推荐使用scroll接口
• 对于实时翻页,推荐使用search_after
可进一步学习
关于JVM
- es用堆内存用的也不多,操作系统内存用的多,更合适。ES中SSD, 内存都很重要,io也重。聚合时正排索引的Doc value适合不分词,FieldData适合分词,FieldData会把所有字段加入JVM堆内存,所以有时候JVM堆太小也不好。有熔断器测试FieldData时候能被堆装下,不能装下会抛异常,所以这块不用担心oom。排序时提前设置global_ordinals有助于更好排序。
- 您可以使堆越小,从Elasticsearch(更快的GC)和Lucene(用于缓存的更多内存)中期望的性能就越好。64g -> 4 -16g
网友评论