Elasticserch 5.6到Elasticsearch7.11跨版本升级踩坑记录
1.背景
公司老的业务使用的是Elasticsearch5.6版本,使用的客户端是TransportClient,java集成的SDK,由于老版本的Elasticsearch不支持加密操作,由于日前网络安全显得很重要,所以需要对索引库进行加密,保证数据的正确性,所以一个工程中要兼容两个版本的Elasticsearch,且能够通过读取环境变量来选择执行哪套代码,所以有了下面这些坑。
2.过程
2.1 安装Elasticsearch7.11和对应版本的分词器
要使用Elasticsearch7.11首先需要做的是从官方下载安装包,然后进行安装,如果需要安装分词器的话可以去Gitlub下载一些开源的分词器,像现在开源的比较流行的是lc-pinyin ,pinyin,ik分词器,再装elasticsearch分词器的时候切记分词器的版本一定要与安装的elasticsearch7.11的版本保持一致,elasticsearch会通过读取plugin-descriptor.properties这个文件中的信息来判断安装分词器的版本,具体的源码可以去gitlub上下,据我所知pinyin这个分词器的版本只更新到了5.6版本,如果要在Elasticsearch7.11中使用肯定是不行的,可以自己将代码下载下来,修改pom文件的jar包版本然后打成jar,最后修改plugin-descriptor.properties中的配置,再上传到elasticsearch7.11目录的plugin目录下,以普通用户启动elasticsearch,用root权限启动elasticsearch会报错,具体的安装步骤网上有很多资料,这里不再叙述
2.2 创建索引
1.Elasticsearch5.6到Elasticsearch7.11.1最大的改变其实是去除了type这个类型,统一文档类型为_doc,但是这个改变从Elasticsearch7.0就开始了,之所以使用Elasticsearch7.11.1主要是因为支持加密并且开源不收费,而7.9以前都是要收费的,当然你可以去申请免费的,大概有效期是30天,如果是个人用户,其实可以更改代码里面的配置来实现永久,但是用于商业最好还是不要用。
2.创建索引主要遇到以下问题
1.当把Elasticsearch5.6中mapping放到Elasticsarch7.11中创建会出现以下问题
{"error":{"root_cause":[{"type":"mapper_parsing_exception","reason":"Root mapping definition has unsupported parameters:

出现上述问题的根本原因是由于Elasticsearch去除了type这个概念,默认都是_doc,只要把doc这个属性去掉就可以了。
2.当我们把doc这个属性去掉以后,再创建索引,然后又出来新来的问题
{"error":{"root_cause":[{"type":"mapper_parsing_exception","reason":"Root mapping definition has unsupported parameters: [_all : {enabled=false}]"}]

看错误我们知道是不支持_all这个属性,原来Elasticsearch7.11把__all这个属性去除了,然后我们再把_all这个去除掉再执行
3.当我们把_all这个属性去除掉后,然后再创建索引,新的问题又来了
{"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"The difference between max_gram and min_gram in NGram Tokenizer must be less than or equal to: [1] but was [19]
出现这个问题的主要原因是因为,我们设置分词器的时候最大分词和最小分词大于1,由于公司问题只能截取部分图片
解决方案其实也很简单,只要将最大和最小的在setting中设置成我们相差的就可以,如下图
2.3思考兼容方案
1.实际上方案我是探索了三四个方案,第一个方案是做一个新的工程,但是由于新的工程后期比较难维护,所以被否掉了,然后就是将Elasticsearch7.11的java的SDk导入gradle会出现在冲突,由于Rest-high-level-client和Transportclient中的类名和包名都是一致的,导致jvm虚拟机不知道加载哪一个类,所以我们采用的是自定义类加载器的方式,通过配置去决定加载哪个版本的jar包,自定义加载器肯定是会有很多不可规避的问题,像一些轮子其实都已经搞了,但是找了半天都没有gradle的都是基于maven的,像蚂蚁金服的SOFAArk和shade还有滴滴的JuShaTa看了很多但是就是没有合适自己的方案,尝试自己写类加载器,写了两天就放弃了,毕竟对于我这种刚毕业的小白,让我写类加载器肯定是有点困难的,怎么办?
2.突然有一天我突发奇想,本身不管是TransportClient和RestHigh-level的Client底层都是用的json的格式去访问Elasticseasrch的,于是我就将TransportClient生成的查询语句用Elasticsearch7.11查询结果发现各种报错,Elasticsearch7.11竟然还剔除了一些查询字段,就连返回结果的格式都变了,我真的是想骂人了。
3.于是我又换了种思考方式,ElasticSearch不是可以通过http请求直接访问吗,我可不可以直接自己调用http请求的来返回结果呢,当我准备做的时候,我说出了我的想法,没想到公司的架构说Elasticsearch低版本的也有Rest-Client可以发送http请求,但是我真的是感慨自己还是要好好学习,再经过讨论,以及公司其他项目组有人用过这种方式的时候准备尝试
2.4尝试
1.思考
在业务逻辑改动不大的情况下,实现两个版本的兼容,怎么说呢,这个工程师个tomcat工程,说实话老工程就是这么恶心,如果自己再把组装查询语句工作量肯定很大,从时间成本上和后期维护上来看肯定是不行的,于是我又灵机一动,直接把生成的查询语句发送http请求不就好了,再把放回的结果写成一个公用的方法来解析不就好了
2.踩坑
尝试避免不了采坑,TransportClient java SDK生成的查询语句中,BoolQueryBuilder这个类中每次生成的时候都会带上adjust_pure_negative这个属性
{
"bool" : {
"should" : [
{
"match_phrase" : {
"robotName" : {
"query" : "kafka",
"slop" : 0,
"boost" : 1.0
}
}
},
{
"match" : {
"pinyin" : {
"query" : "kafka",
"operator" : "AND",
"prefix_length" : 0,
"max_expansions" : 50,
"fuzzy_transpositions" : true,
"lenient" : false,
"zero_terms_query" : "NONE",
"boost" : 1.0
}
}
}
],
"disable_coord" : false,
"adjust_pure_negative" : true,
"boost" : 1.0
}
}
从网上查看这个属性好像作用也不是很大默认是false,当是true的时候主要是怕查询must_not的时候没有数据返回,而做的一种优化,软用没有,但是怎么剔除呢,中途我有想过转换成JSONObject然后取剔除,但是由于层级是不清楚的,采用递归的方式可能会出现栈深度过大而导致出问题,然后就是过程实在是太复杂,果断放弃
3.灵感
记得那天我干到8点还没吃饭,下去吃饭的时候边吃饭边想,果然吃饱了饭,有了灵感,这个东西本身也是一个String,可不可以将他替换呢,吃完饭上来试了一下,结果替换不了,实在太晚了后面就回去了,然后第二天来了给组长讲了我的方法,然后组长给了一点提示,自己经过慢慢试错,最后得出了下面的正则表达式,这下面代码才是精髓,完美的剔除了所有的这个字段,然后取Elasticsearch中查询没有报错,测试了20多个接口都正常
static final String check="\"disable_coord\"\\s:\\sfalse,\n\\s+";
static Pattern pattern=Pattern.compile(check);
4.迁移数据
数据量小的情况下我是使用reindex的方式供自己测试使用,因为公司有运维,大的话可以采用logstatch的方式
下面给出迁移数据的curl
curl --location --request POST 'http://192.168.22.92:9700/_reindex?pretty' \ -目标机器的http端口
--header 'Authorization: Basic ZWxhc3RpYzpLaW5nZGVlQDIwMjE=' \ -elasticsearch 集群的用户名 密码
--header 'Content-Type: application/json' \
--data-raw '{
"conflicts": "proceed",
"source": {
"remote": {
"host": "http://192.168.22.92:9200/" -源机器
},
"index": "textmessage", -需要迁移的索引
"query": {
"term": {
"_type": "doc" -匹配类型
}
},
"size": 6000
},
"dest": {
"index": "textmessage" -目标索引
},
"script": {
"inline": "if (ctx._id.length()>512) {ctx._id = ctx._id.substring(0,511)}", -防止id过长报错进行截取
"lang": "painless"
}
}'
5.插曲
那天和和运维一起玩Elasticsearch,结果他把elasticsearch的索引全部干掉了,然后通过密码去连接报错,显示没有权限,但是密码还是那个密码,为什么会把错了,原来Elasticsearch将用户的账号和密码保存到一个叫.security这个索引中,删除全部索引会把这个索引也删除掉,所以当你装了x-pack的时候鉴权就会失败,需要重新创建一个用户,让我很懵的是Elasticsearch竟然没有做校验直接将索引删了,Elasticsearch还是有一点比较好的,当磁盘空间不足的时候,Elasticsearch会开启自我保护措施,只能读不能写,这点做的还是做的挺好的
3.总结
当你装了x-pack的时候鉴权就会失败,需要重新创建一个用户,让我很懵的是Elasticsearch竟然没有做校验直接将索引删了,Elasticsearch还是有一点比较好的,当磁盘空间不足的时候,Elasticsearch会开启自我保护措施,只能读不能写,这点做的还是做的挺好的
3.总结
开发是一个很有趣的过程,刚开始的时候我很不情愿,老是怕,但是思考又是一个很令人兴奋的事情,特别是当你想到一个灵感的时候,只有不断的学习才能提高自己,通过这件事情我学到了很多的,分析的问题不要将事情想的太复杂,如果一开始你就怕那肯定解决不了问题,要敢于尝试,将问题拆分开,比如我写那个正则表达式的时候,特别想一步到位就写出来,本身对那个就不熟,所以写不出来,只有理智分析问题,把问题拆分开来看,一步一步来最后只要方向对了都能够解决问题,写这个完全是记录自己工作中踩过的一些坑,菜鸡一枚勿喷!