ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ES 7.17.3中文检索:IK分词器安装与自定义词典避坑指南

ES 7.17.3中文检索:IK分词器安装与自定义词典避坑指南

简介:面向在 Windows 环境使用 Elasticsearch 7.17.3 并需要中文分词能力的开发者和运维人员,这份压缩包专门整理了官方渠道有时下载不稳定的 IK 中文分词器插件,使用起来无需再访问外网或手动凑齐依赖包。解压后按照插件目录放置即可,适合本地离线安装和快速验证分词效果。资源包共 19 个文件,体积仅 4.3MB,内部包括 11 个 dic 词典文件、5 个 jar 依赖包、1 个 XML 配置文件、1 个插件描述属性和 1 个安全策略文件;其中 dic 词典覆盖主词典、停用词过滤和扩展词识别,XML 配置可灵活加载自定义词典,适配专业领域词条。已有 1134 人学习下载,适合在本地离线搭建中文检索环境,也能作为团队内共享的 Elasticsearch 分词基础件。相比自行下载依赖或编译源码,这份现成插件省去了网络波动的困扰,目录结构一目了然,后续词库扩展和问题排查都更高效。

1. Windows上的ES 7.17.3中文检索,绕不开IK分词器

在Windows上部署Elasticsearch 7.17.3,只要业务里涉及中文搜索,第一件事就是装一个中文分词器。ES自带的标准分词器对中文是按单字切分的,用户搜“苹果手机”时,倒排索引里被拆成“苹”“果”“手”“机”,真正搜的时候匹配逻辑非常奇怪,召回一堆不相关的文档。elasticsearch-analysis-ik-7.17.3这种IK分词插件,会用词典和歧义算法把文本切成“苹果”“手机”这样的中文词,搜索体验才正常。这个zip包就是为ES 7.17.3编译好的Windows版IK插件,适合刚从原生ES切换到中文场景的开发者,也适合那些装了好几次都没成功的同学。这篇文章会从版本匹配讲起,覆盖安装、自定义词典和几个真实的踩坑点。

2. IK版本匹配原理:为什么ES 7.17.3只能用配套的7.17.3 IK包

很多同学第一次接触IK,看文件名里的“7.17.3”会以为是IK自己的版本号,于是随便下载一个IK往ES里塞。等你重启ES,启动日志里满屏的“illegal version”或者“plugin was built for Elasticsearch 7.16.2”,才意识到这俩版本是强绑定的。IK从5.x开始就放弃了独立版本号,直接跟随ES主版本号,所以ES是7.17.3,IK也必须是7.17.3,而且这个检查发生在ES加载插件的早期阶段,跑都跑不起来。

2.1 插件描述文件是版本命门

ES插件体系里每个插件目录下必须有一个plugin-descriptor.properties,它相当于插件的身份证。ES启动时,会读取这个文件里的elasticsearch.version字段和当前运行版本比对,不一致就直接判定插件非法。IK同样遵守这套规则,所以这个zip包里的核心就是plugin-descriptor.properties,而不是那个jar包。

通常这个文件内容长这样:

name=analysis-ik version=7.17.3 description=IK Analyzer for Elasticsearch elasticsearch.version=7.17.3 java.version=1.8

关键就两行:version和elasticsearch.version。version是插件版本,elasticsearch.version是它要求运行的ES版本,这两个值在官方发布的zip包里是同步的。如果你在网上下载的IK包,第一步不是急着解压,而是先看看这个文件里的elasticsearch.version是不是7.17.3,否则后面全是白费功夫。

你可能会问,既然都是IK,为什么不能跨版本用?因为ES的插件接口内部类版本敏感,IK用到了Lucene内部的Tokenizer接口,而Lucene每个小版本都可能调整API签名。ES 7.17.3内置的Lucene是8.11.2,如果IK是针对7.16.2编译的,它引用的某些Lucene内部类可能已经被改掉,轻则加载失败,重则运行到一半抛NoSuchMethodError。所以这不是ES故意卡你,是二进制兼容性决定了它必须对齐。

2.2 zip包结构:教你识别一个能直接用的插件包

下载来的资源叫elasticsearch-analysis-ik-7.17.3.zip,从文件名看就是一个现成的插件包,不需要你再去编译源码。解压后目录结构大致是:

elasticsearch-analysis-ik-7.17.3/ ├── elasticsearch-analysis-ik-7.17.3.jar ├── plugin-descriptor.properties ├── plugin-security.policy ├── config/ │ ├── IKAnalyzer.cfg.xml │ ├── main.dic │ ├── quantifier.dic │ ├── stopword.dic │ └── ...

