Elasticsearch 如何用 alias 和 reindex 做零停机重建索引?
简化版
ES 的 mapping 很多变更不能原地修改,常用“新建索引 -> reindex 数据 -> 双写或补增量 -> 原子切换 alias”的方式零停机迁移。业务读写访问 alias,而不是直接访问物理索引名。
详细版
典型流程是:旧索引 products_v1,别名 products 指向它;创建 products_v2 并设置新 mapping;用 _reindex 从 v1 拷贝数据到 v2;处理迁移期间增量写入;最后用 aliases API 在一个原子操作里移除旧指向、添加新指向。
POST /_aliases
{
"actions": [
{ "remove": { "index": "products_v1", "alias": "products" } },
{ "add": { "index": "products_v2", "alias": "products" } }
]
}
面试要强调:alias 切换是关键,reindex 不是自动同步,增量数据和回滚方案要设计。
完整版教学
一、为什么需要重建索引
Elasticsearch 的很多 mapping 变更不能直接在原字段上修改。例如字段从 text 改成 keyword,分词器变更,字段类型从字符串改数字,都需要重建索引。
如果业务直接访问物理索引名,迁移时就会影响调用方。alias 的价值是给业务一个稳定入口,底层索引可以换。
business -> products alias -> products_v1
products_v2 later
记忆钩子:物理索引可换,业务别名不变。
二、基础流程是新建、迁移、切别名
假设当前别名 products 指向 products_v1。迁移步骤:
1. 创建 products_v2,配置新 settings/mapping
2. _reindex: products_v1 -> products_v2
3. 校验文档数量和抽样查询
4. 处理迁移期间增量
5. 原子切换 alias 到 products_v2
6. 观察稳定后删除 products_v1
创建新索引:
PUT /products_v2
{
"settings": { "number_of_shards": 3 },
"mappings": {
"properties": {
"name": { "type": "text" },
"sku": { "type": "keyword" }
}
}
}
三、_reindex 只复制存量,不自动追增量
_reindex 会把源索引中的文档复制到目标索引,但它不是持续同步任务。迁移期间如果业务还在写旧索引,增量数据要单独处理。
POST /_reindex
{
"source": { "index": "products_v1" },
"dest": { "index": "products_v2" }
}
增量处理常见方案:
| 方案 | 做法 | 适合场景 |
|---|---|---|
| 停写窗口 | 短暂停写后 reindex | 小系统、低峰迁移 |
| 双写 | 新旧索引同时写 | 对一致性要求高 |
| 时间戳补偿 | 迁移后按 updatedAt 补一遍 | 有可靠更新时间 |
| 消息回放 | 从 MQ 回放变更 | 写链路事件化 |
面试中提到“增量”两个字,答案就立起来了。
四、alias 切换要用原子 actions
不要先 remove 再 add 分两次请求,否则中间可能出现 alias 短暂不存在。应该用 _aliases API 一次提交多个 actions。
POST /_aliases
{
"actions": [
{ "remove": { "index": "products_v1", "alias": "products" } },
{ "add": { "index": "products_v2", "alias": "products" } }
]
}
如果 alias 用于写入,还要关注 is_write_index。多个索引共享写别名时,必须指定哪个是写索引。
{ "add": { "index": "products_v2", "alias": "products", "is_write_index": true } }
五、校验和回滚不能省
迁移前后要校验:
文档数量是否一致
关键查询结果是否一致
mapping 是否符合预期
别名是否指向正确
写入是否进入新索引
错误日志是否异常
回滚也要简单:只要旧索引还保留,就可以把 alias 切回 products_v1。所以不要迁移成功一秒后立刻删除旧索引。
保留窗口可以按业务决定,例如观察 1 到 7 天后删除。
六、性能和安全边界
_reindex 会消耗源索引查询、目标索引写入、网络和磁盘资源。大索引迁移要低峰执行,使用 slices 并行时也要控制压力。
POST /_reindex?slices=4&wait_for_completion=false
{
"source": { "index": "products_v1" },
"dest": { "index": "products_v2" }
}
wait_for_completion=false 可以让任务异步执行,再通过 tasks API 查看进度。并行切片不是越多越好,要看集群吞吐。
七、常见误区与追问
- 误区:mapping 改不了就直接删索引重建。 生产应使用新索引、reindex、alias 原子切换,避免停机。
- 误区:_reindex 会自动同步增量。 它只处理任务期间扫描到的数据,增量写入要额外设计。
- 误区:alias 切换分两步也没关系。 分两次请求可能出现空窗,应使用
_aliases原子 actions。 - 追问:
is_write_index有什么用? 当别名指向多个索引时指定写入目标,避免写入歧义。 - 追问:怎么回滚? 保留旧索引,把 alias 原子切回旧索引,并排查增量差异。
- 追问:大索引 reindex 怎么减少影响? 低峰执行、异步任务、限速或 slices,并监控写入和查询压力。
八、加强记忆
零停机重建索引记成“业务走别名,物理建新版,存量 reindex,增量兜住,原子切换”。Alias 是稳定入口,reindex 只是搬存量,真正难点是迁移期间写入、校验和回滚。面试里只说 reindex 不够,必须把 alias 原子切换和增量方案讲出来。