Elasticsearch系列:番外篇-Fielddata

本文探讨了Elasticsearch中FieldData的工作原理,包括其在聚合、排序操作中的角色,配置方式,内存管理策略,以及与DocValues的主要区别。FieldData针对分词字段提供了查询时的数据结构,但需谨慎管理内存以避免性能问题。

在上一篇DocValues中介绍过,它主要是针对not analyzed String字段存储,那要针对需要分词的字段该如何sort,agg,group,facet呢?一般默认情况下,它会报错。

GET /test_index/test_type/_search 
{
  "aggs": {
    "group_by_test_field": {
      "terms": {
        "field": "test_field"
      }
    }
  }
}
{
  "error": {
    "root_cause": [
      {
        "type": "illegal_argument_exception",
        "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [test_field] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory."
      }
    ],
    "type": "search_phase_execution_exception",
    "reason": "all shards failed",
    "phase": "query",
    "grouped": true,  
    "caused_by": {
      "type": "illegal_argument_exception",
      "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [test_field] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory."
    }
  },
  "status": 400
}

是的,错误信息中已经找到了答案!FieldData主要是针对analyzed String,它是一种查询时(query-time)的数据结构。

1. 原理

FieldData缓存主要应用场景是在对某一个field排序或者计算类的聚合运算时,它会把这个field列的所有值加载到内存,这样做的目的是提供对这些值的快速文档访问。为field构建FieldData缓存可能会很昂贵,因此建议有足够的内存来分配它,并保持其处于已加载状态。

FieldData是在第一次将该filed用于聚合,排序或在脚本中访问时按需构建。FieldData是通过从磁盘读取每个段来读取整个反向索引,然后逆置term↔︎doc的关系,并将结果存储在JVM堆中构建的

所以,加载FieldData是开销很大的操作,一旦它被加载后,就会在整个段的生命周期中保留在内存中。这了可以注意下FieldDataDoc Values的区别。较早的版本中,其他数据类型也是用的FieldData,但是目前已经用随文档索引时创建的Doc Values所替代。

2. 配置

FieldData默认是禁用的,更多设置

PUT my_index/_mapping/_doc
{
  "properties": {
    "my_field": { 
      "type":     "text",
      "fielddata": true
    }
  }
}

3. 加载

fielddata加载到内存又几种策略,默认是懒加载。即对一个分词的字段执行聚合或者排序的时候,加载到内存。所以他不是在索引创建期间创建的,而是查询在期间创建的。

参数值含义
lazy(默认)FieldData只会在需要用到时加载到内存
eager创建新段(通过refresh,flush或段合并)时,启用了eager loding的field将在段对搜索可见之前预先加载其每段的fielddata。如果用户的搜索请求必须触发对一个大型段的延迟加载时,这个选项可以减少延迟。其实说白了,就是把加载FieldData的时间成本从搜索时转移到了处理段可见时。
eager_global_ordinalsFieldDataGlobal Ordinals加载提前到一个新段对搜索可见之前。

4. 监控

GET /_stats/fielddata?fields=*
GET /_nodes/stats/indices/fielddata?fields=*
GET/_nodes/stats/indices/fielddata?level=indices&fields=*

5. 内存管理

那么基于内存,那又会带来一些问题。如果我们不对内存加以控制,如果数据量太大,很容易造成OOM。那我们应该如何控制fielddata呢?

缓存限制

我们可以配置fielddata内存限制,超出这个限制就清除内存中已有的fielddata数据。默认无限制,限制内存使用,但是会导致频繁evict和reload,大量IO性能损耗,以及内存碎片和gc。

indices.fielddata.cache.size: 20%

circuitbreaker

如果一次query load的fielddata超过总内存,就会发生内存溢出,circuit breaker会估算query要加载的fielddata大小,如果超出总内存,就短路,query直接失败。

indices.breaker.fielddata.limit:fielddata的内存限制,默认60%
indices.breaker.request.limit:执行聚合的内存限制,默认40%
indices.breaker.total.limit:综合上面两个,限制在70%以内

frequency

min: 0.01 只是加载至少1%的doc文档中出现过的term对应的文档。比如说tiger,总共有10000个文档,tiger必须在100个文档中出现。min_segment_size: 500 少于500 文档数的segment不加载fielddata,加载fielddata的时候,也是按照segment去进行加载的,某个segment里面的doc数量少于500个,那么这个segment的fielddata就不加载。