这里的jar是IK分词器的实现,plugin-descriptor.properties负责让ES识别它,plugin-security.policy是Java安全策略,在ES这种带安全管理的环境里必须存在,否则插件可能没有权限读取词典文件。config目录下放的是IK的词典资源,包括主词典、量词词典、停用词词典。

有些下载站会把源码包也命名为elasticsearch-analysis-ik-7.17.3.zip,里面全是.java文件和pom.xml。这种包就算你解压进plugins目录,ES也完全不知道它是什么。最直观的判断方法:打开压缩包,如果能看见plugin-descriptor.properties,那就是编译好的插件包;如果只看见src目录和pom.xml,那是源码包,需要自己用构建工具打包,Windows下搞起来非常折腾,不推荐新手碰。

2.3 装之前先确认ES版本和IK包是否匹配

我一般会在解压前先做两个检查,算是这些年装IK下来养成的习惯。第一个是确认ES版本,方法很简单,在ES的bin目录下执行:

D:\elasticsearch-7.17.3\bin\elasticsearch.bat --version

这条命令会输出类似Elasticsearch 7.17.3的字样。如果你是用服务方式启动的ES,也可以看logs目录下的启动日志,第一行基本会带版本号。注意不要看文件目录名就以为是7.17.3,目录名可以随便改,里面的实际版本才靠谱。

第二个检查是看zip包里的插件描述文件。Windows下可以用tar命令直接读取包内文件:

tar -xOf D:\downloads\elasticsearch-analysis-ik-7.17.3.zip plugin-descriptor.properties

如果输出里elasticsearch.version=7.17.3,那就没问题。如果输出是别的版本,直接换包,不要去碰运气。这个检查只花十秒钟,但它能帮你躲掉后面排查半天插件加载失败的坑。

3. 在Windows上安装IK分词器:从解压到第一个中文分词结果

版本对齐之后,安装本身并不复杂,但Windows上有几个细节和Linux不一样,最典型的就是目录层级。Linux上用unzip解压后再移动目录很顺手,Windows上很多人直接用图形界面右键解压,结果插件目录里多套了一层,ES启动时直接找不到插件描述文件。

3.1 安装前的三个准备

动手之前,确认三件事:ES版本是7.17.3,IK包是配套的7.17.3,ES当前处于停止状态。第三点很重要,如果你ES正在运行,插件目录被进程锁住,解压和移动都会失败,而且Windows下ES占用插件目录后,你删都删不掉。

如果ES是通过命令行启动的,按Ctrl+C停掉即可。如果是注册成Windows服务,用管理员权限的PowerShell执行Stop-Service Elasticsearch,具体服务名看你的注册方式。千万别在ES运行的时候直接替换插件目录,可能出现jar被占用导致ES启动后加载的还是旧文件。

3.2 解压到plugins目录的正确姿势

IK插件必须放在ES安装目录的plugins子目录下,而且要保证插件描述文件在plugins\analysis-ik\plugin-descriptor.properties。注意,不是plugins\elasticsearch-analysis-ik-7.17.3\plugin-descriptor.properties,ES会把plugins下每一个子目录当作一个插件,如果目录名里带版本号完全没问题,但如果你直接把zip包里的顶层目录放进plugins,并且它里面还嵌着一层同名目录,ES就找不到了。

我推荐用临时目录中转的方式,避免图形界面解压时的目录嵌套问题。以下PowerShell脚本可以直接跑:

# 1. 把zip解压到临时目录 $zip = "D:\downloads\elasticsearch-analysis-ik-7.17.3.zip" $tmp = "$env:TEMP\ik-$PID" New-Item -ItemType Directory -Path $tmp -Force | Out-Null tar -xf $zip -C $tmp # 2. 把解压出来的目录改名成 analysis-ik 并放到 plugins 下 $esHome = "D:\elasticsearch-7.17.3" Move-Item "$tmp\elasticsearch-analysis-ik-7.17.3" "$esHome\plugins\analysis-ik" # 3. 清理临时目录 Remove-Item $tmp -Recurse -Force

这段脚本里,tar -xf是Windows 10 1803以后自带的BSD tar,支持zip格式。如果你机器上用的是老版本Windows,没有tar,可以直接用Expand-Archive $zip -DestinationPath $tmp代替,效果一样。脚本第二步把移动后的目录改名成analysis-ik,同时保证不存在双重嵌套。执行完以后,可以检查一下:

