📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

在企业级搜索架构中,唯一不变的就是“变化”。随着业务发展,我们经常面临这样的需求:修改索引的分片数(Shards)、调整字段类型(Mapping)、开启 Easysearch 特有的 ZSTD 压缩以节省成本,甚至是将底层引擎从旧版 Elasticsearch 迁移到 Easysearch。

在传统做法中,修改索引结构通常意味着:停机 -> 重建索引 -> 导数据 -> 修改代码配置 -> 重启服务。这对于 7x24 小时运行的核心业务是不可接受的。

今天,我们将介绍 Easysearch 的“索引别名(Index Aliases)”机制,它就像 DNS 之于 IP 地址,接口之于实现类,是你构建高可用、零停机架构的“解耦神器”。


一、 为什么必须使用别名? #

在 Easysearch 中,别名(Alias) 是一个指向一个或多个索引的“软链接”。

如果你的业务代码直接写死了索引名称(例如 order_index_v1),那么当你需要优化结构创建 order_index_v2 时,就必须修改代码并重启。

最佳实践原则:

永远不要在业务代码中直接使用物理索引名称,始终使用别名。

业务程序只认 order_search(别名),至于背后是 order_v1 还是 order_v2,完全由运维在 Easysearch 侧动态控制,业务端无感知。


二、 核心实战:零停机索引重构(Blue/Green 部署) #

假设你正在使用 Easysearch,现有的索引 user_logs_v1 未开启压缩,存储占用较高。现在的目标是:创建一个开启 ZSTD 压缩的新索引 user_logs_v2,并将流量无缝切换过去。

第一步:当前状态 #

应用正在读写别名 user_logs,它指向物理索引 user_logs_v1

GET _alias/user_logs
{
  "user_logs_v1": {
    "aliases": {
      "user_logs": {}
    }
  }
}

第二步:创建新索引(Blue/Green) #

创建优化后的新索引 user_logs_v2,开启 Easysearch 的 ZSTD 压缩(codec: ZSTD)以减少存储空间。

PUT user_logs_v2
{
  "settings": {
    "index.codec": "ZSTD",
    "number_of_shards": 3,
    "number_of_replicas": 1
  },
  "mappings": {
    // ... 最新的 Mapping 定义
  }
}

第三步:数据迁移(Reindex) #

使用 Reindex API 将数据从 v1 迁移到 v2。Easysearch 的 Reindex 效率很高,且支持异步执行。

POST _reindex?wait_for_completion=false
{
  "source": {
    "index": "user_logs_v1"
  },
  "dest": {
    "index": "user_logs_v2"
  }
}

第四步:原子切换(关键步骤) #

这是实现“无感”的核心。我们需要在一个原子操作中,将别名从 v1 移除,并指向 v2。

注意:千万不要先删除别名再添加,那样中间会有几十毫秒的“服务真空期”导致报错。必须使用 _aliases 接口的 actions 列表。

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "user_logs_v2",
        "alias": "user_logs"
      }
    },
    {
      "remove": {
        "index": "user_logs_v1",
        "alias": "user_logs"
      }
    }
  ]
}

结果:对于应用程序而言,它一直在向 user_logs 读写,完全不知道底层存储已经从未压缩的 v1 变成了高效压缩的 v2。


三、 进阶场景:读写分离与滚动管理 #

对于日志类或时序数据,别名的玩法更加高级。我们可以利用别名实现读写分离

1. 滚动写入(Rollover) #

通常我们需要一个指向“当前活跃索引”的别名用于写入(如 logs_write),和一个指向“所有历史索引”的别名用于查询(如 logs_read)。

# 初始化:logs_write 指向 logs-000001
# 所有的 logs-* 都拥有 logs_read 别名

POST logs_write/_rollover
{
  "conditions": {
    "max_age": "7d",
    "max_docs": 10000000,
    "max_size": "50gb"
  }
}

当满足条件时,Easysearch 会自动创建 logs-000002,并将 logs_write 别名指向它,实现自动滚动。

2. 跨集群迁移(利用 Gateway) #

如果你正在从 Elasticsearch 迁移到 Easysearch,或者进行跨集群升级,INFINI Gateway 配合别名是绝佳组合。
你可以在 Gateway 层设置别名路由,将发往旧集群的请求,通过流量双写或灰度切分的方式,逐步引入 Easysearch 集群,实现应用层的完全无感迁移。


四、 常用别名管理命令速查 #

在日常运维中,这几个 DSL 命令非常高频:

1. 查看某个别名指向了哪些索引:

GET _alias/my_app_alias

2. 批量给多个索引添加别名(支持通配符):
例如,给所有 2023 年的日志添加 archive_2023 别名:

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "logs-2023*",
        "alias": "archive_2023"
      }
    }
  ]
}

3. 利用别名进行视图过滤(Filter Alias):
这是一个被低估的功能。你可以创建一个带过滤条件的别名,让业务方只能查看到特定数据,起到权限隔离的作用。

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "users",
        "alias": "active_users",
        "filter": {
          "term": {
            "status": "active"
          }
        }
      }
    }
  ]
}

当用户搜索 active_users 时,Easysearch 会自动强制加上 status=active 的过滤条件。


五、 避坑指南 #

  1. 别名不能与索引同名:如果已经存在一个物理索引叫 products,你不能再创建一个叫 products 的别名。这也是为什么建议物理索引带版本号(_v1)或日期后缀的原因。
  2. 写入限制:如果一个别名指向了多个索引,你不能直接向该别名写入数据(Easysearch 不知道该往哪个物理索引写)。除非显式指定 is_write_index: true 属性。
  3. 清理旧数据:在完成 v1 -> v2 的切换并观察一段时间(如24小时)确认无误后,记得删除旧的 v1 索引以释放磁盘空间。

结语 #

在 Easysearch 的使用中,“索引别名”是区分新手与专家的分水岭之一

通过合理使用别名,你不仅获得了架构上的灵活性,还能充分利用 Easysearch 的 ZSTD 压缩、分片调整等特性不断优化底层存储,而这一切对上层业务都是透明的。

从今天起,检查你的代码配置,把那些写死的索引名,都换成别名吧!