问题描述:

上周运营反馈商城搜索词搜不到 排查发现es ik分词器的ik_smart对搜索词的分词结果不是ik_max_word对索引文档字段值分词结果的子集 即细粒度分词结果不完全包含粗粒度分词结果 代码的全文分词检索又是Operator.And  只能修改ik字典值 公司用的是ik远程字典值  但是没生效(远程拓展字典没生效 远程停用词典生效 很奇怪 也不是编码问题)  原因最后没排查出来  最后用的同义词暂时解决问题  但是公司同义词配置不是热更新  es集群有七台服务器 每个服务器的synonym.txt文件都要修改再重启集群 运维也反馈相当麻烦 所以参考了些资料  最后实现了Ik字典和同义词都能连接mysql不重启的情况下热更新



可以看到粗粒度ik_smart分词分了个水字 细粒度的ik_max_word却没有分出水字 和其他字黏连了  这细粒度分词也不够细啊 哈哈 此时可以采用ik强字典值 将蜜糖水果酒作为一个整词 也可以同义词蜜糖水果酒指向蜜糖水果(蜜糖水果是可以匹配文档字段的)  当然这不是本文重点 只是发现粗粒度和细粒度对同一词条分词时  前者的分词结果并不一定是后者的子集  下面进入正文

1.修改IK源码连接mysql实现热更新:

1)首先ik分词配置字典有三种方式 一是读取本地字典 二是加载远程字典  三是连接mysql  前二者都是在ik config目录下的IKAnalyzer.cfg.xml里配置  第二种也能实现不重启es热更新也是官方推荐的但是还是不够方便  原理就是另起监控线程定时读取远程文件服务  根据返回的header的Last-Modified(记录上次修改时间)和Etag 两者有一个变化就全量读取字典词条加载进主词典内存  第三种是把字典值存储mysql中 也是利用监控线程  但是是增量读取   这一点与第二种不同  且修改字典值较为方便 因为只需修改数据库即可  ik源码加载字典主要是Monitor和Dictionary类 initial方法是入口

<?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"></entry>
     <!--用户可以在这里配置自己的扩展停止词字典-->
    <entry key="ext_stopwords"></entry>
    <!--用户可以在这里配置远程扩展字典 -->
    <!-- <entry key="remote_ext_dict">words_location</entry> -->
    <!--用户可以在这里配置远程扩展停止词字典-->
    <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

2)GitHub - medcl/elasticsearch-analysis-ik: The IK Analysis plugin integrates Lucene IK analyzer into elasticsearch, support customized dictionary.

去git上下载ik源码 版本的话选择和es对应的版本 具体可见readme文档

修改pom文件es版本号和mysql依赖

首先建强词典和停用词表 (词条、修改时间等主要字段)

CREATE TABLE `es_extra_main` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`word` varchar(255) CHARACTER SET utf8mb4 NOT NULL COMMENT '词',`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否已删除',`update_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `es_extra_stopword` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`word` varchar(255) CHARACTER SET utf8mb4 NOT NULL COMMENT '词',`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否已删除',`update_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3)config报下创建jdbc配置文件用于连接mysql  具体包含数据库连接信息、增量定时读取数据库加载词条的sql以及监控线程定时间隔时间

jdbc.url=jdbc:mysql://192.168.227.131:3307/dianpingdb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=root
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.update.main.dic.sql=SELECT * FROM `es_extra_main` WHERE update_time > ? order by update_time asc
jdbc.update.stopword.sql=SELECT * FROM `es_extra_stopword` WHERE update_time > ? order by update_time asc
jdbc.update.interval=10

在上述 Dictionary类 initial方法 加载远程字典监控下添加一行   同样起一个监控线程定时读取mysql更新词条

// 建立数据库监控线程
pool.scheduleAtFixedRate(new DatabaseMonitor(),Long.parseLong(getSingleton().getProperty(DatabaseMonitor.JDBC_UPDATE_INTERVAL)), 10, TimeUnit.SECONDS);

在 Dictionary带参构造中加连接mysql的方法以及强词典、停用词典的增删方法

/*** 加载新词条*/
public static void addWord(String word) {singleton._MainDict.fillSegment(word.trim().toLowerCase().toCharArray());
}/*** 移除(屏蔽)词条*/
public static void disableWord(String word) {singleton._MainDict.disableSegment(word.trim().toLowerCase().toCharArray());
}/*** 加载新停用词*/
public static void addStopword(String word) {singleton._StopWords.fillSegment(word.trim().toLowerCase().toCharArray());
}/*** 移除(屏蔽)停用词*/
public static void disableStopword(String word) {singleton._StopWords.disableSegment(word.trim().toLowerCase().toCharArray());
}/*** 加载 jdbc.properties*/
public void loadJdbcProperties() {Path file = PathUtils.get(getDictRoot(), DatabaseMonitor.PATH_JDBC_PROPERTIES);try {props.load(new FileInputStream(file.toFile()));logger.info("====================================加载数据库连接信息 start====================================");for (Map.Entry<Object, Object> entry : props.entrySet()) {logger.info("{}: {}", entry.getKey(), entry.getValue());}logger.info("====================================加载数据库连接信息 end====================================");} catch (IOException e) {logger.error("failed to read file: " + DatabaseMonitor.PATH_JDBC_PROPERTIES, e);}
}

