ElasticSearch 预加载(preloading fileddata)

ElasticSearch 预加载(preloading FiledData)


ES 默认使用的是懒加载.当Search 请求过来时,才会将要查询的index 所有的数据加载到内存中做处理(一问:如果index数据超级大.是怎么处理的).
这种情况下会出现一个问题: 当数据量大的时候,会有一个明显的等待.因为ES 需要先将对应index 数据加载到内存,然后才能做查询.


这时,有三种解决办法:
1. 饿加载
2. 饿加载全局序数(二问: 这个全局序数,是怎么回事)
3. 对数据进行预缓存(index warmers 缓存预热)


1. 饿加载: 
区分与默认配置的懒加载.当有新的segments 被创建(刷新,合并)时.在Search 之前会先将每一个 segment fielddata 预先加载.
官方例子:
PUT /music/_mapping/_song
{
 "tags": {
   "type": "string",
   "fielddata": {
     "loading" : "eager" 
   }
 }
}
抽象:
PUT /indexName/_mapping/typeName
{
 "filedName": {
   "type": "string",
   "fielddata": {
     "loading" : "eager"// 默认字段是: lazy
   }
 }
}
解释:
告诉ES 将indexName 下的这个typeName 下的filedName 字段.预先加载到内存中


比较:
这两种加载形式对比.只是耗费的步骤时间不同.酌情选择:
lazy : 耗费的是查询时间.使用lazy 加载的数据.会影响查询的反馈时间
eager: 消耗的是数据刷新时间,会加大数据刷新需要的时间.(使用此项,最好是不频繁增删改的index,顺便加大refresh_interval 的时间. 默认refresh_interval 刷新时间是 1s)


通常.大的segments 的刷新时间要比小的segments 时间要长.并且,大的segments 创建是在小的segments 的基础上创建的(参考 ElasticSearch optimize).
所以数据已经是饿加载.所以.长的刷新时间,就不那么重要了.


全局序数:
通常.这项技术是用来减少String fielddata 使用的内存.
官方举例: 有一个几十亿的文档.没有个都有一个状态. 该状态有三个可选项: status_pending, status_published, status_deleted. 
如果用String 类型存储,需要占用大约15GB 的空间.但是用0,1,2 做唯一对应.可以减小到1GB以内.
Remember that fielddata caches are per segment. (fielddata 缓存在每一个segment)
不过还有一个问题.当一段segment 只有两个状态 status_deleted 和 status_published 由此产生的序数0,1 就不能和三个状态全都存在的产生的序数相同.
当我们试图Aggregation 这个状态字段的时候.就需要定下以哪一个segment 为标准并应用到所有的segments. 
天真的方法(这边不知道怎么形容.大概意思就是,反正ES不是这么做的)是将Aggregation 在每一个 segment 上运行,
每一个segment 返回它本身的状态值的String 形式.然后再合并成一个返回结果返回. 但这是慢的并且是CPU密集型操作.
ES 可以这样做:
使用一个成为全球序数(Global Ordinals) 的结构.Global Ordinals 是一个小内存数据结构.构建与fielddata 顶部.所有的segments都知道这个Ordinal 列表.
这时候做Aggregation 操作,就可以按照全局序列进行.转换成实际字符操作.只会在最后返回结果的时候进行(可以增加三到四倍的性能).
重点来了.怎么构建全局序数:
首先,生命中没有什么东西是免费的(nothing in life is free). Globle Ordinals 在index 对所有segment 都有关系. 所以,如果创建一个新的或者删除一个旧的segment
这个Global Ordinals 都需要重新构建.而重构需要读取每一个segment上的唯一值.
带来的影响就是:唯一值的基数越高(唯一值越多)需要的时间就越长.
Global ordinals are built on top of in-memory fielddata and doc values. In fact, they are one of the major reasons that doc values perform as well as they do.(这句话翻译不通.就不翻译了)
和filedata 加载一样Global ordinals 创建默认是懒加载的.
第一个请求击中一个index 因为需要filedata 从而引发Global ordinals 创建.因为依赖这个filed 的基数,可能导致一个非常高的延迟.当Global ordinals 创建完毕.它将会被重用.直到这个index 被刷新和合并(三问:refresh 和 flush 有什么区别没?).


2. 饿加载全局序数:
配置字符串字段为饿加载全局序数
PUT /music/_mapping/_song
{
 "song_title": {
   "type": "string",
   "fielddata": {
     "loading" : "eager_global_ordinals" 
   }
 }
}


抽象:
PUT /indexName/_mapping/typeName
{
 "filedName": {
   "type": "string",
   "fielddata": {
     "loading" : "eager_global_ordinals" 
   }
 }
}


Doc value 也可以构建全局序数
PUT /music/_mapping/_song
{
 "song_title": {
   "type":       "string",
   "doc_values": true,
   "fielddata": {
     "loading" : "eager_global_ordinals" 
   }
 }
}
在这种情况下.fielddata 不加载到内存中.而是加载到文件系统的缓存中.
注意:
如果经常索引数据而查询很少.饿加载是一个更好的选择(牺牲查询时间)反之(牺牲刷新时间)


3. index warmers
警告:由于基于磁盘和规范的doc value warmers 已经于2.3.0 版本弃用.
index warmers 可以提前加载全局序数和filedata (四问:提前加载的意思是什么?如果已经配置了饿加载,它代表什么?).
index warmers,filedata,Globle ordinals 都是为一个目的服务的,那就是提高查询速度,减少加载数据造成的耗时


一个index warmers 需要在你的new segments 可以查询之前,指定一个查询和聚合.这个操作叫做预先安装,预热或者缓存.
最初,这个功能是为了让filedata 可以预先装载,因为装在filedata 的代价很大(官文说的是昂贵的步骤).
然而,filedata 可以通过与构建filter 做缓存,也可以通过预加载fielddata 做缓存.


index warmers 创建:
warmers 属于特定的index 每一个warmers 都需要一个特定的ID.因此.一个index 可以对应多个index warmers.然后就可以指定查询,它可以包含queries,filters,aggregations,sort value,script等等 任何的有效的DSL 查询.重要的是,它可以代表使用者频繁操做的查询.
官网用例:
PUT /music/_warmer/warmer_1 
{
 "query" : {
   "bool" : {
     "filter" : {
       "bool": {
         "should": [ 
           { "term": { "tag": "rock"        }},
           { "term": { "tag": "hiphop"      }},
           { "term": { "tag": "electronics" }}
         ]
       }
     }
   }
 },
 "aggs" : {
   "price" : {
     "histogram" : {
       "field" : "price", 
       "interval" : 10
     }
   }
 }
}
抽象:
PUT /indexName/_warmer/warmerId 
{
 "query" : {
 Query Body
 },
 "aggs" : {
 Aggregation Body
 }
}


当一个新的 segment 被创建时,ES 会在segment 上准确的执行已经注册的适用的warmers 迫使缓存加载,只有所有warmers 加载完成之后这个segment 才允许查询.
同样的.index warmers 也有刷新耗时.你可以创建成千上万个warmers 后果就是,大大增加了刷新时间.
如果这个warmers 命中率不高,反而得不偿失,所以一定要非常明智测创建warmers.

猜你喜欢

转载自blog.csdn.net/sjzxlpp/article/details/66975040