配置和使用同义词

更新时间:
复制 MD 格式

在搜索场景中,用户使用不同词语表达同一概念(例如“手机”与“智能手机”),可能导致搜索结果不全。同义词功能将这些词语视为等同,以扩展搜索范围,提升搜索召回率和用户体验。

操作前检查清单

在执行同义词文件变更前,务必确认以下事项:

  • 确认关键索引至少拥有一个副本分片,以保障服务在单节点重启期间的可用性。(删除词典时将引起集群重启)

  • 监控并确认集群负载处于健康水平(建议:CPU使用率 < 60%,堆内存使用率 < 50%)。

    连接集群,通过GET /_nodes/stats/jvm?filter_path=nodes.*.jvm.mem.heap_*查看所有节点的CPU和堆内存使用率。

工作原理与决策指南

理解同义词的两种配置方式及其利弊,有助于做出正确的技术选型。

同义词语法规则

同义词文件必须为 UTF-8 编码的 .txt 文件。文件中的每一行定义一组同义词规则,支持以下两种格式:

  • 等价同义词(Solr格式)
    用英文逗号分隔的词语将被视为完全等价。搜索其中任意一个词,都会匹配包含这组词中任何一个的文档。

    # 示例:搜索“手机”、“智能手机”或“移动电话”时,效果相同。
    手机,智能手机,移动电话
    ipod,i-pod,i pod
  • 定向映射(WordNet格式)
    使用 => 将一组词语映射到一个标准词,通常用于归一化,即将不规范的用词指向规范用词。

    # 示例:将"usa"和"us"都映射到“美国”。
    usa,us => 美国

两种配置方式对比与决策

支持通过上传文件或在索引配置中内联定义同义词两种方式,两种方式的对比如下表所示。

对比项

方式一:上传文件

方式二:内联定义

操作方式

上传TXT文件到ES集群,然后在索引settings中通过synonyms_path引用。

直接在索引settingssynonym过滤器中写入同义词规则。

优点

  • 易于管理和复用大型词典。

  • 词典与索引配置解耦,多索引可共享。

  • 更新配置可即时生效。

  • 适合小型、不常变化的同义词集。

缺点

已有索引无法动态加载新词典。

  • 难以复用,每个索引需单独定义。

  • 不适合管理大型或复杂的词典。

适用场景

词典内容稳定、不常变更,且需要在多个索引间共享的场景。

对可用性要求极高,或需要频繁、快速更新同义词的场景。

操作步骤

方式一:通过上传文件配置(可复用)

此方式适合词典相对固定的场景。

步骤一:上传同义词文件

以下示例通过filter过滤器配置同义词,使用测试文件aliyun_synonyms.txt,其内容为:begin, start。

  1. 登录阿里云Elasticsearch控制台,选择目标地域和资源组,然后单击目标实例ID。

  2. 在左侧导航栏,选择 配置与管理 > ES集群配置,在基础配置区域找到同义词配置,单击上传

  3. 在弹出的面板中,单击配置,然后选择上传方式:

    文件后缀必须是.txt,文件名包含大小写字母、数字和下划线,且长度不超过30个字符
    • 上传文件:从本地选择同义词TXT文件。

    • 添加OSS文件:输入Bucket名称和同义词文件名称后,单击添加。

      限制:OSS Bucket必须与ES实例在同一地域。
  4. 单击保存,确认后执行。

步骤二:创建索引并引用同义词文件

等待实例状态恢复为正常,连接集群后创建索引,并配置其使用已上传的同义词文件。

PUT /aliyun-index-test
{
  "settings": {
    "index":{
      "analysis": {
          "analyzer": {
            "by_smart": {
              "type": "custom",
              "tokenizer": "ik_smart",
              "filter": ["by_tfr","by_sfr"],
              "char_filter": ["by_cfr"]
            },
            "by_max_word": {
              "type": "custom",
              "tokenizer": "ik_max_word",
              "filter": ["by_tfr","by_sfr"],
              "char_filter": ["by_cfr"]
            }
         },
         "filter": {
            "by_tfr": {
              "type": "stop",
              "stopwords": [" "]
              },
           "by_sfr": {
              "type": "synonym",
              "synonyms_path": "analysis/aliyun_synonyms.txt"
              }
          },
          "char_filter": {
            "by_cfr": {
              "type": "mapping",
              "mappings": ["| => |"]
            }
          }
      }
    }
  }
}
不同版本的集群,其索引创建语法存在一定差异,请参见ES常见版本操作索引示例

