在企业级搜索应用中,随着业务的发展,数据模型(Mapping)往往需要迭代更新。然而,Easysearch(以及 Elasticsearch)底层的倒排索引一旦生成,其字段类型和分词方式便不可直接修改。面对“修改字段类型”、“调整分片数量”或“开启数据压缩”等需求时,数据重建(Reindex) 是必经之路。
如何在 TB 级海量数据重建过程中,保证业务不中断、查询不抖动?本文将详细介绍基于 Alias(别名) 和 Reindex API 的零停机索引升级方案,并结合 INFINI Easysearch 的特性分享性能优化技巧。
为什么我们需要 Reindex? #
在 Easysearch 中,以下场景通常必须进行数据重建:
- 修改 Mapping 定义:例如将
keyword类型改为text以支持全文检索,或者修改分词器(如从标准分词切换到 IK 分词或 Easysearch 的新一代分析器)。 - 调整主分片数(Number of Shards):主分片数在索引创建后不可更改。为了扩容或缩容(Shrink),必须重建索引。
- 开启高级压缩:如利用 Easysearch 的 ZSTD 压缩特性(可节省 40%-50% 存储空间),需要通过重建将旧数据写入开启了压缩配置的新索引。
- 数据清洗与迁移:将旧版本的 Elasticsearch 数据迁移至 Easysearch,或对现有数据进行字段重命名、逻辑删除等 ETL 操作。
核心策略:别名(Alias)设计模式 #
要实现“零停机”,核心在于解耦应用层与物理索引。应用层不应直接访问物理索引名(如 logs-2023-v1),而应访问别名(如 logs-search)。
零停机升级流程如下:
- 初始状态:别名
my_index指向物理索引my_index_v1。 - 创建新索引:创建配置优化后的
my_index_v2。 - 数据同步:使用 Reindex API 将数据从
v1复制到v2。 - 原子切换:使用 Alias API 原子性地将
my_index指向v2并移除v1。 - 收尾:验证无误后,删除
my_index_v1释放空间。
实战演练:Reindex 操作指南 #
假设我们要对一个存储商品数据的索引进行升级,开启 Easysearch 独有的 ZSTD 压缩,并修改标题字段的分词方式。
第一步:创建新索引(v2) #
在创建新索引时,我们可以应用 Easysearch 的最佳实践,例如开启 ZSTD 压缩以降低存储成本。
PUT /products_v2
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"index.codec": "ZSTD"
},
"mappings": {
"properties": {
"product_name": {
"type": "text",
"analyzer": "ik_max_word"
},
"price": {
"type": "double"
},
"created_at": {
"type": "date"
}
}
}
}
第二步:执行 Reindex 数据同步 #
使用 _reindex 端点将数据从 v1 迁移到 v2。
基本用法:
POST /_reindex
{
"source": {
"index": "products_v1"
},
"dest": {
"index": "products_v2"
}
}
进阶用法(脚本处理与查询过滤):
如果在迁移过程中需要修改数据(例如将价格上涨 10%),或者只迁移部分数据,可以结合 Script 和 Query:
POST /_reindex
{
"source": {
"index": "products_v1",
"query": {
"range": {
"created_at": {
"gte": "2023-01-01"
}
}
}
},
"dest": {
"index": "products_v2"
},
"script": {
"source": "ctx._source.price += 10"
}
}
第三步:性能优化(关键) #
对于海量数据,Reindex 可能会非常耗时且消耗资源。Easysearch 提供了多种参数来加速这一过程:
- 使用 Slicing(并行处理):
默认情况下 Reindex 是单线程的。设置slices可以将其拆分为多个并行任务。通常设置为auto或等于分片数。
POST /_reindex?slices=auto&wait_for_completion=false
注意:wait_for_completion=false_ 会让任务在后台异步执行,返回一个 Task ID 用于查询进度。_
- 调整 Batch Size:
通过size参数调整每次批量写入的文档数(默认为 1000)。在 Easysearch 中,由于写入性能优化较好,通常可以适当调大该值(如 2000-5000)。 - 临时禁用副本与刷新:
为了极致的写入速度,可以在 Reindex 开始前,将目标索引(v2)的refresh_interval设为-1,并将number_of_replicas设为0。待迁移完成后再恢复,这可以显著提升重建速度。
第四步:原子切换别名 #
这是实现“零停机”的最关键一步。_aliases 接口的操作是原子性的,意味着在客户端看来,服务从未中断,也没有瞬间查询不到数据的情况。
POST /_aliases
{
"actions": [
{
"add": {
"index": "products_v2",
"alias": "products"
}
},
{
"remove": {
"index": "products_v1",
"alias": "products"
}
}
]
}
第五步:验证与清理 #
- 检查新索引的数据量是否一致:
GET _cat/count/products_v2。 - 通过别名测试查询:
GET products/_search。 - 确认无误后,删除旧索引:
DELETE products_v1。
跨集群迁移(Remote Reindex) #
Easysearch 的 Reindex API 不仅支持本地索引重建,还完美支持从远程集群(如旧版本的 Elasticsearch 集群)迁移数据。这使得 Easysearch 成为国产化替代或架构升级的理想选择。
配置要求:
在 Easysearch 的 easysearch.yml 中配置白名单:
reindex.remote.whitelist: "192.168.1.10:9200, 192.168.1.11:9200"
执行迁移:
POST /_reindex
{
"source": {
"remote": {
"host": "http://192.168.1.10:9200",
"username": "user",
"password": "password"
},
"index": "legacy_index",
"size": 2000
},
"dest": {
"index": "new_easysearch_index"
}
}
提示:Easysearch 对写入进行了深度优化,在从 ES 迁移数据时,瓶颈通常在于源集群的读取速度,而非 Easysearch 的写入速度。
常见问题与注意事项 #
_source** 字段必须启用**:Reindex 依赖源索引的_source字段。如果源索引禁用了_source,则无法进行重建。- 版本冲突处理:如果在重建过程中源数据还在不断更新,可能会遇到版本冲突。可以设置
"conflicts": "proceed"来忽略版本冲突,或者在业务低峰期进行。 - 避免 OOM(内存溢出):虽然 Easysearch 相比 ES 拥有更完善的断路器机制和内存管理,但在处理超大规模 Reindex 时,建议配合
requests_per_second参数进行限流,防止瞬间流量过大影响集群稳定性。 - 长连接超时:对于耗时极长的任务,建议使用异步模式(
wait_for_completion=false),并通过GET /_tasks/{task_id}监控进度。
总结 #
利用 Reindex API 配合别名机制,Easysearch 能够帮助企业轻松应对业务需求变更和架构升级。无论是为了利用 Easysearch 的 ZSTD 压缩降本增效,还是为了实现国产化替代的平滑迁移,掌握这一套标准化的数据重建流程都是运维与开发人员的必备技能。
得益于 Easysearch 在写入性能和稳定性上的内核级优化,即使是 TB 级别的数据重建,也能在更短的时间内完成,且对在线业务的影响降至最低。





