2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > ik分词 动态增加词库 mysql_Elasticsearch7.8.0集成IK分词器改源码实现MySql5.7.2实

ik分词 动态增加词库 mysql_Elasticsearch7.8.0集成IK分词器改源码实现MySql5.7.2实

时间:2021-03-16 00:43:31

相关推荐

ik分词 动态增加词库 mysql_Elasticsearch7.8.0集成IK分词器改源码实现MySql5.7.2实

在前面写过为IK分词器配置远程词库,不过词库没有做成可动态维护的,没有维护到数据库中;在这一期,一起探索一下IK分词器从数据库获取动态词库;

在github上的IK源码描述文件中对远程扩展词库是这样描述的

通过描述可以看到可以通过url的形式请求文件或者是接口,从而实现热更新分词,不需要重启ES实例;

不过在网上看到好像大家都不是很推荐这种方式,说不是很稳定,所以采用网上大家所推荐的方式,修改IK分词器源码轮询查库实现词库热更新;

本案例以ES7.8.0和MySql数据库5.7为例进行配置;

修改源码步骤

1、修改maven依赖es版本号

使用工具打开IK源码后,打开pom.xml文件,修改elasticsearch版本号为7.8.0

7.8.0

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.8.0.zip文件,我们将Mysql驱动mysql-connector-java.jar也放到这个压缩包里面;

安装插件

完成上述步骤后,拿到elasticsearch-analysis-ik-7.8.0.zip插件,我们将其放在ES安装目录下的plugins目录下,新建一个ik文件夹,将其解压到ik文件夹下;目录结构大概如下,记得要有MySql驱动mysql-connector-java.jar

完成上述步骤后,我们就可以启动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 目录,服务器上是/data/jdk1.8.0_181/jre/lib/ext

4、异常4:AccessControlException: access denied (".SocketPermission" "127.0.0.1:3306" "connect,resolve")

这个异常,我们修改jdk安装路径下的C:\Java\jdk_8u_231\jre\lib\security目录下的文件java.policy,在下面新增一行即可解决

permission .SocketPermission "*", "connect,resolve";

ik分词 动态增加词库 mysql_Elasticsearch7.8.0集成IK分词器改源码实现MySql5.7.2实现动态词库实时更新...

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。