Elasticsearch 6.2.4中clientip字段映射冲突问题排查求助
让我来拆解下你遇到的两个问题,以及背后的原因和正确的解决方式:
第一个错误:Fielddata 禁用的问题
你执行基数聚合时收到的错误,核心原因是**clientip字段是text类型**,而Elasticsearch默认对text字段禁用了fielddata——这是因为fielddata会把分词后的内容加载到内存,不仅内存开销大,更关键的是:IP地址作为text字段会被分词(比如192.168.1.1会被拆成192、168、1、1),用它做基数聚合根本得不到正确的唯一IP数量!
Elasticsearch提示里说的“使用keyword字段”才是正确的思路,因为IP是精确值,不需要分词,应该用不分词的keyword类型来做聚合。
第二个错误:映射更新冲突的问题
你尝试更新映射时出现的冲突,是因为现有clientip字段的norms参数被设置为disabled(大概率是Logstash自动生成映射时的默认配置),而你新的映射里没有指定norms,Elasticsearch会默认开启norms,这就和原有映射的配置冲突了——一旦字段的映射已经创建,像norms这类参数是不能修改的,除非重建索引。
正确的解决路径
优先方案:用keyword子字段做聚合(无需修改映射)
如果你的clientip字段是Logstash自动生成的映射,默认情况下text类型会附带一个keyword子字段(格式为clientip.keyword),这个子字段是不分词的,完全适合做基数聚合。直接修改你的聚合查询即可:
{ "size": 0, "aggs" : { "distinct_ips" : { "filter" : { "term": { "company" : "XXX" } }, "aggs" : { "cardinality" : { "cardinality": {"field": "clientip.keyword" } } } } } }
彻底方案:修改Logstash配置,正确设置字段类型
如果想从根源上解决,修改你的Logstash解析配置,把clientip字段设置为keyword类型(或者同时保留text和keyword,但聚合用keyword),这样新生成的索引里clientip的映射就适合精确聚合了。示例配置片段:
# 在filter阶段确保字段类型正确,或者更推荐用索引模板 output { elasticsearch { hosts => ["your-es-host"] index => "logstash-%{+YYYY.MM.dd}" template => "/path/to/your/template.json" template_overwrite => true } }
模板里的clientip配置(如果需要同时支持全文搜索和精确聚合):
{ "mappings": { "_doc": { "properties": { "clientip": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } } } }
临时应急方案(不推荐):如果必须修改现有索引的映射
如果你一定要在现有索引上开启fielddata,需要保持norms参数和原有一致,比如原有映射里norms是false,那么更新命令要加上这个配置:
PUT /*/_mapping/_doc?update_all_types { "properties": { "clientip": { "type": "text", "fielddata": true, "norms": false } } }
但再次强调:这种方法内存开销大,而且聚合结果可能不准确(因为text字段会分词),只适合临时应急,不建议长期使用。
内容的提问来源于stack exchange,提问作者chrisan