Test-Path "D:\elasticsearch-7.17.3\plugins\analysis-ik\plugin-descriptor.properties"

输出True说明路径对了,后面重启ES就不会在目录层级上翻车。

3.3 重启ES并验证插件加载

启动方式和以前一样,在bin目录下执行:

D:\elasticsearch-7.17.3\bin\elasticsearch.bat

看到日志输出里带[analysis-ik]的加载信息,就说明插件被ES接受了。如果日志里出现WARN或ERROR,千万别忽略,直接去查具体报错,多半是版本不匹配或目录层级问题。

验证插件列表还有一条命令:

D:\elasticsearch-7.17.3\bin\elasticsearch-plugin.bat list

输出里如果有analysis-ik,说明ES已注册这个插件。不过这条命令只能证明插件描述文件被读到了,IK的分词器是否真正可用,还要实测一下才知道。

3.4 用_analyze接口测分词

ES提供了_analyze接口,专门用来测试文本经过某个分词器后的结果。用curl就能调,不需要装Kibana。注意Windows的cmd里单双引号处理很恶心,建议用PowerShell,或者干脆把请求写成一个json文件用curl -d @file。

curl -X POST "http://localhost:9200/_analyze?pretty" -H "Content-Type: application/json" -d "{\"analyzer\":\"ik_max_word\",\"text\":\"中华人民共和国成立了\"}"

返回结果大致是这样的:

{ "tokens": [ {"token": "中华人民共和国", "start_offset":0, "end_offset":7, "type":"CN_WORD", "position":0}, {"token": "中华人民", "start_offset":0, "end_offset":4, "type":"CN_WORD", "position":1}, {"token": "共和国", "start_offset":4, "end_offset":7, "type":"CN_WORD", "position":2}, {"token": "成立", "start_offset":7, "end_offset":9, "type":"CN_WORD", "position":3}, {"token": "了", "start_offset":9, "end_offset":10, "type":"CN_WORD", "position":4} ] }

注意ik_max_word是最大切分模式,会把一句话拆成尽可能多的词,所以会出现“中华人民共和国”和“中华人民”“共和国”这样重叠的子词。它适合在索引文档时使用,能提高召回率。另有一个ik_smart是智能切分模式,只保留最合理的词序列,同样的文本会输出“中华人民共和国”“成立”“了”,更适合做搜索关键词。常见搭配是索引用ik_max_word,搜索用ik_smart,这样既能保证召回,又能让query尽量精确。

4. 把业务词喂给IK:自定义词典、停用词与远程热更新

IK的默认词典覆盖的是通用中文词,你公司产品名、内部系统名、特定领域术语基本都不在里面。比如“某跨平台系统”这个词,不配词典的话会被拆成“某”“跨平台”“系统”,用户搜索“某跨平台系统”时可能根本匹配不上整词。所以安装IK之后,真正决定分词好不好用的,往往是自定义词典能不能配好。

4.1 先理解IK的词典链

IK启动时会依次加载主词典、量词词典、停用词词典,然后合并你额外指定的扩展词典。主词典是main.dic,里面是通用词;stopword.dic是停用词,比如“的”“了”“是”;自定义词典通过IKAnalyzer.cfg.xml里的ext_dict字段追加。这个配置文件的默认路径在plugins\analysis-ik\config\IKAnalyzer.cfg.xml。

如果你改过ES的path.plugins配置,或者用的不是默认目录,那配置文件位置也要跟着变,不要死认这个路径。我在某一次部署时把plugins目录移到了外部,结果忘改词典路径,排查了好久才发现IK加载的是旧配置。

4.2 修改IKAnalyzer.cfg.xml添加本地词典

用文本编辑器打开IKAnalyzer.cfg.xml,默认内容类似这样:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties> <comment>IK Analyzer 扩展配置</comment> <entry key="ext_dict">custom/product.dic;custom/brand.dic</entry> <entry key="ext_stopwords">custom/my_stopwords.dic</entry> <entry key="remote_ext_dict">http://dict.example.com/product.txt</entry> <entry key="remote_ext_stopwords">http://dict.example.com/stop.txt</entry> </properties>

其中ext_dict的值是相对于config目录的路径,多个词典文件用英文分号分隔。比如上面配置了custom/product.dic,实际文件就在plugins\analysis-ik\config\custom\product.dic。注意路径分隔符建议统一用正斜杠/,不要用Windows反斜杠,否则某些环境下解析会出奇怪问题。

