如果你觉得官网下载很慢,那么你可以去华为云的镜像去下载,速度很快,自己找对应版本就可以。
ElasticSearch: https://mirrors.huaweicloud.com/elasticsearch/?C=N&O=D
logstash: https://mirrors.huaweicloud.com/logstash/?C=N&O=D
kibana: https://mirrors.huaweicloud.com/kibana/?C=N&O=D
提示:你的换用中要有Node.js、Python和jdk1.8+的环境,我们的elasticsearch的环境是7.6.1版本的,安装的时候一定要注意版本统一
一、ElasticSearch 的安装
1、安装
对于ElasticSearch的安装,我们只需要解压就可以
2、目录介绍
bin:启动目录
config:配置目录
log4j2.properties:日志配置文件
jvm.options:java虚拟机配置文件
elasticsearch.yml:elasticsearch配置文件
jdk:javajdk
lib:相关jar包
log:日志
modules:功能模块
plugins:插件
3、启动
双击 D:\environment\elasticsearch\elasticsearch-7.6.1\bin\elasticsearch.bat
我们可以看到开始有这么一句话,提示我们的jdk1.8版本太低,提示我们使用更新的版本,所以,要求你的jdk最低是1.8
elasticsearch的路由端口是9300,公有地址是9200
4、访问
访问http://127.0.0.1:9200/
二、head 的安装(可视化界面)
head是elasticsearch的可视化工具,是准备针对elasticsearch的客户端
1、下载
在https://github.com/mobz/elasticsearch-head中下载head插件,选择下载zip
解压即可
2、安装
官方的安装步骤(你的电脑中一定要安装了Node.js)
npm install 或 cnpm install
3、启动
npm run start
访问:http://localhost:9100
,连接(会存在跨域问题)
解决跨域问题:关闭elasticsearch,配置elasticsearch.yml
文件
# ---------------------------------- Various -----------------------------------
#
# Require explicit names when deleting indices:
#
#action.destructive_requires_name: true
http.cors.enabled: true # 配置开启跨域
http.cors.allow-origin: "*" # 允许所有人访问
重启elasticsearch,访问http://127.0.0.1:9200/
访问:http://localhost:9100
,连接(成功)
建立索引
ElasticSearch是文档型数据库,索引(Index)定义了文档的逻辑存储和字段类型,每个索引可以包含多个文档类型,文档类型是文档的集合,文档以索引定义的逻辑存储模型。我们可以认为索引就是一个库就可以,其中可以存储信息
三、kibana 的安装
Kibana 是一款免费开源的前端应用程序,其基础是 Elastic Stack,可以为 Elasticsearch 中索引的数据提供搜索和数据可视化功能。尽管人们通常将 Kibana 视作 Elastic Stack(之前称作 ELK Stack,分别表示 Elasticsearch、Logstash 和 Kibana)的制图工具,但也可将 Kibana 作为用户界面来监测和管理 Elastic Stack 集群并确保集群安全性,还可将其作为基于 Elastic Stack 所开发内置解决方案的汇集中心
1、安装
对kibana的安装,我们只需要解压即可
2、启动
双击D:\environment\elasticsearch\kibana-7.6.1-windows-x86_64\binkibana.bat
默认端口为5601
访问http://localhost:5601/
开发工具(我们的查询代码基本上都会在这里写)
3、汉化
我们可看到在这里有一个中文的json
设置汉化
配置kibana-7.6.1-windows-x86_64\config\kibana.yml
重启kibana(汉化成功)
四、ES 工作原理
1、介绍
先说Elasticsearch的文件存储,Elasticsearch是面向文档型数据库,一条数据在这里就是一个文档,用JSON作为文档序列化的格式,比如下面这条用户数据:
{
"name" : "studioustoger",
"sex" : "male",
"age" : 18,
"birthDate": "2020/05/01",
"about" : "I love to sleep,
"interests": [ "sports", "music" ]
}
用Mysql这样的数据库存储就会容易想到建立一张User表,有各种各样的字段,在Elasticsearch里这就是一个文档,当然这个文档会属于一个User的类型,各种各样的类型存在于一个索引当中。这里有一份简易的将Elasticsearch和关系型数据术语对照表:
关系数据库 ⇒ 数据库 ⇒ 表 ⇒ 行 ⇒ 列(Columns)
Elasticsearch ⇒ 索引(Index) ⇒ 类型(type) ⇒ 文档(Docments) ⇒ 字段(Fields)
一个 Elasticsearch 集群可以包含多个索引(数据库),也就是说其中包含了很多类型(表)。这些类型中包含了很多的文档(行),然后每个文档中又包含了很多的字段(列)。Elasticsearch的交互,可以使用Java API,也可以直接使用HTTP的Restful API方式,比如我们打算插入一条记录,可以简单发送一个HTTP的请求:
PUT /demo/user/1
{
"name" : "studioustoger",
"sex" : "male",
"age" : 18,
"birthDate": "2020/05/01",
"about" : "I love to sleep,
"interests": [ "sports", "music" ]
}
至于更新和查询,也是类似的操作,在这里就不展开讲了,后面会详细的讲…
2、索引
Elasticsearch最关键的就是提供强大的索引能力了,其实InfoQ的这篇时间序列数据库的秘密(2)——索引写的非常好,我这里也是围绕这篇结合自己的理解进一步梳理下,也希望可以帮助大家更好的理解这篇文章。
Elasticsearch索引的精髓:一切设计都是为了提高搜索的性能
另一层意思:为了提高搜索的性能,难免会牺牲某些其他方面,比如插入/更新,否则其他数据库不用混了。前面看到往Elasticsearch里插入一条记录,其实就是直接PUT一个json的对象,这个对象有多个fields,比如上面例子中的name, sex, age, about, interests,那么在插入这些数据到Elasticsearch的同时,Elasticsearch还默默1的为这些字段建立索引–倒排索引,因为Elasticsearch最核心功能是搜索。
Elasticsearch是如何做到快速索引的
InfoQ那篇文章里说Elasticsearch使用的倒排索引比关系型数据库的B-Tree索引快,为什么呢?
什么是B-Tree索引?
上大学读书时老师教过我们,二叉树查找效率是logN,同时插入新的节点不必移动全部节点,所以用树型结构存储索引,能同时兼顾插入和查询的性能。因此在这个基础上,再结合磁盘的读取特性(顺序读/随机读),传统关系型数据库采用了B-Tree/B+Tree这样的数据结构:
为了提高查询的效率,减少磁盘寻道次数,将多个值作为一个数组通过连续区间存放,一次寻道读取多个数据,同时也降低树的高度。
什么是倒排索引?
Elasticsearch最强大的就是为每个字段提供了倒排索引,当查询的时候不用担心没有索引可以利用,什么是倒排索引,举个简单例子:
每一行是一个文档(document),每个document都有一个文档ID。那么给这些文档建立的倒排索引就是:
年龄的索引:
性别的索引:
可以看到,倒排索引是针对每个字段的,每个字段都有自己的倒排索引,25、32、男、女这些叫做term,[1,3]、[1,2]这种叫做posting list,
它是一个int的数组,存储了所有符合某个term的文档id,这时候我们想找出年龄为25的人,就会很快速,但是这里只有两个term,如果有成百上千个term呢,那找出某个term就会很慢,因为term还没有排序,解决这个问题需要了解两个概念:Term Dictionary 和 Term Index。
Term Dictionary
Elasticsearch为了能快速找到某个term,将所有的term进行了排序,然后二分法查找term,类似于上学时候老师教我们的翻新华字典的方式,所以这叫做Term Dictionary,这种查询方式其实和传统关系型数据库的B-Tree的方式很相似,所以这并不是Elasticsearch快的原因。
Term Index
如果说Term Dictionary是直接去二分法翻字典,那么Term Index就是字典的目录页(当然这比我们真的去翻字典目录快多了),假设我们的term如果全是英文单词,那么Term Index就是26个字母表,但是通常term未必都是英文,而可以是任意的byte数组。因为就算26个英文字符也不一定都有对应的term,比如:a开头的term只有一个,c开头的term有一百万个,x开头的term一个也没有,这样查询到c的时候又会很慢了。所以通常情况下Term Index 是包含term的一些前缀的一棵树,例如这样的一个Term Index:
这棵树不会包含所有的term,它包含的是term的一些前缀。通过term index可以快速地定位到term dictionary的某个offset,然后从这个位置再往后顺序查找。
这样的情况下通过Term Index据可以快速定位到某个offset(分支的开端),然后以此位置向下查找,再加上FST(Finite-State Transducer,Lucene4.0开始使用该算法来查找Term 在Dictionary中的位置)的压缩技术,将Term Index 缓存到内存中,通过Term Index 找到对应的Term Dictionary的 block,然后再去磁盘直接找到term,减少磁盘的随机读写次数,大大的提升查询效率。
3、Posting List压缩技术 Roaring Bitmap
说到Roaring bitmaps,就必须先从bitmap说起。Bitmap是一种数据结构,假设有某个posting list:
[1,3,4,7,10]
对应的bitmap就是:
[1,0,1,1,0,0,1,0,0,1]
非常直观,用0/1表示某个值是否存在,比如10这个值就对应第10位
,对应的bit值是1,这样用一个字节就可以代表8个文档id,旧版本(5.0之前)的Lucene就是用这样的方式来压缩的,但这样的压缩方式仍然不够高效,如果有1亿个文档,那么需要12.5MB的存储空间,这仅仅是对应一个索引字段(我们往往会有很多个索引字段)。于是有人想出了Roaring bitmaps这样更高效的数据结构。
Bitmap的缺点是存储空间随着文档个数线性增长,Roaring bitmaps需要打破这个魔咒就一定要用到某些指数特性:
Elasticsearch不仅压缩了Term Index,还对posting list 进行了压缩,posting list虽然只存储了文档id,但是当文档id很大的时候,比PB级的数据,Elasticsearch对posting list的压缩做了两件事:排序和大数变小数,引用一张被引用无数次的图:
解释上面的图:
step1:在对posting list进行压缩时进行了正序排序。
step2:通过增量将73后面的大数变成小数存储增量值。
step3: 转换成二进制,取占最大位的数,227占8位,前三个占八位,30占五位,后三个数每个占五位。
从上面的step3中可以看出,这种压缩方式仍然不够高效,所以Lucene使用的数据结构叫做Roaring Bitmap,其压缩的原理可以理解为,与其保存100个0,占用100个bit,还不如保存0一次,然后声明这个0有100个,它以两个自己可以表示的最大数65535为界,将posting list分块,比如第一块是0-65535,第二块是65536-131071,如图:
解释上面的图:
step1:从小到大进行排序。
step2:将大数除以65536,用除得的结果和余数来表示这个大数。
step3::以65535为界进行分块。
为什么是以65535为界限?
程序员的世界里除了1024外,65535也是一个经典值,因为它=2^16-1,正好是用2个字节能表示的最大数,一个 short[] 的存储单位,注意到上图里的最后一行“If a block has more than 4096 values, encode as a bit set, and otherwise as a simple array using 2 bytes per value(如果一个块有超过4096个值,则编码为一个位集,否则编码为一个简单数组,每个值使用2个字节)”。
为什么用4096来区分大块还是小块呢?
4096*2bytes = 8192bytes < 1KB, 磁盘一次寻道可以顺序把一个小块的内容都读出来,再大一位就超过1KB了,需要两次读。
4、联合索引
上面说了半天都是单field索引,如果多个field索引的联合查询,倒排索引如何满足快速查询的要求呢?
利用跳表(Skip list)的数据结构快速做“与”运算,或者利用上面提到的bitset按位“与”
先看看跳表的数据结构:
下面是一个链表的数据结构:
从链表中搜索(32,39,77)需要查找的次数为:2+4+6 =12次,可以得到所有结果,这样做其实没有用到链表的有序性,我们在查询39,77时候其实都做了一些重复查找,势必会造成效率低的问题,这时候可以用Skip List算法来优化查找次数,把某些节点提取出来,将链表分成两级:
样我们在查找39,77的时候次数就得到了简化,因为列表时有序的,所以当我们查到15的时候,23的大概位置就知道了(15—>23),查到31的时候,39的大概位置也就知道了(15—>31—>39),查到72的时候,77的大概位置也就知道了(15—>31—>72—>77)。避免了一部分重复查查找,这时候我们找到所以结果的次数为2+3+4 = 9次,查询77的时候似乎又对39的查找重复了一次,似乎还有优化的空间,那我们再对二级链表再进行一次分级:
样我们在查找77的时候次数就得到了简化,因为列表时有序的,所以当我们查到15的时候,23的大概位置就知道了(15—>23),查到31的时候,39的大概位置也就知道了(15—>31—>39),查到72的时候,77的大概位置也就知道了(15—>72—>77)。避免了一部分重复查查找,这时候我们找到所以结果的次数为2+3+3 = 8次。
从Skip List的查找原理可以看出,它的高效其实是牺牲了一定的空间冗余换来的,所以在有些情况下还是使用bitset更加的直观,比如下面这种数据结构:
level03 | 15 | 72 | |||||
---|---|---|---|---|---|---|---|
level02 | 15 | 31 | 72 | 90 | |||
level01 | 15 | 23 | 31 | 39 | 72 | 77 | 90 |
五、IK 分词器
1、简介
分词:即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作 ,默认的中文分词是将每个字看成一个词,比如“我爱寒江”会被分为"我",“爱”,“寒”,“江” ,这显然是不符合要求的,所以我们需要安装中文分词器ik来解决这个问题。
如果要使用中文,建议使用ik分词器!
IK提供了两个分词算法: ik_ smart和ik_ max _word ,其中ik. smart为最少切分, ik max word为最细粒度划分! -会我们测试!
2、安装
下载
下载地址: https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.6.1
解压
解压到 elasticsearch-7.6.1\plugins \ik下
重启elasticsearch
我们可以发现analysis-ik分词器插件已经被加载了。
3、使用Kibana实现分词
简单测试
开启elasticsearch和Kibana,访问http://localhost:5601/
.IK提供了两个分词算法: ik_ smart和ik_ max _word,我们下面就来试一试这两种方式的区别。
我们可以发现,我们使用ik_smart时,只是进行简单的分词处理,如下:
我们可以发现,当我们使用ik_max_word时,时进行复杂的分词处理,就就是更细致换的分词,如下:
自定义词库
我们还可能遇到这么一种情况,就是当字典中没有我们需要的词时,那么它就不会将我们需要词进行分词处理。
例如:我们的名字是胡学好,但是字典中并没有我的名字,所以默认并不会将我的名字进行分词。
我们需要将我们需要的词加入到elasticsearch的字典中。操作如下:
打开这个文件
编写自己的字典
配置自定义的字典的路径
重启elasticsearch,并重新测试
六、Rest 风格测试
一种软件架构风格 ,而不是标准,只是提供了一-组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
我们的操作都是通过Kibana进行操作的。
1、elasticsearch的Rest操作
创建一个索引为studioustiger,类型名称为type1,id为1的文档
打开 head ,访问http://localhost:9200/
,我们可以发现我们创建的文档已经被head捕捉到了,而且是一条记录的形式被捕捉到的,类似于关系型数据库。
2、ElasticSearch 字段类型
创建一个索引,指定其字段类型。我们在创建索引的时候,只需要指定索引名称即可。(格式固定)
我们可以发现,我们创建的只是单纯的索引,并没有值,但是字段的类型已经被指定好了。
elasticsearch是很智能的,即使我们不指定数据类型,那么elasticsearch会自定比配
3、增(插入)
4、删(删除)
5、改(修改)
6、查(查询)
q=:query表示查询。
name:zhangsan:表示的是精准查询,只有name值完全等于zhangsan时才会被查出。多个条件之间使用&连接
我们可以发现,如果只是匹配zhang,那么是查不出来的。
七、复杂搜索(☆☆☆)
1、组合查询
在mysql中,我们知道我们是可以进行复杂查询的,例如(模糊、排序、分页…),在elasticsearch中也是支持复杂查询的。
要查询的索引如下:
query:表示查询
match_phrase_prefix:表示前缀匹配
match_all:查询所有
match:表示模糊匹配(只有ik分词器可以分出来的词时可以被模糊查询到的)
_source:表示要查询那些字段(和sql中的select xxx, xxxx 效果相同)
sort:表示排序(desc是降序,asc是升序)
from x ,size x:表示分页(索引是从0开始的)
GET /tiger/_search
{
"query": {
"match": {
"name": "胡学好"
}
},
"_source": ["name","age"],
"sort": [
{
"age": {
"order": "asc"
}
}
],
"from": 0,
"size": 3
}
我们可以查询所有信息,那么可以使用"match_all": {}
GET /tiger/_search
{
"query": {
"match_all": {} //表示查询所有信息
}
}
2、条件查询
must_not
查询的结果不能包含 must_not 中任意一个条件,如下:查询的结果中的age不能是42 或 name中不能带03的记录
GET /tiger/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"age": 42
}
},
{
"match": {
"name": "03"
}
}
]
}
}
}
must
查询的结果中必须满足must中的所有条件,如下:查询的结果中的每一条记录中的name必须等于“胡学好”,age必须等于333
GET /tiger/_search
{
"query": {
"bool": {
"must": [ //表示满足如下所有条件的记录被查询出来
{
"match": {
"name": "胡学好"
}
},
{
"match": {
"age": 333
}
}
]
}
}
}
should
查询的结果中的记录只要满足should中的任意一个条件即可,如下:查询的结果中只要满足gender是woman,或age等于18即可
GET /tiger/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"gender": "woman"
}
},
{
"match": {
"age": 18
}
}
]
}
}
}
3、查询结果过滤
对于查询的结果,我们可以只用filter进行过滤
GET /tiger/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"gender": "man"
}
}
],
"filter": { // 过滤查询的结果
"range": { //范围
"age": { // 过滤age的范围
"gt": 14, //大于14
"lt": 100 //小于100
}
}
}
}
}
}
4、term查询
term查询是通过倒排索引进行精确查询的,使用的是默认的分词器(standard)进行分词的,standard会将一段文本分解成一个一个的字,不会出现词组的情。match查询会使用ik分词器(先分析文档,然后通过分析的文档进行查询)
我们可以发现使用term进行查询"胡学好"时,没有查询结果,说明term没有使用ik分词器
GET /tiger/_search
{
"query": {
"term": {
"name": "胡学好"
}
}
}
我们可以发现使用term进行查询"胡"时,有查询结果,说明term使用的是默认的分词器(standard)
GET /tiger/_search
{
"query": {
"term": {
"name": "胡"
}
}
}
5、keyworld 类型
在elasticsearch中分词器会对类型为text的内容进行分词器解析,不会对类型为keyworld的内容进行分词器解析
如下我们创建了一个索引,并将name的类型置为keyword类型,插入了三条数据
PUT studious
{
"mappings": {
"properties": {
"name":{
"type": "keyword"
}
}
}
}
PUT studious/_doc/1
{
"name": "胡学好01"
}
PUT studious/_doc/2
{
"name": "胡学好02"
}
PUT studious/_doc/3
{
"name": "胡学好03"
}
使用match查询"胡学好",没有查询结果,说明keyworld不会被分词器解析
GET studious/_search
{
"query": {
"match": {
"name": "胡学好"
}
}
}
使用match查询"胡学好01",有查询结果,说明我们只能进行精准查询
GET studious/_search
{
"query": {
"match": {
"name": "胡学好01"
}
}
}
对于term查询,我们也可以使用多个值匹配的精确查询,如下
GET studious/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"name": "胡学好01"
}
},
{
"term": {
"name": "胡学好02"
}
}
]
}
}
}
6、高亮查询
在一些电商网址站中,我们搜索一些商品时,对于商品的搜索信息会有一些高亮的显示,如下:
在elasticsearch中给我提供了高亮查询的操作,我们可以在"highlight": {}中自定义高亮设置。我们可以看到,高亮查询的结果中,指定的字段被加上的自定义HTML标签
GET tiger/_search
{
"query": {
"match": {
"name": "胡好学"
}
},
"highlight": {
"pre_tags": "<em style='color:red'>", //自定义前缀
"post_tags": "</em>", //自定义后缀
"fields": {
"name": {} //自定义显示高亮的字段
}
}
}