增加DatabaseMonitor类  加载mysql驱动获取连接   以及读取上面jdbc配置的sql语句获取mysql词典数据加载进字典内存

SELECT * FROM `es_extra_main` WHERE update_time > ? order by update_time asc

内存中有个lastUpdateTimeOfMainDic记录数据库最后一个字典值的修改时间  根据这个时间查询sql判断是否有新词添加  如果有就重新赋值给lastUpdateTimeOfMainDic 然后加载字典值

 

4)打包配置

plugin.xml   打包的文件中能有mysql的驱动jar包

<include>com.mysql:mysql-connector-j</include>

在plugin-security.policy加入如下配置 防止报权限错误
permission java.lang.RuntimePermission "setContextClassLoader";

最后maven package打包  到target release下找打压缩包  移动到es plugin目录下ik目录中解压

如果是docker部署es 可以直接放进挂载的数据卷中

docker volume inspect es-plugins 找到你挂载的数据卷目录

最后重启es就可以验证啦  可以通过docker logs -f es看日志

mysql增加词条(注意修改时间字段  根据这个字段增量更新) 然后等个时间(监控程序设置的定时时间)就生效了  可以postman http分词验证

2.修改同义词插件源码连接mysql实现热更新:

1)同样同义词也有是三种方式  读取本地synonym.txt文件(es config目录下)、远程文件热更新(建索引时settings synonyms_path设置http远程文件路劲  具体实现以及原理不赘述拉)以及本文要将的连接mysql 监控线程实现热更新  大致和ik连接mysql热更新有相似之处

2)同样下载同义词插件源码 下载对应版本

GitHub - bells/elasticsearch-analysis-dynamic-synonym: The dynamic synonym plugin adds a synonym token filter that reloads the synonym file(local file or remote file) at given intervals (default 60s).

同上修改pom文件  es版本号和mysql依赖

新建config配置目录  新建 jdbc-reload.properties mysql配置信息  包括数据库连接基本信息以及读取mysql同义词的sql

jdbc.url=jdbc:mysql://192.168.227.131:3307/dianpingdb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai
jdbc.user=root
jdbc.password=root
jdbc.reload.synonym.sql=SELECT words FROM sys_synonym_t where is_valid = 1
jdbc.lastModified.synonym.sql=SELECT max(last_modify_dt) as last_modify_dt FROM sys_synonym_t
jdbc.driver=com.mysql.cj.jdbc.Driver

创建表

CREATE TABLE `sys_synonym_t` (`id` int NOT NULL AUTO_INCREMENT,`words` varchar(255) DEFAULT NULL,`last_modify_user` varchar(255) DEFAULT NULL,`last_modify_dt` datetime DEFAULT NULL,`is_valid` int DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

新建DBRemoteSynonymFile类   这个类主要是连接mysql 加载同义词典

DynamicSynonymTokenFilterFactory类getSynonymFile方法新增读取数据库加载的判断
//数据库动态更新同义词
else if (location.equalsIgnoreCase("fromDB")) {synonymFile = new DBRemoteSynonymFile(environment, analyzer, expand, format,location);
} 

加载mysql同义词

sql:

jdbc.reload.synonym.sql=SELECT words FROM sys_synonym_t where is_valid = 1
jdbc.lastModified.synonym.sql=SELECT max(last_modify_dt) as last_modify_dt FROM sys_synonym_t

首先判断最后一个词条的修改时间和当前内存保存的lastModified 决定是否有新词条需要加载

3)打包

plugin.xml增加:用于生成config目录和mysql驱动jar包

<include>com.mysql:mysql-connector-j</include>
<fileSets><fileSet><directory>${project.basedir}/config</directory><outputDirectory>config</outputDirectory></fileSet>
</fileSets>

权限

同上plugin-security.policy增加

permission java.lang.RuntimePermission "setContextClassLoader";
permission java.lang.RuntimePermission "accessDeclaredMembers";
permission java.lang.RuntimePermission "createClassLoader";

还要修改 es config下的jvm.options文件 增加

-Djava.security.policy=/usr/share/elasticsearch/plugins/analyzer-dynamic-synonym/plugin-security.policy  替换成你自己的路劲

最后就Maven打包target release目录下获取打包的压缩文件

到linux es plugin目录下新建同义词目录 把压缩包放进去解压  然后重启es 验证吧

mysql增加词条(修改is_valid last_modify_dt实现增加删除) docker日志查看  postman分词验证

总结:

1,源码还是很好解读修改的  上面解读的不是很多  还有挺多地方都是可以扩展的

2, gitee上可以自行下载  包含了sql

elasticsearch-analysis-ik: 修改ik分词插件源码连接mysql实现热更新

elasticsearch-analysis-dynamic-synonym: 修改es同义词插件源码连接mysql实现热更新

3,ik分词肯定不是完全智能、能满足各种分词的  所以才有扩展词典来补充   es集群服务器较多的时候热更新可以避免每次修改字典值都要重启

