# elasticsearch-analysis-ik-mysql **Repository Path**: peng_yiheng/elasticsearch-analysis-ik-mysql ## Basic Information - **Project Name**: elasticsearch-analysis-ik-mysql - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-07-14 - **Last Updated**: 2023-05-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: ik分词器改造, ElasticSearch ## README 1、IK分词器源码下载: 本案例以ES7.9.1和MySql数据库5.5+为例进行配置; 修改源码步骤 1、修改maven依赖es版本号 使用工具打开IK源码后,打开pom.xml文件,修改elasticsearch版本号为7.9.1 7.9.1 2、引入MySql驱动到项目中 mysql mysql-connector-java 5.1.38 3、开始修改源码 在项目中找到Dictionary类,找到Dictionary单例类的初始化方法initial方法,在初始化方法中我们起一个线程,用来执行远程词库的热更新,再修改之前,我们在Dictionary类同目录下新建一个类HotDictReloadThread,代码如下: public class HotDictReloadThread { private static final Logger log = ESPluginLoggerFactory.getLogger(HotDictReloadThread.class.getName()); public void initial(){ while (true) { log.info("正在调用HotDictReloadThread..."); Dictionary.getSingleton().reLoadMainDict(); } } } 上述代码的含义为: 获取词典单子实例,并执行它的reLoadMainDict方法; 完成上述操作后,我们就来开始修改initial方法,改动如下图,创建上面新建的类并调用它的initial方法,从而执行Dictionary类的reLoadMainDict方法;改动代码如下,在字典实例初始化完成后新起一个线程来执行字典的热更新操作; pool.execute(() -> new HotDictReloadThread().initial()); 跟着程序一步步走下去,找到Dictionary类的reLoadMainDict方法,可以看到在方面里面,有2个方法tmpDict.loadMainDict()和tmpDict.loadStopWordDict(),分别维护的是扩展词库和停用词库,一块先看一下对扩展词库的维护; 在方法tmpDict.loadMainDict()中,我们在最后一行加载远程自定义词库后面新增一个方法this.loadMySQLExtDict(),用于加载MySql词库,在加载MySql词库之前,我们需先准备一下MySql相关的配置以及sql语句;在数据库中新建一张表,用户维护扩展词和停用词,表结构如下 CREATE TABLE `es_lexicon` ( `lexicon_id` bigint(8) NOT NULL AUTO_INCREMENT COMMENT '词库id', `lexicon_text` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '词条关键词', `lexicon_type` int(1) NOT NULL DEFAULT 0 COMMENT '0扩展词库 1停用词库', `lexicon_status` int(1) NOT NULL DEFAULT 0 COMMENT '词条状态 0正常 1暂停使用', `del_flag` int(1) NOT NULL DEFAULT 0 COMMENT '作废标志 0正常 1作废', `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`lexicon_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ES远程扩展词库表' ROW_FORMAT = Dynamic; 然后我们在项目的根路径的config目录下新建配置文件jdbc-reload.properties,内容如下 # 数据库地址 jdbc.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=GMT&autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useAffectedRows=true&useSSL=false # 数据库用户名 jdbc.user=root # 数据库密码 jdbc.password=123456 # 数据库查询扩展词库sql语句 jdbc.reload.sql=select gel.lexicon_text as word from es_lexicon gel where gel.lexicon_type = 0 and gel.lexicon_status = 0 and gel.del_flag = 0 order by gel.lexicon_id desc # 数据库查询停用词sql语句 jdbc.reload.stopword.sql=select gel.lexicon_text as word from ges_lexicon gel where gel.lexicon_type = 1 and gel.lexicon_status = 0 and gel.del_flag = 0 order by gel.lexicon_id desc # 数据库查询间隔时间 每隔10秒请求一次 jdbc.reload.interval=10 完成了这些基础配置之后,我们再一同看看关于同步MySql词库的方法loadMySQLExtDict();代码较长,粘贴如下 /** * 从MySql中加载动态词库 */ private void loadMySQLExtDict() { Connection conn = null; Statement stmt = null; ResultSet rs = null; try { Path file = PathUtils.get(getDictRoot(), "jdbc-reload.properties"); props.load(new FileInputStream(file.toFile())); logger.info("[==========]jdbc-reload.properties"); for(Object key : props.keySet()) { logger.info("[==========]" + key + "=" + props.getProperty(String.valueOf(key))); } logger.info("[==========]query hot dict from mysql, " + props.getProperty("jdbc.reload.sql") + "......"); // Class.forName(props.getProperty("jdbc.className")); conn = DriverManager.getConnection( props.getProperty("jdbc.url"), props.getProperty("jdbc.user"), props.getProperty("jdbc.password")); stmt = conn.createStatement(); rs = stmt.executeQuery(props.getProperty("jdbc.reload.sql")); while(rs.next()) { String theWord = rs.getString("word"); logger.info("[==========]正在加载Mysql自定义IK扩展词库词条: " + theWord); _MainDict.fillSegment(theWord.trim().toCharArray()); } Thread.sleep(Integer.valueOf(String.valueOf(props.get("jdbc.reload.interval"))) * 1000); } catch (Exception e) { logger.error("erorr", e); } finally { if(rs != null) { try { rs.close(); } catch (SQLException e) { logger.error("error", e); } } if(stmt != null) { try { stmt.close(); } catch (SQLException e) { logger.error("error", e); } } if(conn != null) { try { conn.close(); } catch (SQLException e) { logger.error("error", e); } } } } 在上述代码中,通过加载配置文件,获取数据库连接,执行扩展词sql,将结果集添加到扩展词库中; 同理,同步MySql停用词的逻辑也是一样的,这里我直接把代码粘贴过来;停用词方法调用顺序为tmpDict.loadStopWordDict(),在方法后面,新增一个方法调用this.loadMySQLStopwordDict(),新方法中处理通用词逻辑,代码如下 /** * 从MySql中加载远程停用词库 */ private void loadMySQLStopwordDict() { Connection conn = null; Statement stmt = null; ResultSet rs = null; try { Path file = PathUtils.get(getDictRoot(), "jdbc-reload.properties"); props.load(new FileInputStream(file.toFile())); logger.info("[==========]jdbc-reload.properties"); for(Object key : props.keySet()) { logger.info("[==========]" + key + "=" + props.getProperty(String.valueOf(key))); } logger.info("[==========]query hot stopword dict from mysql, " + props.getProperty("jdbc.reload.stopword.sql") + "......"); // Class.forName(props.getProperty("jdbc.className")); conn = DriverManager.getConnection( props.getProperty("jdbc.url"), props.getProperty("jdbc.user"), props.getProperty("jdbc.password")); stmt = conn.createStatement(); rs = stmt.executeQuery(props.getProperty("jdbc.reload.stopword.sql")); while(rs.next()) { String theWord = rs.getString("word"); logger.info("[==========]正在加载Mysql自定义IK停用词库词条: " + theWord); _StopWords.fillSegment(theWord.trim().toCharArray()); } Thread.sleep(Integer.valueOf(String.valueOf(props.get("jdbc.reload.interval"))) * 1000); } catch (Exception e) { logger.error("erorr", e); } finally { if(rs != null) { try { rs.close(); } catch (SQLException e) { logger.error("error", e); } } if(stmt != null) { try { stmt.close(); } catch (SQLException e) { logger.error("error", e); } } if(conn != null) { try { conn.close(); } catch (SQLException e) { logger.error("error", e); } } } } 完成这些,整体代码改造完毕;在上述代码中,有很多的地方是可以进一步优化的,比如扩展词和停用词的大量重复代码,以及读取本地配置文件项可以做到只读取一次等,这个大家可以自行优化; 完成了这些之后,我们就可以开始打包插件了;直接使用maven package命令进行打包,在target/releases/elasticsearch-analysis-ik-7.6.1.zip文件; 安装插件 完成上述步骤后,拿到elasticsearch-analysis-ik-7.8.0.zip插件,我们将其放在ES安装目录下的plugins目录下,新建一个ik文件夹,将其解压到ik文件夹下;目录结构大概如下 完成上述步骤后,我们就可以启动ES了,在启动过程中,可以看到关于IK热更新MySql词库相关的日志输出;在实际过程中,可能会报很多的异常,下面是我所遇到的一些问题以及解决方案; 常见问题 1、异常1:java.sql.SQLException: Column 'word' not found. 此异常是因为编写sql时,查询的数据库字段需要起别名为 word,修改一下sql即可解决这个问题; 2、异常2:Could not create connection to database server 此异常通常是因为引用的mysql驱动和mysql版本号不一致导致的,只需要替换成对应的版本号即可解决,另外,数据库连接我们不需要再额外的去配置显示加载,即不需要写 Class.forName(props.getProperty("jdbc.className")); 3、异常3:no suitable driver found for jdbc:mysql://... 此异常我们需要在环境的JDK安装目录的jre\lib\ext目录下添加mysql驱动mysql-connector-java.jar;比如我本地的是C:\Java\jdk_8u_231\jre\lib\ext 目录,服务器上是/usr/local/jdk/jdk1.8.0_181/jre/lib/ext 4、异常4:AccessControlException: access denied ("java.net.SocketPermission" "127.0.0.1:3306" "connect,resolve") 这个异常,我们修改jdk安装路径下的C:\Java\jdk_8u_231\jre\lib\security目录下的文件java.policy,在下面新增一行即可解决 permission java.net.SocketPermission "*", "connect,resolve";