当前位置: 首页 > news >正文

ES_flattened

一、 flattened 类型是什么?为什么需要它?

flattened 是一种特殊的映射类型,它将一个完整的 JSON 对象“扁平化”处理,将其所有子字段的值合并为一个字段,并将这个字段索引为一个统一的 keyword 类型的集合。

核心思想: 牺牲字段级查询的精度,换取映射的简洁性、存储的效率和对未知字段的包容性。

1. 面临的问题:映射爆炸

想象一个日志系统,每条日志都有一个 headers 字段,里面是来自不同客户端、浏览器的 HTTP 请求头。这些请求头的键(如 User-Agent, X-Forwarded-For, Accept-Language, 以及各种自定义头)千变万化。

如果使用默认的动态映射("dynamic": "true"),Elasticsearch 会为每一个新出现的关键字(如 headers.User-Agent, headers.X-Forwarded-For)创建一个新的映射字段。对于一个大型系统,这可能会在短时间内产生成千上万个字段。后果是:

  • 集群状态(Cluster State)膨胀:映射信息存储在集群状态中,并同步到每个节点。过大的映射会使集群状态变得臃肿,导致管理操作(如创建新索引)变慢,甚至引发稳定性问题。
  • 内存压力:大量的字段会消耗大量的 JVM 堆内存。
2. flattened 提供的解决方案

使用 flattened 类型,你只需要定义一个字段,例如 headers,类型为 flattened。无论传入的对象有多少个键值对,ES 都不会为内部的键(如 User-Agent)创建独立的映射条目。整个 headers 对象在映射中只表现为一个字段。

它将:
{"headers": {"User-Agent": "Mozilla/5.0", "X-Custom-ID": "abc123"}}

索引为类似于:
["User-Agent": "Mozilla/5.0", "X-Custom-ID": "abc123"] (概念上,非实际存储格式)

所有子字段的值都被视为 keyword,这意味着它们可以被搜索、过滤和聚合,但不能被分词,也不能进行范围查询等需要特定数据类型的操作。


二、 flattened 类型的工作原理与特性

  1. 存储机制:将整个 JSON 对象扁平化为一系列键值对,并将所有值作为 keyword 类型进行处理和索引。
  2. 映射开销:无论子字段有多少,在映射中只计为一个字段。这是它解决映射爆炸的关键。
  3. 查询能力
    • 支持:精确匹配查询(term, terms)、前缀查询(prefix)、存在性查询(exists)等基于 keyword 的查询。
    • 不支持:全文搜索(match query)、数值范围查询(range on numbers)、日期查询等。因为 ES 不知道也不关心子字段值的具体数据类型。

三、 结合实际案例:电商平台商品属性与日志处理

案例一:电商商品动态规格(Specifications)

在电商系统中,不同品类的商品拥有完全不同的属性。手机的属性是 ["cpu", "memory", "screen_size"],而衣服的属性是 ["color", "size", "material"]。为每个属性预先定义映射是不现实的。

1. 映射设计:
我们使用 flattened 类型来存储这些动态变化的规格参数。

