ES默认的IK词典加载机制有个硬伤:修改词典必须重启整个ES集群,这在生产环境是不可接受的。热更新的目的就是让IK每隔一段时间自动从外部拉取新词,零停机生效。
方案一:基于远程HTTP文件(最推荐,官方原生支持)
这是IK官方提供的方案,利用ik-analyzer的remote_ext_dict配置项,让IK定时轮询一个远程URL。
1. 修改IK配置文件
找到ES的plugins/ik/config/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/mydict.dic</entry> <!-- 停用词词典 --> <entry key="ext_stopwords">custom/mystopword.dic</entry> <!-- ========== 重点:热更新远程词典 ========== --> <entry key="remote_ext_dict">http://your-server.com/ik/dict.txt</entry> <!-- 远程停用词热更新 --> <entry key="remote_ext_stopwords">http://your-server.com/ik/stopword.txt</entry> </properties>2. 搭建HTTP文件服务
在http://your-server.com/ik/dict.txt地址上部署一个纯文本文件,每行一个词:
内卷 YYDS 区块链 元宇宙 碳中和 李子柒关键要求:
HTTP响应头必须包含
Last-Modified或ETag。IK每隔60秒会发一次
HEAD请求检查文件是否修改。如果没变化,不会重新加载;只有Last-Modified变了,才会GET拉取新内容。
3. 验证生效
写入新文档测试:
POST /test/_doc/1 { "content": "今天你内卷了吗" } POST /test/_search { "query": { "term": { "content": "内卷" } } }能命中说明热更新成功。
方案二:基于数据库(MySQL/Redis)- 适合运维管控
如果你想把词典管理放到后台系统中,让运营人员通过界面增删词,可以自己写一个同步程序。
架构思路
运营后台(增删词) -> MySQL/Redis(存储词典) -> 同步程序(定时拉取) -> 写入本地文件 -> IK 重新加载IK本身不支持直接读数据库,所以需要拐个弯:
在
IKAnalyzer.cfg.xml中配置ext_dict为本地路径(如/data/ik/dynamic.dic)。写一个后台定时任务(Java/Python),每隔1分钟从MySQL拉取最新词表,覆盖写入
dynamic.dic。关键步骤:修改文件的
mtime(修改时间戳),因为IK只通过文件的最后修改时间判断是否变更。Linux命令:
touch -t 202501011200 dynamic.dicJava代码:
File.setLastModified(System.currentTimeMillis())
缺点
需要额外维护同步程序,且ES集群每个节点都要有本地文件,操作较繁琐。
方案三:基于Nacos / Apollo(配置中心)
如果你公司已经在用配置中心,可以把词典作为配置项管理:
将词典内容存入Nacos的配置项(String类型,换行分隔)。
写一个监听器,当Nacos配置变更时,触发回调:
将新词表写入ES节点的本地文件。
调用
touch修改文件时间戳。
IK到60秒检查周期时发现文件变了,自动Reload。
避坑指南(必看)
1. IK的缓存机制
即使热更新拉到了新词,已存在的倒排索引不会重建。
新词只对更新后新写入的文档生效。
想让历史文档也带新词?必须Reindex(重建索引)。
2. 文件编码必须是UTF-8(无BOM)
如果文件是GBK或带BOM的UTF-8,IK会解析乱码,导致分词异常。用Notepad++或VS Code保存时选UTF-8 without BOM。
3. 远程文件响应要快
如果remote_ext_dict指向的HTTP服务响应超时(默认5秒),IK会跳过本次更新,继续用旧词表。确保你的文件服务高可用。
4. 各节点独立拉取
每个ES数据节点都会独立向HTTP服务器请求词典,如果集群有10个节点,每秒会有10个请求,注意服务器压力。
完整配置示例(包含同义词热更新)
如果你想连同义词也热更新,可以结合synonym过滤器:
<!-- IKAnalyzer.cfg.xml --> <entry key="remote_ext_dict">http://your-server.com/ik/main.dic</entry> <entry key="remote_ext_stopwords">http://your-server.com/ik/stop.dic</entry>然后在索引的settings中配置同义词文件指向远程路径(同义词不支持remote方式,需配合Nginx动态生成):
{ "settings": { "analysis": { "filter": { "my_synonym": { "type": "synonym", "synonyms_path": "analysis/synonym.txt" // 这个文件需要放在ES的config目录下,无法热更新 } } } } }同义词热更新较复杂,一般是配合Nginx反向代理+动态文件生成来实现,如需我可以单独给你方案。
一句话总结
用
remote_ext_dict指向一个HTTP文本文件,IK每60秒自动检查更新,这是最简单、最稳妥的热更新方案。记住:新词只影响新数据,历史数据需要Reindex。