Index Template 与 Alias:零停机变更的秘诀

22 May 2026 – wusfe · 3 min read

Index Template 与 Alias:零停机变更的秘诀

凌晨两点,索引 Mapping 有个字段类型错了,需要重建。你把旧索引删了重建,几分钟后监控告警:订单服务全线报错,ES 返回 index_not_found_exception

"重建索引"不难,难的是让上游服务无感知地切换。这一篇讲清楚两个关键基础设施:Index Template 和 Alias——前者管"新索引长什么样",后者管"服务找不找得到新索引"。

Index Template:自动给新索引套上配置

每个运维过 ES 的人都干过这事:创建一个索引,忘记设分片数、忘记配 analyzer、忘记关掉不必要的动态映射。回头发现已经有 200 个分片散落集群,或者某个字段类型又错了。

Index Template 解决的就是"每次建索引都要手动配"的痛点——它会在索引创建时自动应用你预设的配置。

基础用法

PUT /_index_template/orders_template
{
  "index_patterns": ["orders-*"],
  "priority": 100,
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "refresh_interval": "5s",
      "analysis": {
        "analyzer": {
          "ik_smart_analyzer": { "type": "custom", "tokenizer": "ik_smart" }
        }
      }
    },
    "mappings": {
      "dynamic": "strict",
      "properties": {
        "order_id":    { "type": "keyword" },
        "user_id":     { "type": "keyword" },
        "amount":      { "type": "scaled_float", "scaling_factor": 100 },
        "status":      { "type": "keyword" },
        "created_at":  { "type": "date" }
      }
    }
  }
}

现在任何名字匹配 orders-* 的新索引都会自动套上这套配置。建索引只需要:

PUT /orders-2025-06-01
// 不需要写任何 settings 和 mappings,template 自动生效

Component Template:模板复用

多个索引共享同一套配置(比如 analyzer 配置、分片设置),可以抽成 component template:

// 基础设置组件
PUT /_component_template/baseline_settings
{
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "refresh_interval": "5s",
      "codec": "best_compression"
    }
  }
}

// IK 分词器组件
PUT /_component_template/ik_analyzer
{
  "template": {
    "settings": {
      "analysis": {
        "analyzer": {
          "ik_smart_analyzer": { "type": "custom", "tokenizer": "ik_smart" },
          "ik_max_analyzer":   { "type": "custom", "tokenizer": "ik_max_word" }
        }
      }
    }
  }
}

// 组合使用
PUT /_index_template/orders_template
{
  "index_patterns": ["orders-*"],
  "priority": 100,
  "composed_of": ["baseline_settings", "ik_analyzer"],
  "template": {
    "mappings": { ... }
  }
}

Template 优先级

一个索引名可能匹配多个 template,priority 越高越优先。如果多个 template 有冲突字段,高优先级覆盖低优先级。

PUT /_index_template/orders_log_template
{
  "index_patterns": ["orders-*", "logs-*"],
  "priority": 50
}

PUT /_index_template/orders_special_template
{
  "index_patterns": ["orders-special-*"],
  "priority": 200
}

orders-special-2025-06 同时匹配两个 template,但优先级 200 > 50,所以用 orders_special_template 的配置。

Alias:让服务永远只认一个名字

Alias(别名)是 ES 零停机切换的关键。原理很简单:

应用程序 → orders(alias) → orders_v1(实际索引)
                                orders_v2(新索引,准备切换)

创建与切换

// 1. 创建 v1 并绑定 alias
POST /_aliases
{
  "actions": [
    { "add": { "index": "orders_v1", "alias": "orders" } }
  ]
}

// 2. v2 建好,数据迁完,原子切换
POST /_aliases
{
  "actions": [
    { "remove": { "index": "orders_v1", "alias": "orders" } },
    { "add":    { "index": "orders_v2", "alias": "orders" } }
  ]
}

这两个操作是原子的——不存在"alias 同时指向两个索引"或"alias 不指向任何索引"的中间状态。上游服务全程只读 orders,完全不知道底层已经换了索引。

Write Alias:读写分离

写只落到一个索引,读可以从多个历史索引一起查:

POST /_aliases
{
  "actions": [
    { "add": { "index": "orders_v2", "alias": "orders_read" } },
    { "add": { "index": "orders_v1", "alias": "orders_read" } },
    { "add": { "index": "orders_v2", "alias": "orders_write", "is_write_index": true } }
  ]
}

写入时用 orders_write,读取时用 orders_read(同时搜 v1 和 v2)。

Filtered Alias:一个索引,多种视角

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "orders",
        "alias": "orders_paid",
        "filter": { "term": { "status": "paid" } }
      }
    }
  ]
}

应用读 orders_paid 时,ES 自动叠加上 status=paid 的过滤条件,业务代码不用写。

零停机 Reindex 完整流程

把六步串起来:

# 确保 template 正确(新索引会自动套用)
PUT /_index_template/orders_template { ... }

# 创建新索引(template 自动生效)
PUT /orders_v2

# 老数据迁移
POST /_reindex?wait_for_completion=false
{
  "source": { "index": "orders_v1" },
  "dest":   { "index": "orders_v2" }
}

# 检查 reindex 进度
GET /_tasks?detailed=true&actions=*reindex

# reindex 完成后,原子切换
POST /_aliases
{
  "actions": [
    { "remove": { "index": "orders_v1", "alias": "orders" } },
    { "add":    { "index": "orders_v2", "alias": "orders" } }
  ]
}

# 确认 v1 不再有流量后删除
DELETE /orders_v1

真实事故复盘

某电商团队修改了商品索引的 mapping,新增了一个 nested 字段。变更流程:

  1. 创建 products_v15,reindex 迁移数据(约 800 万条,耗时 3 小时)
  2. 凌晨 3 点做完,准备切换 alias
  3. 运维执行了 DELETE /products(忘了有 alias 保护)——无事发生,因为 alias 不是索引
  4. 执行 alias 切换,products alias 从 v14 切到 v15
  5. 搜索服务全部正常——一根蜡烛都没灭

Alias 是 ES 运维的第一道安全网。不用 alias 就上线 = 裸奔。

总结

  • Index Template 确保新建的索引自动符合规范,不会漏配分片数、忘配分词器
  • Component Template 把公共配置抽出来复用,一套 analyzer 配置在所有索引间共享
  • Alias 是零停机切换的核心——应用代码只认 alias 名字,底层索引随便换
  • 两者配合形成闭环:Template 管"新索引怎么建",Alias 管"新索引怎么切"

一句话:新项目上线的第一天,先把 Index Template 和 Alias 配好。这两个东西花 10 分钟,能省掉未来无数个凌晨的应急。