PUT /products
{"mappings": {"properties": {"title": {"type": "text"},"price": {"type": "float"},"specs": { // 动态规格属性,使用flattened类型"type": "flattened"}}}
}

2. 索引文档:
插入一个手机和一个衣服商品。

// 插入一部手机
POST /products/_doc/1
{"title": "高性能智能手机","price": 3999,"specs": {"cpu": "骁龙8 Gen 2","memory": "12GB","storage": "256GB","screen_size": "6.7英寸"}
}// 插入一件衣服
POST /products/_doc/2
{"title": "纯棉T恤","price": 99,"specs": {"color": "白色","size": "L","material": "100%棉"}
}

映射中始终只有 title, price, specs 三个字段,尽管 specs 对象内部结构完全不同。

3. 查询示例:

  • 查找所有内存为 12GB 的手机:

    GET /products/_search
    {"query": {"term": {"specs": "12GB"} // 注意:这是在整个flattened字段中搜索值}
    }
    

    这个查询可能会找到内存是12GB的手机,也可能找到描述中有“12GB”的其他商品,不够精确。

  • 更精确的查找:查找 CPU 是“骁龙8 Gen 2” 且 内存是“12GB”的商品:

    GET /products/_search
    {"query": {"bool": {"must": [{"term": {"specs.cpu": "骁龙8 Gen 2" // 使用点号语法指定子字段}},{"term": {"specs.memory": "12GB"}}]}}
    }
    

    这才是 flattened 类型的正确用法:通过 flattened_field_name.sub_field_name 来精确查询特定的键值对。ES 虽然不为 specs.cpu 创建映射,但在内部索引中仍然保留了键和值的关系,支持这种形式的查询。

案例二:集中式日志管理(如 HTTP Access Log)

1. 映射设计:

PUT /app-logs-2023-10
{"mappings": {"properties": {"@timestamp": {"type": "date"},"message": {"type": "text"},"http": {"type": "flattened" // 存储HTTP请求和响应信息},"user_agent": { // 单独拿出来,因为可能需要单独分析"type": "text"}}}
}

2. 索引一条日志:

POST /app-logs-2023-10/_doc
{"@timestamp": "2023-10-27T10:00:00.000Z","message": "GET /api/v1/products HTTP/1.1 200 1234","http": {"request": {"method": "GET","headers": {"Accept": "application/json","X-Request-ID": "abc-123"}},"response": {"status_code": 200,"body": {"bytes": 1234}}},"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
}

3. 查询示例:

  • 查找所有包含特定追踪ID的请求:

    GET /app-logs-2023-10/_search
    {"query": {"term": {"http.request.headers.X-Request-ID": "abc-123"}}
    }
    
  • 统计所有非200状态的请求:

    GET /app-logs-2023-10/_search
    {"query": {"bool": {"must_not": [{"term": {"http.response.status_code": 200}}]}}
    }
    

四、 架构师视角:flattened 的优缺点与最佳实践

优点:
  1. 防止映射爆炸:这是其最核心的价值,完美解决了不可知字段或海量动态字段带来的映射膨胀问题。
  2. 减少存储开销:相比为每个子字段创建完整映射,flattened 存储更高效。
  3. 保持一定的查询能力:虽然功能受限,但仍支持关键的精确查询和聚合,在很多时候已经足够。
缺点与限制:
  1. 查询功能受限:无法进行全文搜索、数值计算和范围查询(数字被当作关键字)。你不能对 flattened 里的数字子字段做 gte/lte 范围过滤。
  2. 不支持高亮:无法对 flattened 字段的内容进行高亮显示。
  3. 索引开销:由于需要索引所有内容,写入时可能会比严格的 keyword 映射稍慢。
最佳实践与决策树:
  1. 何时使用 flattened

    • 数据源是用户生成的,其结构不受你控制(如表单提交、API参数)。
    • 字段名是动态的、不可枚举的(如 HTTP 头、标签、商品属性)。
    • 你主要需要对字段进行存在性检查、精确匹配和简单的聚合
    • 你的首要目标是控制映射大小,维护集群健康度
  2. 何时使用其他方案?

    • 需要对子字段进行全文搜索 -> 使用动态映射 + text 子字段(如果字段数可控)。
    • 需要对子字段进行数值/日期范围查询 -> 考虑在应用层提前解析,并将重要字段作为顶级字段单独定义。
    • 数据结构是固定的、已知的 -> 使用明确的 object 类型并预定义子字段。
    • 数组中的对象需要独立查询(如博客评论)-> 使用 nested 类型。

决策流程:
遇到动态对象 -> 需要防止映射爆炸吗? -> -> 只需要精确匹配/存在性查询吗? -> -> 使用 flattened
-> 需要范围查询/全文搜索吗? -> -> 考虑应用层处理或忍受映射爆炸的风险。

总结

flattened 类型是 Elasticsearch 映射工具箱中一把非常专业的“手术刀”。它并非万能,但在特定的场景下——即需要高效、安全地处理大量未知或动态字段,且查询模式较为简单时——它是无可替代的最佳选择。

将其与 objectnested 和动态映射策略结合使用,可以构建出既灵活又稳健的数据模型,从容应对真实世界中海量复杂数据的挑战。

http://www.dtcms.com/a/342066.html

相关文章:

  • Nacos部署微服务
  • Python机器学习入门:用scikit-learn构建你的第一个预测模型
  • 安装nvtop编译报错:fatal error: linux/kcmp.h: No such file or directory
  • 亚远景科技助力力邦合信通过ASPICE CL2评估
  • 今日科技焦点 | A股科技芯片受追捧,美股科技股承压——技术赛道的资本与市场博弈
  • 云计算下数据隐私保护系统的设计与实现(LW+源码+讲解+部署)
  • 2025高性能氢气传感器领域的创新引领者:杭州德克西智能科技有限公司
  • 云计算学习笔记——用户和组的管理、周期性任务篇
  • ECCV 2020 | 动态图像在3D MRI图像阿尔茨海默病分类中的应用
  • Wireshark USRP联合波形捕获
  • 软件测试体系建设要怎么做
  • 手写MyBatis第28弹:告别代理,直击本质:手写MyBatis SqlSession的增删改查奥秘
  • 深入解析HashMap的存储机制:扰动函数、哈希计算与索引定位
  • Halcon那些事:Halcon非常核心的1个概念reduce_domain算子的理解和1个详细的使用示例
  • Nginx缓存配置指南:使用proxy_cache为动态网站提速10倍
  • WPF中UI线程频繁操作造成卡顿的处理
  • Ingress控制器深度解析:Nginx与Traefik实战指南
  • 【DICOM HL7】DICOM hl7协议的哪个字段对应操作者,操作者ID?
  • C++析构函数
  • Linux下Docker版本升级保姆攻略
  • 结合 Flutter 和 Rust 的跨平台开发方案
  • 微软Auzre云的技术支持运营模式是什么
  • Flutter - UI布局
  • Android APP防止应用被动态调试
  • 大数据毕业设计选题推荐-基于大数据的北京气象站数据可视化分析系统-Hadoop-Spark-数据可视化-BigData
  • 浏览器【详解】页面加载过程(含页面加载时序图,页面加载性能优化方案)
  • 搭建我的世界mc服务器全流程——阿里云游戏攻略
  • 09_测试与性能优化
  • 新型犯罪浪潮下的法律迷局:网络、AI与跨境犯罪解析
  • 惯性导航中的IMU传感器是什么?