ES 系列 #04:ES 写入最佳实践

发布于 2026-05-26 10:25 👁 6 次阅读
#性能#elasticsearch#best-practice#indexing

从 Mapping 设计、索引模板、Bulk 批写、Refresh/Translog 调优四个层面系统梳理 ES 写入性能优化手段,覆盖从"数据建模"到"参数配置"的全链路实践。


目录

章节 说明
Mapping 设计 字段类型选择、动态映射控制
索引模板与滚动策略 时序索引、冷热分离
Bulk 批量写入 批大小选择、自动 ID
Refresh 与 Translog 调优 降低刷盘频率提升吞吐
段合并策略 写入与合并的 Trade-off
副本与写入 副本数对写入的影响
性能基准参考 不同机型的写入 QPS 参考值

Mapping 设计

字段类型选择

场景 推荐类型 原因
精确匹配(ID、状态码) keyword term 查询性能比数值类型快 3-5 倍
全文检索 text 分词建倒排索引
数值仅做精确 term 查询 keyword 数字类型 term 查询性能差,CPU 占用高
数值需要范围查询或聚合 long / double BKD 树结构更适合范围查询
不需要排序/聚合的字段 关闭 doc_values 节省磁盘和内存
不需要全文检索的字符串 关闭 index 不建倒排,节省存储

核心原则:不需要范围查询时优先用 keyword;需要范围查询时才用数值类型。ES 5.x 起数字类型 term 查询性能相比 2.x 下降约 80%,CPU 利用率飙升 30%+。

Schema 扁平化

关键 Mapping 参数

{
  "mappings": {
    "dynamic": "strict",
    "_source": {
      "enabled": true
    },
    "properties": {
      "id": { "type": "keyword" },
      "price": { "type": "double" },
      "name": {
        "type": "text",
        "fields": {
          "keyword": { "type": "keyword", "ignore_above": 256 }
        }
      },
      "tag": {
        "type": "keyword",
        "doc_values": false
      }
    }
  },
  "settings": {
    "index.mapping.total_fields.limit": 1000,
    "index.mapping.depth.limit": 20,
    "index.mapping.nested_fields.limit": 50
  }
}
参数 默认值 说明
dynamic: strict - 禁止写入未定义字段,防止字段爆炸
total_fields.limit 1000 单索引最大字段数
depth.limit 20 嵌套对象最大深度
nested_fields.limit 50 nested 字段最大数量

_source 的作用与取舍

_source 是 ES 写入时单独保存的原始 JSON 副本,与倒排索引、Doc Values 并存,互相独立:

写入一条文档时,ES 同时构建:
  ├─ 倒排索引  → 用于搜索
  ├─ Doc Values → 用于排序/聚合
  └─ _source   → 存储原始 JSON,用于返回文档内容 / update / reindex / highlight

_source 在磁盘上以**压缩 JSON(LZ4)**存储,占索引总体积的 30%~50%。

_source: false 的影响

功能 是否依赖 _source
搜索命中(返回文档内容) ✅ 依赖,关闭后返回空
_update 部分字段更新 ✅ 依赖,关闭后报错
reindex 跨索引迁移 ✅ 依赖,关闭后无法迁移
highlight 高亮 ✅ 依赖
搜索匹配本身(召回) ❌ 不依赖(走倒排索引)
排序/聚合 ❌ 不依赖(走 Doc Values)

_source: false 几乎没有合适的使用场景——节省的存储空间换来的是一堆功能残废,且无法恢复(不存就是没了)。

真正有用的是 includes/excludes 裁剪:只存需要的字段,兼顾存储节省与功能完整:

"_source": {
  "includes": ["id", "name", "price", "status"],
  "excludes": ["raw_content", "embedding_vector"]
}

⚠️ 如需部分字段更新,必须保留 _source: true(或至少 includes 包含相关字段),否则 _update 请求会报错。


索引模板与滚动策略

什么时候用模板

按时间滚动的好处

分片大小规范

索引别名

