Elasticsearch 之(25)重写IK分词器源码来基于mysql热更新词库

热更新

在上一节《 IK分词器配置文件讲解以及自定义词库》自定义词库,每次都是在es的扩展词典中,手动添加新词语,很坑
(1)每次添加完,都要重启es才能生效,非常麻烦
(2)es是分布式的,可能有数百个节点,你不能每次都一个一个节点上面去修改

es不停机,直接我们在外部某个地方添加新的词语,es中立即热加载到这些新词语

热更新的方案

(1)修改ik分词器源码,然后手动支持从mysql中每隔一定时间,自动加载新的词库
(2)基于ik分词器原生支持的热更新方案,部署一个web服务器,提供一个http接口,通过modified和tag两个http响应头,来提供词语的热更新

用第一种方案,第二种,ik git社区官方都不建议采用,觉得不太稳定

1、下载源码
https://github.com/medcl/elasticsearch-analysis-ik/tree/v5.2.0
ik分词器,是个标准的java maven工程,直接导入eclipse就可以看到源码

2、修改源码

Dictionary单例类的初始化方法initial,在这里需要创建一个我们自定义的线程,并且启动它

1/** 2 * 词典初始化 由于IK Analyzer的词典采用Dictionary类的静态方法进行词典初始化 3 * 只有当Dictionary类被实际调用时,才会开始载入词典, 这将延长首次分词操作的时间 该方法提供了一个在应用加载阶段就初始化字典的手段 4 * 5 * @return Dictionary 6 */ 7public static synchronized Dictionary initial(Configuration cfg) { 8 if (singleton == null) { 9 synchronized (Dictionary.class) { 10 if (singleton == null) { 11 12 13 singleton = new Dictionary(cfg); 14 singleton.loadMainDict(); 15 singleton.loadSurnameDict(); 16 singleton.loadQuantifierDict(); 17 singleton.loadSuffixDict(); 18 singleton.loadPrepDict(); 19 singleton.loadStopWordDict(); 20 21 new Thread(new HotDictReloadThread()).start(); 22 23 if(cfg.isEnableRemoteDict()){ 24 // 建立监控线程 25 for (String location : singleton.getRemoteExtDictionarys()) { 26 // 10 秒是初始延迟可以修改的 60是间隔时间 单位秒 27 pool.scheduleAtFixedRate(new Monitor(location), 10, 60, TimeUnit.SECONDS); 28 } 29 for (String location : singleton.getRemoteExtStopWordDictionarys()) { 30 pool.scheduleAtFixedRate(new Monitor(location), 10, 60, TimeUnit.SECONDS); 31 } 32 } 33 34 35 return singleton; 36 } 37 } 38 } 39 return singleton; 40}

HotDictReloadThread类:就是死循环,不断调用Dictionary.getSingleton().reLoadMainDict(),去重新加载词典

1public class HotDictReloadThread implements Runnable { 2 3private static final Logger logger = ESLoggerFactory.getLogger(HotDictReloadThread.class.getName()); 4 5@Override 6public void run() { 7 while(true) { 8 logger.info("[==========]reload hot dict from mysql......"); 9 Dictionary.getSingleton().reLoadMainDict(); 10 } 11} 12 13}

Dictionary类:更新词典 this.loadMySQLExtDict()

1/** 2 * 加载主词典及扩展词典 3 */ 4private void loadMainDict() { 5 // 建立一个主词典实例 6 _MainDict = new DictSegment((char) 0); 7 8 // 读取主词典文件 9 Path file = PathUtils.get(getDictRoot(), Dictionary.PATH_DIC_MAIN); 10 11 InputStream is = null; 12 try { 13 is = new FileInputStream(file.toFile()); 14 } catch (FileNotFoundException e) { 15 logger.error(e.getMessage(), e); 16 } 17 18 try { 19 BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"), 512); 20 String theWord = null; 21 do { 22 theWord = br.readLine(); 23 if (theWord != null && !"".equals(theWord.trim())) { 24 _MainDict.fillSegment(theWord.trim().toCharArray()); 25 } 26 } while (theWord != null); 27 28 } catch (IOException e) { 29 logger.error("ik-analyzer", e); 30 31 } finally { 32 try { 33 if (is != null) { 34 is.close(); 35 is = null; 36 } 37 } catch (IOException e) { 38 logger.error("ik-analyzer", e); 39 } 40 } 41 // 加载扩展词典 42 this.loadExtDict(); 43 // 加载远程自定义词库 44 this.loadRemoteExtDict(); 45 // 从mysql加载词典 46 this.loadMySQLExtDict(); 47} 48 49/** 50 * 从mysql加载热更新词典 51 */ 52private void loadMySQLExtDict() { 53 Connection conn = null; 54 Statement stmt = null; 55 ResultSet rs = null; 56 57 try { 58 Path file = PathUtils.get(getDictRoot(), "jdbc-reload.properties"); 59 prop.load(new FileInputStream(file.toFile())); 60 61 logger.info("[==========]jdbc-reload.properties"); 62 for(Object key : prop.keySet()) { 63 logger.info("[==========]" + key + "=" + prop.getProperty(String.valueOf(key))); 64 } 65 66 logger.info("[==========]query hot dict from mysql, " + prop.getProperty("jdbc.reload.sql") + "......"); 67 68 conn = DriverManager.getConnection( 69 prop.getProperty("jdbc.url"), 70 prop.getProperty("jdbc.user"), 71 prop.getProperty("jdbc.password")); 72 stmt = conn.createStatement(); 73 rs = stmt.executeQuery(prop.getProperty("jdbc.reload.sql")); 74 75 while(rs.next()) { 76 String theWord = rs.getString("word"); 77 logger.info("[==========]hot word from mysql: " + theWord); 78 _MainDict.fillSegment(theWord.trim().toCharArray()); 79 } 80 81 Thread.sleep(Integer.valueOf(String.valueOf(prop.get("jdbc.reload.interval")))); 82 } catch (Exception e) { 83 logger.error("erorr", e); 84 } finally { 85 if(rs != null) { 86 try { 87 rs.close(); 88 } catch (SQLException e) { 89 logger.error("error", e); 90 } 91 } 92 if(stmt != null) { 93 try { 94 stmt.close(); 95 } catch (SQLException e) { 96 logger.error("error", e); 97 } 98 } 99 if(conn != null) { 100 try { 101 conn.close(); 102 } catch (SQLException e) { 103 logger.error("error", e); 104 } 105 } 106 } 107}

Dictionary类:更新分词 this.loadMySQLStopwordDict();

1/** 2 * 从mysql加载停用词 3 */ 4private void loadMySQLStopwordDict() { 5 Connection conn = null; 6 Statement stmt = null; 7 ResultSet rs = null; 8 9 try { 10 Path file = PathUtils.get(getDictRoot(), "jdbc-reload.properties"); 11 prop.load(new FileInputStream(file.toFile())); 12 13 logger.info("[==========]jdbc-reload.properties"); 14 for(Object key : prop.keySet()) { 15 logger.info("[==========]" + key + "=" + prop.getProperty(String.valueOf(key))); 16 } 17 18 logger.info("[==========]query hot stopword dict from mysql, " + prop.getProperty("jdbc.reload.stopword.sql") + "......"); 19 20 conn = DriverManager.getConnection( 21 prop.getProperty("jdbc.url"), 22 prop.getProperty("jdbc.user"), 23 prop.getProperty("jdbc.password")); 24 stmt = conn.createStatement(); 25 rs = stmt.executeQuery(prop.getProperty("jdbc.reload.stopword.sql")); 26 27 while(rs.next()) { 28 String theWord = rs.getString("word"); 29 logger.info("[==========]hot stopword from mysql: " + theWord); 30 _StopWords.fillSegment(theWord.trim().toCharArray()); 31 } 32 33 Thread.sleep(Integer.valueOf(String.valueOf(prop.get("jdbc.reload.interval")))); 34 } catch (Exception e) { 35 logger.error("erorr", e); 36 } finally { 37 if(rs != null) { 38 try { 39 rs.close(); 40 } catch (SQLException e) { 41 logger.error("error", e); 42 } 43 } 44 if(stmt != null) { 45 try { 46 stmt.close(); 47 } catch (SQLException e) { 48 logger.error("error", e); 49 } 50 } 51 if(conn != null) { 52 try { 53 conn.close(); 54 } catch (SQLException e) { 55 logger.error("error", e); 56 } 57 } 58 } 59}

配置

1jdbc.url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT 2jdbc.user=root 3jdbc.password=root 4jdbc.reload.sql=select word from hot_words 5jdbc.reload.stopword.sql=select stopword as word from hot_stopwords 6jdbc.reload.interval=1000

3、mvn package打包代码

target\releases\elasticsearch-analysis-ik-5.2.0.zip

4、解压缩ik压缩包

将mysql驱动jar,放入ik的目录下

5、重启es

6、在mysql中添加词库与停用词

7、kibana分词验证

1GET /my_index/_analyze 2{ 3 "text": "一人饮酒醉", 4 "analyzer": "ik_max_word" 5} 6 7{ 8  "tokens": [ 9    { 10      "token": "一人饮酒醉", 11      "start_offset": 0, 12      "end_offset": 5, 13      "type": "CN_WORD", 14      "position": 0 15    }, 16    { 17      "token": "一人", 18      "start_offset": 0, 19      "end_offset": 2, 20      "type": "CN_WORD", 21      "position": 1 22    }, 23    { 24      "token": "一", 25      "start_offset": 0, 26      "end_offset": 1, 27      "type": "TYPE_CNUM", 28      "position": 2 29    }, 30    { 31      "token": "人", 32      "start_offset": 1, 33      "end_offset": 2, 34      "type": "COUNT", 35      "position": 3 36    }, 37    { 38      "token": "饮酒", 39      "start_offset": 2, 40      "end_offset": 4, 41      "type": "CN_WORD", 42      "position": 4 43    }, 44    { 45      "token": "饮", 46      "start_offset": 2, 47      "end_offset": 3, 48      "type": "CN_WORD", 49      "position": 5 50    }, 51    { 52      "token": "酒醉", 53      "start_offset": 3, 54      "end_offset": 5, 55      "type": "CN_WORD", 56      "position": 6 57    }, 58    { 59      "token": "酒", 60      "start_offset": 3, 61      "end_offset": 4, 62      "type": "CN_WORD", 63      "position": 7 64    }, 65    { 66      "token": "醉", 67      "start_offset": 4, 68      "end_offset": 5, 69      "type": "CN_WORD", 70      "position": 8 71    } 72  ] 73}
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Elasticsearch 之(25)重写IK分词器源码来基于mysql热更新词库 - HelloWorld