由于最近公司的的线上商城发现了对于商品搜索的问题,之前用的是mysql数据库进行实现的。当用户输入关键字的时候,就会在数据库里对商品名称进行模糊查询。这样查询的弊端有几个
1:当数据量大的时候,用like效率往往不高
2:满足不了日常的搜索需求,比如搜索儿童口罩,用like,是查询不到儿童一次性口罩的。这并不是我们想要的。
所以就决定把搜索引擎独立出来。
因为ES无论是从效率,还是结果上都符合我们的预期。并且ES的更新比较活跃。
一、关于ES的安装
ES的安装网上有很多资料,不过自己要注意自己安装的ES版本。大版本之间的区别还是挺大的。
我这里安装的是ES7.6.2,我的资源里面有,相关的插件也在里面。可以打包下载。
ES安装
1、将包放进服务器(我这是linx),然后解压缩 :
tar -zxvf elasticsearch-7.6.2-linux-x86_64.tar.gz
解压完成后会有一个
elasticsearch-7.6.2的文件夹
2、进入文件夹修改config文件下的elasticsearch.yml文件 命令vi elasticsearch.yml
编辑的内容如下
cluster.name: xl#集群名称
node.name: xl-node-1 #节点名称
#数据和日志的存储目录
path.data: /usr/local/bin/es-groot/elasticsearch-7.6.2/data
path.logs: /usr/local/bin/es-groot/elasticsearch-7.6.2/logs
#设置绑定的ip,设置为0.0.0.0以后就可以让任何计算机节点访问到了
network.host: 0.0.0.0
http.port: 9200 #端口
#设置在集群中的所有节点名称,这个节点名称就是之前所修改的,当然你也可以采用默认的也行,目前是单机,放入一个节点即可
cluster.initial_master_nodes: ["xl-node-1"]
#跨域用(等下安装HEAD的时候会牵扯到跨域问题)
http.cors.enabled: true
http.cors.allow-origin: "*"
到此基本配置结束。
3、elasticsearch是不允许使用root账户启动的。
adduser username
passwd grootes
4、把整个解压目录的权限给这个用户
chown username /usr/local/elasticsearch-7.6.2 -R
5.修改elasticsearh根目录下bin目录中的elasticsearch脚本。
export JAVA_HOME=/usr/local/elasticsearch-7.6.2/jdk/
export PATH=$JAVA_HOME/bin:$PATH
在开头添加这两行,这一步主要是用EL的内置jdk,因为EL所需要的JDK版本极有可能和你的JDK版本不一样,所以直接用内置JDK
6 修改jvm.options
将 : -XX:+UseConcMarkSweepGC
改为:-XX:+UseG1GC
因为内置版本会影响回收机制,需要修改配置,否则启动报错
7、这个时候就可以到bin下面启动了
./elasticsearch -d(-d是后台启动)
然后再网页进行验证输入ip:9200会出一下界面
这说明我们启动成功了。
二、安装ik分词器
在elasticsearch-7.6.2/plugins下创建ik文件夹
Mkdir ik
将压缩包解压到ik文件夹下面 unzip elasticsearch-7.6.2.zip
重启elasticsearch
可以用postman进行验证是否安装成功
如果和图片上一样,那么就安装成功了
三、接下来我们安装HEAD插件。
HEAD是EL集群管理工具,也可以当做可视化工具。
1、将 elasticsearch-head-master.zip进行加压 unzip elasticsearch-head-master.zip
解压完成后进入 elasticsearch-head ,然后执行npm install (没有命令的先安装nodejs
)
然后直接启动 npm run start
访问ip:9100查看是否安装成功
四、ES基本概念
索引,类型,文档,字段。
对应关系型数据可以理解为索引对应数据库,类型对应表,文档对应数据,字段对应字段。
所以我们的第一步是建立索引。
可以先到刚才的HEAD页面进行操作
其中有几个参数需要填写。索引名称,分片数,副本数,索引名称就是数据库名称。分片数是把数据存储成几份,比如10条数据设置的分片数是2,则会分成两份存储,每一份存5条。副本数就是产生的副本数量。单分片超过50G可能影响性能,所以根据实际需求进行分片设置。这里我们建立一个people的索引,分片数1,副本数0
建立完成后
这个时候,我们的索引就建立完成了
然后想索引中插入一个文档,这里的文档指的就是一条数据。
存入一条name为刘大虎的数据,然后进行查询,people后面的"_doc是类型,也就是数据库中的表名字,由于后面的版本这个会去除,所以这个版本也只允许一个index只有一个type了。所以可以固定一个死的。这里用了_doc
查询条件中加入 刘,刘大虎,大虎,虎子,都可以查询出来结果,因为我们在添加这条数据的时候,ES默认对他进行了分词处理,ES自带的分词器对中文的分词是一个字一个字的,也就是在添加数据的时候,ES把刘大虎这个字段分别建立了刘,大,虎三个索引。用math匹配也会自动为你进行分词处理。你的搜索条件是虎子的时候,就会把你的搜索条件拆分成虎,子两个字和索引进行匹配。所以是可以查到的,但是这也并不是我们想要的结果。查分的颗粒度太小。一方面会占用大量的存储空间,一方面会匹配到很多完全不相关的搜索结果。这里就要用到IK分词插件了,对中文有良好的支持,并且可以扩展自定义字典。在建立索引的时候我们就要指定哪些是需要进行分词索引的,因为毕竟分词索引占用的空间比较多,一个说明内容分词出来的索引可能达上百个。
现在我们重新建立所以
{
"settings" : {
"number_of_shards" : 1, //分片数
"number_of_replicas" : 0, //备份数
"analysis" : {
"analyzer" : {
"ik" : {
"tokenizer" : "ik_max_word"
}
}
}
},
"mappings" : {
"properties" : {
"intro" :
{ "type": "text", //字段类型
"analyzer": "ik_max_word", //分词器
"search_analyzer": "ik_max_word" //搜索时用的分词器
},
"title" : {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"name":{
"type":"keyword"
}
}
}
}
我们制定了intro字段为IK分词,查询IK分词。title字段也一样,name指定为不分词。我们分别插入数据测试 ,最后再详细说其中的原因和详细配置项
这个时候进行查询发现
输入儿童口罩可以查询到,那会不会还是索引的颗粒度最小化了呢,我们再输入一个儿子,发现结果就查不到了。证明这次的索引建立规则和之前的规则不一样的,这次是数据在保存的时候,IK按照中文词汇进行了字段分词,将儿童一次性口罩分成了儿童,一次性,一次,口罩存入了索引之中,在查询的时候也会将查询条件进行中文词汇分词,输入儿童口罩,会自动分成儿童,口罩,两个词,然后去匹配索引,任一匹配到,将返回结果。
到这里是不是感觉只要在前期进行好索引的建立。字段的初始化配置。后期只要使用就行了。很方便。
那我们就来看一看建立一个索引的时候需要的参数
{
"settings" : {
"number_of_shards" : 1, //分片数
"number_of_replicas" : 0, //备份数
"analysis" : {
"analyzer" : {
"ik" : {
"tokenizer" : "ik_max_word"
}
}
}
},
"mappings" : {
"properties" : {
"intro" :
{ "type": "text", //字段类型
"analyzer": "ik_max_word", //分词器
"search_analyzer": "ik_max_word" //搜索时用的分词器
},
"title" : {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"name":{
"type":"keyword"
}
}
}
}
ik分词经常用到的有ik_smart和ik_max_word,ik_smart分出来的词会尽量避免重复,而有价值,但是查全率低,ik_max_word会进行尽可能多的分词,但是查准率低。
为了使得同时提高查全率和查准率,可以在建立索引的时候用ik_max_word分词,在查询的时候用ik_smart分词。这样可能造成的结果就是ik_smart分出来的词如果在ik_max_word建立的索引中不存在(比如一些网红词汇,ik_max_word需要更新词库才能分出来),那么将搜索不到。比较暴力的解决办法就是将查询和搜索都用ik_max_word。根绝业务功能选择