词典文件本身是一个纯文本文件,每一行一个词,注意保存成UTF-8编码,不要带BOM。例如:

某跨平台系统 组件化网关 数据抽取任务

配置好以后,重启ES,用第3章的_analyze接口测一下。如果ik_max_word能把“某跨平台系统”切成一个完整token,说明词典生效了。

4.3 远程词典:不用重启就能加词

业务词库是动态变化的,每次加词都重启ES,运维和开发都要疯掉。IK天然支持远程词典,只要在IKAnalyzer.cfg.xml里配了remote_ext_dict,插件会定期去请求这个URL,根据HTTP响应的Last-Modified或ETag判断词典文件有没有变化,有变化就重新加载。也就是说,你只要更新远端服务器上的文本文件,IK会在几个周期内自动把新词刷进内存,节点不用重启。

远程词典文件的要求和本地词典一样,纯文本、UTF-8、每行一个词。常见做法是放在内网的静态资源服务器上,比如Nginx或对象存储,保证ES节点能访问到。我一般会在配置里同时保留本地扩展词典和远程词典,本地放小组内部的高频词,远程放需要频繁更新的动态词。注意远程词典URL如果失效或返回非200状态,IK会静默忽略,日志里不一定有提示,所以加完词记得用_analyze验证,别理所当然地以为配置没写错。

4.4 词典生效的验证方法

在开发环境里,验证词典是否生效最直接的方法就是调用_analyze接口。比如刚才加了一个词“某跨平台系统”,就测这句话:

curl -X POST "http://localhost:9200/_analyze?pretty" -H "Content-Type: application/json" -d "{\"analyzer\":\"ik_max_word\",\"text\":\"某跨平台系统\"}"

如果返回的tokens里包含完整的“某跨平台系统”,说明词典加载成功。如果返回的是“某”“跨”“平台”“系统”,说明词典没生效,需要按第5章里的方法逐项排查。注意_analyze接口测试的是分词器配置,而索引文档时的实际分词还取决于这个字段的mapper设置。有时候_analyze能切对,但线上搜索还是不行,那是因为索引里的旧文档已经用旧词典分过词了,不重建索引就不会变。

5. Windows下IK分词器安装避坑:五个翻车现场

IK安装本身不难,但Windows环境下的坑比Linux多得多。我把自己遇到过的还有同行反馈过的几类典型问题整理一下,每条都是现象、原因、解决三段式,你遇到时可以按图索骥。

5.1 启动日志提示找不到插件描述文件

现象:ES启动一两秒后就报错退出,日志里有类似plugin-descriptor.properties not found或Unable to create plugin from ...的信息。

原因:zip包解压出来的顶层目录是elasticsearch-analysis-ik-7.17.3,你把它整个放进了plugins目录,于是路径变成plugins\elasticsearch-analysis-ik-7.17.3\elasticsearch-analysis-ik-7.17.3\plugin-descriptor.properties。ES扫描plugins下的每个一级子目录,但第一层目录里没有插件描述文件,所以加载失败。

解决:把外层目录改名去掉一层,保证plugin-descriptor.properties在plugins\analysis-ik\下。用第3章里的脚本就可以避开这个问题。如果已经错了,先停ES,把错误目录移出,再按正确层级放回去。

5.2 启动报错“Illegal version”或版本不匹配

现象:插件能被elasticsearch-plugin.bat list看到,但ES启动时抛异常,提示插件要求的ES版本和当前版本不一致。

原因:下载的IK包可能对应另一个小版本。比如ES是7.17.3,IK包却是7.17.0。ES插件校验是严格匹配,要求elasticsearch.version完全相等,小版本差一位都不行。

解决:先确认ES真实版本,再检查IK包内plugin-descriptor.properties。如果已经解压安装,可以直接看plugins\analysis-ik\plugin-descriptor.properties。版本不对就直接换配套的zip包,不要自己改elasticsearch.version糊弄过去,那样就算启动成功,运行中也可能出NoSuchMethodError,属于埋雷操作。

5.3 Windows Defender锁文件导致解压失败或启动慢

现象:解压zip包时提示“另一个程序正在使用此文件”,或者ES启动时卡在加载插件环节很久,日志里还有文件访问超时。

原因:Windows Defender会实时扫描新出现的大文件,尤其是jar包内有大量class文件,扫描过程会占用文件句柄,导致解压或加载异常。

