作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
上期文章:详解SpringCloud微服务技术栈:一文速通RabbitMQ,入门到实践
订阅专栏:微服务技术全家桶
希望文章对你们有所帮助
ElasticSearch已经是非常常见和必须要掌握的技术了。这篇文章包括ElasticSearch原理的描述,安装与实践,还没有涉及到java层代码,就跟当初学MySQL一样,先学完这部分内容,之后再结合RestClient操作ElasticSearch。
ElasticSearch是一个非常强大的开源搜索引擎,可以帮助我们从海量数据中心快速找到需要的内容。
我们常用的GitHub,搜索仓库的时候,不仅可以搜索到仓库,还可以把其中的代码片段都给搜出来,高亮显示。
ElasticSearch结合kibana、Logstash、Beats,也就是elastic stack(ELK,即以ElasticSearch为核心的技术栈),被广泛应用在日志数据分析、实时监控等领域。
ElasticSearch就是elastic stack的核心,负责存储、搜索、分析数据。
其中数据抓取和数据可视化就不一定要用上图的组件,可以自行实现,核心还是ElasticSearch
传统数据库(MySQL)采用正向索引,即利用表中的id来创建索引,形成一颗B+树,所以根据id来检索的速度会非常的快。但如果是根据标题或者内容来进行检索,就会逐条扫描,非常慢。
而倒排索引,在创建的时候会形成一个新的表,有2个字段:
文档(document):每条数据就是一个文档
词条(term):文档按照语义分成的词语
例如:
可以很直观的发现,词条是唯一的,因此可以基于词条去做哈希,或者基于词条去设计B+树,这样就可以大大提高检索标题、内容的速度,而不像正向索引那样必须用模糊匹配这种非常慢的方式。
首先需要弄明白ElasticSearch中的2个概念:文档、索引。
文档:
ElasticSearch是面向文档存储的,可以是数据库中的一条商品数据,一个订单信息。
文档数据会被序列化为json格式后存储在ElasticSearch中。
索引:
索引:相同类型的文档的集合
例如下面的若干文档,ElasticSearch会将相同类型的文档放在一起:
上面的步骤就是索引的映射,即索引中文档的字段约束信息,相当于表结构的约束。
因此,可以将MySQL的一些概念与ElasticSearch进行对比:
MySQL | ElasticSearch | 说明 |
---|---|---|
Table | Index | 索引(index),就是文档的集合,类似数据库的表(table) |
Row | Document | 文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式 |
Column | Field | 字段(Field),就是JSON文档中的字段,类似数据库中的列(Column) |
Schema | Mapping | Mapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema) |
SQL | DSL | DSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD |
但ElasticSearch并不能替代MySQL。
MySQL擅长事务类型操作,可以确保数据的安全和一致性
ElasticSearch没有事务的概念,它擅长海量数据的搜索、分析、计算
因此,两个联合使用是可以结合两者的优点的,当我们进行写操作的时候,写到MySQL是更好的选择,而当我们需要进行搜索的时候,在ElasticSearch中搜索效率更高,因此这就要求ElasticSearch中也要存储数据,只不过不是处理请求的时候写,那样并不安全,而是写入MySQL之后,由MySQL去做数据同步给ElasticSearch。
1、创建网络
因为我们还需要部署kibana容器,因此需要让es和kibana容器互联,这里先创建一个网络:
docker network create es-net
2、加载镜像
这里采用elasticsearch的7.12.1版本的镜像,接近1G,不建议直接pull,直接用百度网盘的镜像tar包:
链接:https://pan.baidu.com/s/1fyquyv0tEWyNG9kOQj97cg?pwd=upej
提取码:upej
上传完毕后,运行加载命令即可:
docker load -i es.tar
docker run -d \
--name es \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
-e "discovery.type=single-node" \
-v es-data:/usr/share/elasticsearch/data \
-v es-plugins:/usr/share/elasticsearch/plugins \
--privileged \
--network es-net \
-p 9200:9200 \
-p 9300:9300 \
elasticsearch:7.12.1
配置了内存大小为512M,单点模式,并将ES容器加入到es-net中,而且指定了插件的目录,之后如果需要一些插件就放在这个目录下。其中9200端口为暴露的HTTP协议端口,9300是各个ES容器互联的端口。
kibana可以帮助我们编写ES中的DSL语句,从而操作ES。
给一个kibana.tar的文件:
链接:https://pan.baidu.com/s/1AeEJZ1VBn8jEO031S7r5Ww?pwd=0699
提取码:0699
和上面步骤一样,拖到\tmp
目录下再执行docker load -i kibana.tar
就行,接着运行docker命令,部署kibana:
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601 \
kibana:7.12.1
ES在创建倒排索引时需要对文档分词;在搜索时,需要对用户输入内容分词。但默认的分词规则对中文处理并不友好。标准的分词规则会是一字一分,这显然是不符合需求的。
IK分词的安装,需要知道elasticsearch的plugins目录位置,用了数据卷挂载,因此需要查看elasticsearch的数据卷目录,通过docker volume inspect es-plugins
查看:
把tar包在本地解压一下上传到这个文件夹下,tar包可以从网盘获取:
链接:https://pan.baidu.com/s/1beOMuAA6uns4CFM1UPoVAg?pwd=b0zi
提取码:b0zi
上传了以后需要重启ES:docker restart es
。
IK分词器包含2种模式:
ik_smart:粗粒度切分,例如
程序员
单纯被切成程序员
ik_max_word:细粒度切分,例如程序员
被切成程序员
和程序
+员
具体用哪个模式,需要看具体的业务。
可以自行
测试IK分词器是可以查看到效果还是很不错的,其底层不用想,一定是有一个中文字典的,其他分词器也肯定是这样的。但是字典里面不一定就包含全部的中文词语,而且现在是会产生一些新词的,但字典里面却没有,就会导致一些情况下拆分的准确率大打折扣。
所以理论上我们可以手动进行分词器的拓展,也就是拓展这个词典。
另外一个重要的地方,一些不重要的词,例如的
,它无法和其他词组成在一起,而且没有什么含义,没有必要单独划分出来还占空间,而IK分词器正好具有停用字典的功能选项。
要拓展IK分词器的词库或者禁用某些词条,只需要修改IK分词器目录中的config目录中的IKAnalyzer.cfg.xml文件:
填写一下文件名:
这两个文件就在该xml文件的所在目录下,其中ext.dic需要我们自行去创建。
而stopword.dic已经是有一些现成的了,可以自行再添加一些:
接着要用docker restart es
重启,这个拓展和停用就可以生效了。
类似MySQL,MySQL是要现有表才能插入数据,而ES是先要有索引库才能插入文档。MySQL的表在创建的时候要建立约束,ES的索引库在创建的时候要建立mapping映射属性。
mapping是对索引库中文档的约束,常见的mapping属性包括:
type:字段数据类型,常见的简单类型有:
(1)字符串:text(可分词的文本)、keyword(精确值,例如毛里求斯
不可拆)
(2)数值:long、integer、short、byte、double、float
(3)布尔:boolean
(4)日期:date
(5)对象:object
index:是否创建倒排索引,默认为true
analyzer:使用那种分词器
properties:该字段的子字段
ES中通过Restful请求操作索引库、文档。请求内容用DSL来表示。创建索引库和mapping的DSL语法如下:
PUT /user
{
"mappings": {
"properties": {
"info": {
"type": "text",
"analyzer": "ik_smart"
},
"email": {
"type": "keyword",
"index": "false"
}
"name": {
"properties": {
"firstName": {
"type": "keyword"
}
}
}
}
}
}
info内容太多所以用ik_smart来分词,email没必要用来搜索,就没有必要建立倒排索引,name里面可能还有别的属性。
总之,看具体应用场景来选择参数。
索引库的查询和修改操作都是很容易实现的。
查看索引库:
GET /索引库名
删除索引库:
DELETE /索引库名
而索引库的修改并不那么容易,实际上是不允许修改的,因为当创建好索引库之后,其mapping映射都已经创建好了,ES也做好了倒排索引了,修改索引库带来的影响就会很大。
但是mapping映射虽然不允许修改,但是可以在原有的那些映射中再加新的映射,语法如下:
PUT /索引库名/_mapping
{
"properties": {
"新字段名":{
"type": "integer"
}
}
}
新字段名一定是不能和之前的字段名是重复的,否则就禁止修改。
文档的操作可以对标MySQL中对数据的操作,使用的语言是DSL,与MySQL使用的SQL语言对标。
新增文档的DSL语法如下:
POST /索引库名/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子属性1": "值3",
"子属性2": "值4"
}
}
查询文档的DSL语法:
GET /索引库名/_doc/文档id
删除文档的DSL语法:
DELETE /索引库名/_doc/文档id
其实这就是一种Restful风格的,做起来都挺容易的,也容易记住。
修改文档就相对复杂一点。
方式一:全量修改,会删除旧文档,添加新文档
PUT /索引库名/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子属性1": "值3",
"子属性2": "值4"
}
}
这种方式有一个好处,当文档id不存在的时候,这条指令相当于执行了新增操作,因此它是可以直接替代掉之前所说的新增POST请求。
方式二:增量修改,修改指定字段值
POST /索引库名/_update/文档id
{
"doc": {
"字段名": "新的值"
}
}