4,注意:修改了字典值  只对新增的文档有效(es倒排索引的分词)  旧数据的倒排索引要更新还是要重建索引的

 
 

es 修改ik和同义词插件源码连接mysql实现字典值同义词热更新相关推荐

  1. Elasticsearch7.15.2 修改IK分词器源码实现基于MySql8的词库热更新

    文章目录 一.源码分析 1. 默认热更新 2. 热更新分析 3. 方法分析 二.词库热更新 2.1. 导入依赖 2.2. 数据库 2.3. JDBC 配置 2.4. 打包配置 2.5. 权限策略 2. ...

  2. QML 地图修改插件源码(三),Map在Plugin中设置加载地图类型

    常用的地图种类分为交通图,地形图,卫星图等等,在QML的Map(以OSM地图为例)中提供activeMapType属性用于读取当前显示的地图类型(注意:该属性为只读属性,不能用于赋值),QML中地图的 ...

  3. [Unity3D]修改PaintIn3D插件源码以便用于VR

    修改PaintIn3D插件源码 1. 导入PaintIn3D插件和SteamVR 2. 修改PaintIn3D插件源码 2.1 修改P3dHitScreen.cs 2.2 修改P3dInputMana ...

  4. 基于webpack修改插件源码,使用自定义文件替换node_modules里面的源码文件

    基于webpack修改插件源码,使用自定义文件替换node_modules里面的源码文件 需求:插件不满足要求,需要修改源码,但又想永远保留自己修改的这份,不想被重新下载的覆盖 方法:在运行时执行你替 ...

  5. QML 地图修改插件源码(四),Map根据目录作为索引加载地图瓦片

    QML中的地图(以OSM为例)在使用过程中会发现当地图层数很多时,特别是如果使用离线地图且地图层级较多时地图会变得很卡(在线地图加载的层级数多且不清除缓存时也会卡),原因在于QML地图插件对地图瓦片的 ...

  6. QML 地图修改插件源码(一)解决Map使用Open Street Map(OSM)无法加载在线地图的解决办法

    最近在使用Qml地图中发现,osm地图在线加载地图失败,通过查看osm插件源代码,找到了osm瓦片地图的源地址为:http://c.tile.openstreetmap.org/%z/%x/%y.pn ...

  7. 如何查看google chrome 插件源码

    为什么80%的码农都做不了架构师?>>>    常用浏览器google chrome 有很多优秀的插件,寂寞的时候想看看人家是怎么实现的,说是快那就动手吧 插件代码位置 本人mac笔 ...

  8. 粤嵌打卡第51天(小白带你进入bootstrap的学习(包含常用的页面插件源码供大家ctr+v))

    今天我们来学习下在做项目的过程中如何使用Bootstrap来引入比较美观的样式,学完了这篇博客,大家就可以尽情的使用模板了呀! bootstrap官网:https://v3.bootcss.com/g ...

  9. 开源Vue表格组件,表格插件源码

    开源Vue表格组件,表格插件源码 前言: 关于html里面原生的table,通常满足不了程序员的要求.所以开发了一款表格插件,其功 能有: 1 导入json格式数据后,自动填充表格.表格长宽自适应.排 ...

最新文章

  1. 2019.03.18 连接my sql
  2. 浅析大规模DDOS防御架构:应对T级攻防
  3. PCL已有点类型介绍和增加自定义的点类型
  4. 使用VideoView做个实用的视频播放器
  5. P1455-搭配购买【图论,并查集,dp,背包】
  6. linux定时scp脚本,linux expect 实现定时scp的任务
  7. 服务器上装filezilla server后,本地的ftp客户端连接不上去
  8. Dxperience 7.3.4 简体中文本地化
  9. MPC(模型预测控制)之二(路径规划)
  10. java string 数组 个数,Java - 定义一个接收String的方法,并返回一个整数数组,其中包含每个元音的数量...
  11. 誉赐 PC阳光板www.ycpc.icoc.cn/详尽概述
  12. RGB在线取色器,可视化三通道颜色
  13. 74cms v5.0.1 漏洞复现
  14. Cordova插件之跳转第三方app
  15. 广通优云徐育毅:做中国的ServiceNow
  16. 前端实现 html 下载(保存)为 word 格式的文件
  17. 自动定时发送邮件的方法 —— 定时执行专家V6.8
  18. Shopee大规模毁约offer,今年的大厂装都不装了
  19. C语言:输入起始日期与截至日期,计算相距天数。
  20. Android模拟电子墨水屏,彩色电子墨水屏手机海信A5proCC使用体验

热门文章

  1. 谷歌大数据搜索工具——datasetsearch
  2. Ubuntu如何配置桥接网络
  3. 禁止U盘拷贝电脑数据设置
  4. 2020年是时候迁移AndroidX了
  5. ResearchRabbit.ai: 学术论文摘要研究工具
  6. VIM快捷键大全(附图片一张)
  7. Android空格占位符
  8. 使用 JavaScript 漂亮地打印 JSON
  9. iOS---集成融云即时通讯详细教程
  10. android9.0 修改默认输入法,切换语言时不改变输入法