# 创建索引时绑定别名
PUT /orders-2024-01
{
  "aliases": {
    "orders": {}
  }
}

# 原子切换别名(零停机)
POST /_aliases
{
  "actions": [
    { "remove": { "index": "orders-2024-01", "alias": "orders" }},
    { "add":    { "index": "orders-2024-02", "alias": "orders" }}
  ]
}

⚠️ 别名绑定的索引不宜超过 20 个,否则查询需要遍历过多分片,影响性能。要定期解绑或清理历史索引。


写入优化

es write optimization

Bulk 批量写入

核心原则

自动 ID vs 自定义 ID

// 推荐:自动生成 ID,避免 Get-before-Write 查重
POST /my_index/_doc
{
  "field": "value"
}

// 不推荐(如无必要):指定 ID 时 ES 需先查重
PUT /my_index/_doc/custom_id_123
{
  "field": "value"
}

使用自定义 ID 会触发写入前查重(Get-before-Write),增加额外开销,尽量使用 ES 自动生成的文档 ID

Index Buffer 调优

# elasticsearch.yml
indices.memory.index_buffer_size: 15%

写入期间数据先缓存在 index buffer,适当增大此值可提高写入效率(默认 10%)。


Refresh 与 Translog 调优

Refresh 策略

ES 默认每 1 秒 refresh 一次(将内存数据刷到文件系统缓存),每次 refresh 产生一个新 segment。

# 对实时性要求不高的索引,延长 refresh 间隔
PUT /my_index/_settings
{
  "index": {
    "refresh_interval": "30s"
  }
}

# 大批量导入时,临时关闭 refresh
PUT /my_index/_settings
{
  "index.refresh_interval": "-1"
}
# 导入完成后恢复
PUT /my_index/_settings
{
  "index.refresh_interval": "1s"
}

Translog 异步策略

Translog 默认每次写入都同步刷盘(request 模式),改为异步可大幅提升写入吞吐:

PUT /my_index/_settings
{
  "index.translog.durability": "async",
  "index.translog.sync_interval": "60s"
}
参数 默认值 推荐值 说明
translog.durability request async 异步刷盘,提升吞吐
translog.sync_interval 5s 60s 刷盘间隔,可适当调大

⚠️ 异步模式下节点崩溃可能丢失最近 sync_interval 内的数据,非强一致性场景下才适用。


段合并策略

为什么要合并段

每次 refresh 产生一个新 segment,segment 过多会:

手动强制合并

# 对不再写入的历史只读索引执行强制合并,极大提升查询速度
POST /my_index/_forcemerge?max_num_segments=1

⚠️ 不要对正在写入的索引执行 forcemerge,会消耗大量 I/O,影响正常写入和查询。避免在高峰期执行。

提升段合并速度(SSD 场景)

PUT /_cluster/settings
{
  "persistent": {
    "indices.store.throttle.max_bytes_per_sec": "100mb"
  }
}

默认合并速度限制为 20 MB/s,SSD 可调高到 100 MB/s。


副本与写入

副本越多,写入越慢(每条数据需同步到所有副本节点)。

# 大批量导入前临时关闭副本
PUT /my_index/_settings
{
  "index.number_of_replicas": 0
}

# 导入完成后恢复
PUT /my_index/_settings
{
  "index.number_of_replicas": 1
}

关闭副本期间集群无容灾能力,仅适用于全量数据导入场景,增量写入不建议关闭副本。


性能基准参考

以下为简单写入场景、单节点压测参考值,复杂场景需自行压测。索引大小 50 GB,文档数 2 亿+,字段数 8 个。

机器类型 配置 单条写入 QPS 单条写入 avg(ms) Bulk QPS(bulk=100) Bulk avg(ms)
SSD(8C 16G) VM/物理机 ~500 10-13 ~100 18-20
高性能 SSD(16C 32G) - ~1000 9 ~200 19

结论


参考资料

← 返回列表

评论 (0)

暂无评论,来留下第一条吧。

发表评论