2. Elasticsearch数据模型基础
Elasticsearch 不是关系型数据库,它的数据模型从根上就与“表—行—列”不同。理解这一点,是后续做映射设计、字段分析和查询优化的前提。本节围绕五个相互关联的概念展开:文档导向模型、JSON 文档结构、索引与类型的关系、映射定义,以及动态映射与显式映射的取舍。
文档导向模型:以整份文档为读写单位
在 Elasticsearch 中,最小可存储、可检索、可更新的单元是文档(document)。一份文档通常对应现实中的一个实体,例如一个商品、一条日志、一位用户。文档是自包含的:它把该实体的所有字段放在一起,而不是像关系模型那样把属性拆散到多张表再靠 JOIN 拼回。
这种“文档导向”带来两个直接后果。第一,读写以整份文档为单位:新增是索引一份文档,更新在底层也是“标记旧文档删除 + 写入新版本”,而不是原地改某个字段。第二,检索时不需要跨表连接,一次查询就能拿到完整对象,因此更适合搜索、日志、内容检索这类“按对象取回”的场景。代价是跨实体的强事务与频繁的关联更新不擅长,需要在建模阶段就决定把哪些数据冗余进同一份文档。
JSON 文档结构:字段、类型与嵌套
文档以 JSON 表示,由一组字段名与字段值构成。字段值可以是字符串、数值、布尔、日期、数组或对象。需要特别注意的是:JSON 本身没有类型声明,类型是由映射赋予的。同一个值 "2024-01-01" 可以被当作字符串,也可以被当作日期,取决于映射怎么定义。
数组在 Elasticsearch 中不需要单独声明:任何字段都可以接受多个值,底层按“多值字段”处理。对象则会被“扁平化”成点号路径,例如:
{
"order_id": "A1001",
"customer": { "name": "李雷", "city": "杭州" },
"amount": 268.00,
"tags": ["数码", "加急"]
}
其中 customer.name 与 customer.city 会成为可独立查询的字段。这里有一个常见陷阱:对象数组(object array)在扁平化后会丢失“哪个值属于哪个子对象”的对应关系。例如两个 customer 对象各自带 name 和 city,扁平化后无法判断“李雷”对应的是“杭州”还是“上海”。若这种对应关系重要,应使用 nested 类型,把每个子对象作为独立隐藏文档保存。
索引与类型的关系:从多类型到单类型
索引(index)是文档的逻辑集合,也是分片、副本、映射和查询的基本单位。早期版本允许一个索引下放多个 类型(type),类似一张表。但从 6.x 起单索引只允许一个类型,7.x 起类型概念被彻底移除,8.x 中已不再使用。因此今天的正确心智模型是:一个索引 ≈ 一类文档的集合,类型不再承担建模职责。
如果确实需要区分不同种类的数据,应使用多个索引,例如 orders-2024、logs-2024.06,再通过索引别名或数据流(data stream)统一对外查询。这样既符合单类型约束,也便于按时间滚动和生命周期管理。
映射定义:字段的类型与行为契约
映射(mapping)定义索引中每个字段的类型与处理方式,是文档能否被正确检索的关键。它主要包含:字段类型(text、keyword、long、double、date、boolean、nested 等)、是否索引、是否可被聚合、分词器、日期格式等。
其中最容易混淆的是 text 与 keyword:text 会分词,适合全文检索;keyword 不分词,适合精确匹配、排序和聚合。一个字段可以同时具备两者,用 fields 定义多字段:
PUT /products
{
"mappings": {
"properties": {
"title": { "type": "text", "fields": { "raw": { "type": "keyword" } } },
"price": { "type": "double" },
"created": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss" },
"brand": { "type": "keyword" }
}
}
}
这样 title 用于全文搜索,title.raw 用于精确排序或聚合。映射一旦建立,字段类型基本不可修改,只能新增字段,因此映射设计应在写入数据前完成。
动态映射与显式映射的取舍
动态映射指写入文档时,Elasticsearch 自动为新字段推断类型。例如首次遇到 "price": 268.00 会推断为 float,遇到 "2024-01-01" 会推断为 date。它上手快,适合探索性写入和日志场景。但风险也很明显:推断可能不符合预期(如把编号推断成数值导致前导零丢失)、字段爆炸(大量随机字段撑爆映射)、以及后续无法改类型。
显式映射指在创建索引时手动声明字段类型与行为。它可控、可预测,适合生产环境和有明确查询需求的业务数据。实践中常采用折中策略:对核心业务字段显式定义,对不确定的辅助字段允许动态映射,同时用 dynamic 参数控制行为——true 允许自动添加,false 忽略新字段但仍可查询,strict 遇到未知字段直接报错,适合强约束场景。
keyword 或数值类型;如果只是全文检索,可交给 text 并配置合适分词器。把上述关系串起来看:一份 JSON 文档写入某个索引,索引通过映射决定每个字段如何被解析和检索;映射可以自动生成,也可以显式声明,而生产环境通常选择“核心显式、边缘动态”的折中。理解这条链路,后续的字段类型选择、分词器配置和查询优化才有稳定的落脚点。