PUT /animals/_mapping/crawler
{
   "properties":{
       "desc":{
           "type":"text",
            "fielddata": {
               "filter": {
                 "frequency": {
                        "min":0.01,
                       "min_segment_size": 500 
                   }
               }
            }
        }
    }
}

6. 和doc_value对比

相同点

  • 都要创建正排索引,数据结构类似于列式存储
  • 都是为了可以聚合,排序之类的操作

不同点

  • 存储索引数据的方式不一样:fielddata: 内存存储;doc_values: OS Cache+磁盘存储
  • 对应的字段类型不一样:fielddata: 对应的字段类型是text; doc_values:对应的字段类型是keyword
  • 针对的类型,也不一样:field_data主要针对的是分词字段;doc_values针对大是不分词字段
  • 是否开启:fielddata默认不开启;doc_values默认是开启

7. 总结

我们知道fielddata堆内存要求很高,如果数据量太大,对于JVM来及回收来说存在一定的挑战。所以doc_value的出现我们可以使用磁盘存储,他同样是和fielddata一样的数据结构,在倒排索引基础上反向出来的正排索引,并且是预先构建,即在建倒排索引的时候,就会创建doc values。,这会消耗额外的存储空间,但是对于JVM的内存需求就会减少。总体来看,DocValues只是比fielddata慢一点,大概10-25%,则带来了更多的稳定性。

### 三级标题:Docker Compose ELK Stack 网络与版本冲突修复 在使用 `docker-compose.yml` 配置 ELK 栈时,若指定的 `version: '3.8'` 并出现网络错误 `service "logstash" refers to undefined network elk-net`,说明服务中引用了未定义的自定义网络。为确保容器间的通信正常运行,必须在 `networks` 段显式声明所有自定义网络[^1]。 #### 正确配置自定义网络 在 YAML 文件顶部添加如下网络声明: ```yaml networks: elk-net: driver: bridge ``` Logstash、Elasticsearch 和 Kibana 的服务配置需统一使用该网络: ```yaml logstash: image: docker.elastic.co/logstash/logstash:7.2.0 networks: - elk-net ports: - "5044:5044" - "9600:9600" depends_on: - elasticsearch ``` --- #### 完整 ELK 栈配置示例(适配 Logstash 7.2.0) 以下是一个适用于 Logstash 7.2.0 的 `docker-compose.yml` 示例片段,确保兼容性与功能性: ```yaml version: '3.8' networks: elk-net: driver: bridge services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0 container_name: elasticsearch environment: - discovery.type=single-node ports: - "9200:9200" - "9300:9300" networks: - elk-net logstash: image: docker.elastic.co/logstash/logstash:7.2.0 container_name: logstash ports: - "5044:5044" - "9600:9600" volumes: - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf depends_on: - elasticsearch networks: - elk-net kibana: image: docker.elastic.co/kibana/kibana:7.2.0 container_name: kibana ports: - "5601:5601" depends_on: - elasticsearch networks: - elk-net ``` --- #### 日志管道无输出问题排查 若 Logstash 启动成功但数据无法写入 Elasticsearch 或控制台无输出,可检查日志确认是否因安全认证失败导致: ``` [security_exception] unable to authenticate user [elastic] for REST request ``` 此错误表明 Logstash 使用的账号权限不足或未正确配置安全凭证。可在 Logstash 输出插件中明确指定用户名和密码: ```ruby output { elasticsearch { hosts => ["http://elasticsearch:9200"] user => "elastic" password => "your_secure_password" } } ``` 此外,确保 Elasticsearch 已关闭安全验证或已正确配置 TLS 加密与用户权限管理[^2]。 --- #### 网络连通性验证 进入 Logstash 容器并测试对 Elasticsearch 的访问能力: ```bash docker exec -it logstash bash curl -v http://elasticsearch:9200 ``` 若返回 `Connection refused`,应进一步检查 Elasticsearch 是否启动成功,并确认其绑定地址是否为 `0.0.0.0` 而非仅限于 `localhost`。 --- #### 版本兼容性建议 Logstash 7.2.0 属于较旧版本,需确保与 Elasticsearch 和 Kibana 的版本一致,避免因协议变更或功能弃用导致不可预见的问题。官方推荐使用相同主版本的组件以保证稳定性。对于新部署项目,建议优先考虑更新至 8.x 系列以获得更好的安全性与性能优化。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值