步骤三:配置同义词字段title

  • ES 7.0以下版本

    PUT /aliyun-index-test/_mapping/doc
    {
    "properties": {
     "title": {
       "type": "text",
       "analyzer": "by_max_word",
       "search_analyzer": "by_smart"
     }
    }
    }
  • ES 7.0及以上版本

    PUT /aliyun-index-test/_mapping/
    {
    "properties": {
     "title": {
       "type": "text",
       "analyzer": "by_max_word",
       "search_analyzer": "by_smart"
     }
    }
    }

步骤四:验证同义词效果

使用_analyze API验证分词器是否已正确加载同义词,假设同义词文件包含 begin,start

GET /aliyun-index-test/_analyze
{
"analyzer": "by_smart",
"text":"begin"
}

成功的返回结果应同时包含 begin 和 start 两个词元(token)。

{
  "tokens" : [
    {
      "token" : "begin",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "ENGLISH",
      "position" : 0
    },
    {
      "token" : "start",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "SYNONYM",
      "position" : 0
    }
  ]
}

步骤五:测试搜索效果

  1. 写入两份包含同义词的文档

    PUT /aliyun-index-test/doc/1
    {
    "title": "Shall I begin?"
    }
    PUT /aliyun-index-test/doc/2
    {
    "title": "I start work at nine."
    }
  2. 搜索其中一个词(例如 begin),可同时召回包含 begin 和 start 的文档。

    GET /aliyun-index-test/_search
    {
     "query" : { "match" : { "title" : "begin" }},
     "highlight" : {
         "pre_tags" : ["<red>", "<bule>"],
         "post_tags" : ["</red>", "</bule>"],
         "fields" : {
             "title" : {}
         }
     }
    }

    返回结果:

    {
      "took" : 70,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 2,
          "relation" : "eq"
        },
        "max_score" : 0.28247005,
        "hits" : [
          {
            "_index" : "aliyun-index-test",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 0.28247005,
            "_source" : {
              "title" : "Shall I begin?"
            },
            "highlight" : {
              "title" : [
                "Shall I <red>begin</red>?"
              ]
            }
          },
          {
            "_index" : "aliyun-index-test",
            "_type" : "_doc",
            "_id" : "2",
            "_score" : 0.25069216,
            "_source" : {
              "title" : "I start work at nine."
            },
            "highlight" : {
              "title" : [
                "I <red>start</red> work at nine."
              ]
            }
          }
        ]
      }
    }
    

方式二:在索引设置中内联配置(不便复用)

此方式将同义词规则直接写入索引配置,适合词典小且需要快速迭代的场景。

步骤一:创建索引并定义同义词

连接集群,在创建索引时直接将同义词规则定义在synonyms数组中。

PUT /my_index
{
 "settings": {
     "analysis": {
         "analyzer": {
             "my_synonyms": {
                 "filter": [
                     "lowercase",
                     "my_synonym_filter"
                 ],
                 "tokenizer": "ik_smart"
             }
         },
         "filter": {
             "my_synonym_filter": {
                 "synonyms": [
                     "begin,start"
                 ],
                 "type": "synonym"
             }
         }
     }
 }
}

上述命令创建了一个名为 my_index 的索引,并配置了自定义文本分析,处理流程为:
当文本字段使用 my_synonyms 分析器时,ik_smart 分词器将输入文本切分成词元;lowercase 过滤器将所有词元转换为小写;my_synonym_filter 过滤器将小写后的词元(如 begin 或 start)映射到其同义词组(即两者都被视为同一个规范词)。

步骤二:配置同义词字段title

  • ES 7.0以下版本

    PUT /my_index/_mapping/doc
    {
    "properties": {
     "title": {
       "type": "text",
       "analyzer": "my_synonyms"
     }
    }
    }
  • ES 7.0及以上版本

    PUT /my_index/_mapping/
    {
    "properties": {
     "title": {
       "type": "text",
       "analyzer": "my_synonyms"
     }
    }
    }

步骤三:验证同义词效果

GET /my_index/_analyze
{
 "analyzer":"my_synonyms",
 "text":"Shall I begin?"
}

返回结果:

{
  "tokens" : [
    {
      "token" : "shall",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "ENGLISH",
      "position" : 0
    },
    {
      "token" : "i",
      "start_offset" : 6,
      "end_offset" : 7,
      "type" : "ENGLISH",
      "position" : 1
    },
    {
      "token" : "begin",
      "start_offset" : 8,
      "end_offset" : 13,
      "type" : "ENGLISH",
      "position" : 2
    },
    {
      "token" : "start",
      "start_offset" : 8,
      "end_offset" : 13,
      "type" : "SYNONYM",
      "position" : 2
    }
  ]
}