解决:把ES安装目录加入Defender的排除项,路径比如D:\elasticsearch-7.17.3。如果你用的是ES本身解压的绿色版,也可以临时把整个下载目录加进去,安装完再移除。注意不要在服务器上随便关闭Defender,排除指定目录就够了。

5.4 词典文件有BOM导致第一个词乱码

现象:自定义词典里第一个词怎么都匹配不上,第二个词却正常。用_analyze测试时,第一个词前面好像多了一个看不见的字符。

原因:Windows记事本保存UTF-8文件时,默认会在文件开头写入一个BOM头(\uFEFF)。IK读取词典时把这个BOM当成词条内容的一部分,所以第一个词变成了\uFEFF某跨平台系统,当然匹配不上。

解决:用Notepad++或VS Code打开词典文件,编码选择“UTF-8无BOM”再保存。注意不仅要看自定义词典,IKAnalyzer.cfg.xml本身如果带BOM,也可能导致属性解析异常,所以配置文件同样建议用无BOM格式保存。

5.5 改完自定义词典重启ES后还不生效

现象:配置文件修改了,词典文件也加了词,重启ES后测_analyze,分词结果一点变化都没有。

原因:常见原因有三个。第一,ext_dict里的路径写错了,比如文件名大小写不对或路径少了一层custom。第二,你改的是别处的副本,比如从Google搜索出来的教程里复制了一个配置路径,实际IK加载的是另一个IKAnalyzer.cfg.xml。第三,旧索引已经用原来的词典生成了倒排索引,新词典只对新建索引和后续写入的文档生效,旧文档不会被重新分词。

解决:先确认配置文件路径和实际加载路径一致,再看词典文件是否在config下。然后检查日志里IK加载词典的debug信息,启动时加-Dorg.apache.logging.log4j.level=DEBUG能看到具体加载了哪个文件。如果确认词典已生效,但旧数据搜不到,那就必须把旧索引重建一遍,或者给新索引指定新的analyzer,然后reindex。

6. 用 _termvectors 核对IK自定义词典是否真的切出了词

_analyze接口虽然方便,但它只反映分词器本身的表现,不代表索引里的文档字段真的用了这个分词器。实际项目中经常出现修改完IK词典,在_analyze里测试一切正常,线上搜索还是搜不到新词,原因就是字段mapping里写的是standard或者ik_smart,和你改的ik_max_word完全是两套逻辑。这时候有一个更靠谱的验证手段:_termvectors接口。它能直接查看某篇文档的某个字段在索引时切出了哪些词,相当于把生产链路里的真实分词结果拉出来看。

先建一个测试索引,字段指定用ik_max_word,同时打开词向量,这样才能观察到具体term:

curl -X PUT "http://localhost:9200/demo_index" -H "Content-Type: application/json" -d "{\"mappings\":{\"properties\":{\"content\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\",\"term_vector\":\"with_positions_offsets\"}}}}"

写入一篇包含自定义词的文档:

curl -X PUT "http://localhost:9200/demo_index/_doc/1" -H "Content-Type: application/json" -d "{\"content\":\"某跨平台系统开启了数据抽取任务\"}"

然后查词向量:

curl -X GET "http://localhost:9200/demo_index/_termvectors/1?fields=content&pretty"

返回结果里的terms对象会列出content字段的全部词项。如果你能在这个列表里找到“某跨平台系统”,说明IK自定义词典在索引链路中真的生效了。如果找不到,但_analyze里又能切出来,就要回头查字段的analyzer配置,大概率是mapping里的analyzer没换成IK。

做这一步的时候,我习惯把词向量里的关键词和业务侧期望的词逐一比对,而不是只看有没有新增词。因为你加一个自定义词,可能影响的不只是这个词本身,还会改变相邻文本的切分结果。比如把“平台”和“系统”组合成“平台系统”后,原本“平”“台”“系”“统”的字被合并了,其他查询词的匹配也会受影响。

以前我改IK词典只跑一遍_analyze,以为万事大吉,后来线上反馈“某跨平台系统”始终搜不出来,最后查出来是旧索引建在IK还没装好的时候,倒排索引里根本没有这个词。从那以后我每次在Windows上配完IK词典,都强制走一遍“新建索引、写入文档、查_termvectors、核对词项”的流程,词典改完才算真正落地。希望这篇笔记能帮到你少走我走过的弯路。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进