同义词操作对集群的影响

不同的同义词操作对集群的影响不同。了解以下区别,有助于根据业务需求选择合适的更新方式。

操作方式

是否触发集群重启

说明

增量更新(上传同名文件)

上传与已有文件同名的同义词文件时,属于增量更新(热更新),不触发集群重启

控制台更新同义词词库(新文件名或删除文件)

上传新文件名的同义词文件或删除已有文件后保存,会触发集群滚动重启

增量更新同义词文件(热更新)

当上传的同义词文件与已有文件同名时,系统执行增量更新(热更新),不会触发集群重启。新上传的文件直接覆盖原文件,新创建的索引将自动使用更新后的词库。

说明

增量更新后,已有索引不会自动加载新词库。如需在已有索引中生效,需关闭后重新打开索引(Close/Open),或重建索引。

控制台更新词库触发集群重启

以下操作会触发集群滚动重启:

  • 上传新文件名的同义词文件后保存。

  • 删除已有同义词文件后保存。

若业务需要避免集群重启,建议使用 elasticsearch-analysis-dynamic-synonym 插件实现动态更新机制。

具体操作,请参见阿里云ES实现同义词动态更新

滚动重启期间可能出现以下影响:

  • 服务抖动:节点逐个重启期间,即使有副本分片,也可能出现查询延迟短暂升高或服务抖动。

  • 服务中断风险:集群负载过高或索引没有副本等极端条件下,重启可能导致部分请求失败或短暂服务中断。

  • 重启时长:重启和词库分发的总耗时取决于集群规模、数据量和负载,可能需要几分钟甚至更长时间。

配置生效期间的读写可用性

同义词配置提交后,实例进入生效中状态。在此期间:

  • 实例的读写功能正常可用,不影响现有业务的数据读写操作。

  • 仅同义词扩展功能暂时不生效,使用新同义词规则的搜索查询可能返回不完整的结果。

  • 生效完成后,实例恢复正常状态,新创建的索引将自动使用更新后的同义词词库。

常见问题

配置或更新同义词后集群状态异常(Yellow)或变更卡住怎么办?

配置或更新同义词后,如果集群状态变为 Yellow 或后续变更卡住,通常由以下原因引起:

  • 同义词文件格式不规范:文件中包含大写字母但未转换为小写,导致分析器(Analyzer)解析失败。

  • OpenStorePlugin 报错:同义词文件内容异常触发 OpenStorePlugin 错误,导致分片无法正常分配,集群状态异常并阻塞后续变更。

可以按照以下方法排查和解决:

  1. 检查并修正同义词文件:确认文件中所有单词均已统一转换为小写字母,修正后重新上传。

  2. 增加 lowercase 过滤器:在索引 Settings 的 analyzer filter 配置中增加 lowercase 过滤器,确保分词时自动将词条转换为小写。示例配置:

    "filter": {
      "my_synonym_filter": {
        "type": "synonym",
        "synonyms_path": "analysis/your-dict-name.txt"
      }
    },
    "analyzer": {
      "my_synonyms": {
        "filter": ["lowercase", "my_synonym_filter"],
        "tokenizer": "ik_smart"
      }
    }
  3. 索引无法操作时的恢复:若因修改 Analyzer Filter 导致需要关闭或重建索引且影响生产环境,或在控制台无法执行操作时,可尝试通过强制重新分片等方式恢复集群状态:

    POST /_cluster/reroute?retry_failed=true

使用 analysis-dynamic-synonym 插件进行同义词热更新有哪些风险?

开源插件 analysis-dynamic-synonym 支持从远程或本地文件动态加载同义词,无需重启集群即可使新的同义词规则生效。但该插件存在以下已知风险:

  • 并发缺陷:在高并发查询和写入场景下,该插件可能导致 Elasticsearch 进程死锁,进而引起 CPU 使用率打满和服务不可用。

  • 适用场景限制:在 Serverless 实例或对稳定性要求极高的生产环境中,谨慎使用该插件。启用前,建议先在测试环境中充分评估实际查询和写入负载下的稳定性表现,确认后再部署到生产环境。如果同义词更新频率不高,建议优先使用增量更新方式(上传同名文件),避免引入第三方插件的潜在风险。