0.学习目标
- 能够独立安装Elasticsearch服务
- 能够理解Elasticsearch相关概念
- 能够使用Elasticsearch集成IK分词器
- 能够使用RestfulAPI操作Elasticsearch
- 能够搭建elasticsearch集群
1.Elasticsearch介绍和安装
1.1.Elasticsearch简介
1.1.1.什么是Elasticsearch
Elaticsearch简称为es,是一个开源的可扩展的全文检索引擎服务器,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。es使用Java开发并使用Lucene作为其核心来实现索引和搜索的功能,但是它通过简单的RestfulAPI****和javaAPI**来隐藏Lucene的复杂性,从而让全文搜索变得简单。
Elasticsearch官网:https://www.elastic.co/cn/products/elasticsearch
1.1.2.Es企业使用场景
企业使用场景一般分为2种情况:
1)已经上线的系统,某些模块的搜索功能是使用数据库搜索实现的,但是已经出现性能问题或者不满足产品的高亮相关度排序的需求时候,就会对系统的搜索功能进行技术改造,使用全文检索,而es就是首选。针对这种情况企业改造的业务流程如下图:
2)系统新增加的模块,产品一开始就要实现高亮相关度排序等全文检索的功能或者技术分析觉得该模块使用全文检索更适合。针对这种情况企业改造的业务流程如下图:
3)索引库存什么数据
索引库的数据是用来搜索用的,里面存储的数据和数据库一般不会是完全一样的,一般都比数据库的数据少。
那索引库存什么数据呢?
以业务需求为准,需求决定页面要显示什么字段以及会按什么字段进行搜索,那么这些字段就都要保存到索引库中。
1.1.3.版本
目前Elasticsearch最新的版本是6.7.1,我们使用6.2.2版本,建议使用JDK1.8及以上
1.2.Elasticsearch安装和配置
为了模拟真实场景,我们将在linux下安装Elasticsearch。环境要求:JDK8及以上版本
1.2.1.新建一个用户
useradd elastic
设置密码:
passwd elastic
1.2.2.上传安装包并解压
我们将安装包上传到:/home/elastic目录
cd /home/elastic
rz
解压缩:
tar xvf elasticsearch-6.2.2.tar.gz
我们把目录重命名:
mv elasticsearch-6.2.4/ elasticsearch
# 修改权限(可选)
chown elastic:elastic -R elasticsearch
进入,查看目录结构:
ll elasticsearch
- bin 二进制脚本,包含启动命令等
- config 配置文件目录
- lib 依赖包目录
- logs 日志文件目录
- modules 模块库
- plugins 插件目录,这里存放一些常用的插件比如IK分词器插件
- data 数据储存目录 ,可以直接拷贝到其他的节点进行使用(需要手动创建)
出于安全考虑,elasticsearch默认不允许以root账号运行。
切换用户:
su - elastic
1.2.3.修改配置
我们进入config目录:
cd config
ll
需要修改的配置文件有两个:
修改jvm配置
Elasticsearch基于Lucene的,而Lucene底层是java实现,因此我们需要配置jvm参数
vim jvm.options
默认配置如下:
-Xms1g
-Xmx1g
内存占用太多了,我们调小一些,大家虚拟机内存设置为2G:
-Xms512m
-Xmx512m
修改elasticsearch.yml
vim elasticsearch.yml
修改数据和日志目录:
path.data: /home/elastic/elasticsearch/data # 数据目录位置
path.logs: /home/elastic/elasticsearch/logs # 日志目录位置
修改绑定的ip:
network.host: 0.0.0.0 # 绑定到0.0.0.0,允许任何ip来访问
默认只允许本机访问,修改为0.0.0.0后则可以远程访问
目前我们是做的单机安装,如果要做集群,只需要在这个配置文件中添加其它节点信息即可。
elasticsearch.yml的其它可配置信息:
属性名 | 说明 |
---|---|
cluster.name | 配置elasticsearch的集群名称,默认是elasticsearch。建议修改成一个有意义的名称。 |
node.name | 节点名,es会默认随机指定一个名字,建议指定一个有意义的名称,方便管理 |
path.conf | 设置配置文件的存储路径,tar或zip包安装默认在es根目录下的config文件夹,rpm安装默认在/etc/ elasticsearch |
path.data | 设置索引数据的存储路径,默认是es根目录下的data文件夹,可以设置多个存储路径,用逗号隔开 |
path.logs | 设置日志文件的存储路径,默认是es根目录下的logs文件夹 |
path.plugins | 设置插件的存放路径,默认是es根目录下的plugins文件夹 |
bootstrap.memory_lock | 设置为true可以锁住ES使用的内存,避免内存进行swap |
network.host | 设置bind_host和publish_host,设置为0.0.0.0允许外网访问 |
http.port | 设置对外服务的http端口,默认为9200。 |
transport.tcp.port | 集群节点之间通信端口 |
discovery.zen.ping.timeout | 设置ES自动发现节点连接超时的时间,默认为3秒,如果网络延迟高可设置大些 |
discovery.zen.minimum_master_nodes | 主节点数量的最少值 ,此值的公式为:(master_eligible_nodes / 2) + 1 ,比如:有3个符合要求的主借点,那么这里要设置为2 |
1.2.4.创建data和logs目录
刚才我们修改配置,把data和logs目录修改指向了elasticsearch的安装目录。但是这两个目录并不存在,因此我们需要创建出来:
进入Elasticsearch的根目录,然后创建:
mkdir data
mkdir logs //logs目录默认已经存在可以不用创建
1.3.启动运行
进入elasticsearch/bin目录,可以看到下面的执行文件:
然后输入命令:
./elasticsearch
发现报错了,启动失败:
1.3.1.错误1:内核过低
使用centos6,其linux内核版本为2.6。而Elasticsearch的插件要求至少3.5以上版本。不过没关系,我们禁用这个插件即可。
修改elasticsearch.yml文件,在最下面添加以后配置:
bootstrap.system_call_filter: false
然后重启
1.3.2.错误2:
再次启动,又出错了:
[1]: max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536]
问题翻译过来就是:elasticsearch用户拥有的可创建文件描述的权限太低,至少需要65536;
我们用的是elastic用户,而不是root,所以文件权限不足。
首先用root用户登录。
然后修改配置文件:
vim /etc/security/limits.conf
添加下面的内容:
#可打开的文件描述符的最大数(软限制)
* soft nofile 65536
#可打开的文件描述符的最大数(硬限制)
* hard nofile 131072
#单个用户可用的最大进程数量(软限制)
* soft nproc 4096
#单个用户可用的最大进程数量(硬限制)
* hard nproc 4096
1.3.3.错误3
刚才报错中,还有一行:
[1]: max number of threads [1024] for user [leyou] is too low, increase to at least [4096]
这是线程数不够。
继续修改配置:
vim /etc/security/limits.d/90-nproc.conf
修改下面的内容:
* soft nproc 1024
改为:
* soft nproc 4096
1.3.4.错误4
[3]: max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144]
问题翻译过来就是:elasticsearch用户拥有的最大虚拟内存太小,至少需要262144;
继续修改配置文件:
vim /etc/sysctl.conf
添加下面内容:
vm.max_map_count=655360
然后执行命令:
sysctl -p
1.3.6.启动
再次启动,终于成功了!
可以看到绑定了两个端口:
- 9300:集群节点间通讯接口,接收tcp协议
- 9200:客户端访问接口,接收Http协议
验证是否启动成功:
在浏览器中访问:http://192.168.154.129:9200/,如果不能访问,则需要关闭虚拟机防火墙
查看防火墙:firewall-cmd --state
关闭防火墙:systemctl stop firewalld.service
禁止开机启动防火墙:systemctl disable firewalld.service
1.4.图形化可视工具安装
ElasticSearch没有自带图形化界面,我们可以通过安装ElasticSearch的图形化插件,完成图形化界面的效果,完成索引数据的查看。es用的比较多的图形化插件有以下2种:
1)elasticsearch-head
2)kibana
安装的详细步骤,见资料:
1.5.集成ik分词器
Lucene的IK分词器早在2012年已经没有维护了,现在我们要使用的是在其基础上维护升级的版本,并且开发为Elasticsearch的集成插件了,与Elasticsearch一起维护升级,版本也保持一致
https://github.com/medcl/elasticsearch-analysis-ik
1.5.1.安装
1)上传课前资料中的zip包,解压到Elasticsearch目录的plugins目录中:
使用unzip命令解压:
unzip elasticsearch-analysis-ik-6.2.2.zip
得到一个名为elasticsearch的目录:
2)我们将其改名为ik-analyzer
(注意:名字必须是这个!)
mv elasticsearch ik-analyzer
3)然后重启elasticsearch,启动之前一定要把plugins目录下安装包elasticsearch-analysis-ik-6.2.2.zip删除,不然报错:
删除之后,重新启动:
4)添加扩展词典和停用词典
进入到plugins/ik-analyzer/config,将我们昨天lucene的扩展词典和停用词典文件添加进来,然后在IKAnalyzer.cfg.xml配置这2个文件
然后重启:
1.5.2.测试
IK分词器有两种分词模式:ik_max_word和ik_smart模式。
1、ik_max_word (常用)
会将文本做最细粒度的拆分,比如会将“我是中国人”拆分为“我、是、中国人、中国、国人等词语。
2、ik_smart
会做最粗粒度的拆分,比如会将“我是中国人”拆分为“我、是、中国人”三个词
大家先不管语法,我们先测试一波在kibana控制台输入下面的请求:
POST _analyze
{
"analyzer": "ik_max_word",
"text": "我是中国人"
}
运行得到结果:
{
"tokens": [
{
"token": "我",
"start_offset": 0,
"end_offset": 1,
"type": "CN_CHAR",
"position": 0
},
{
"token": "是",
"start_offset": 1,
"end_offset": 2,
"type": "CN_CHAR",
"position": 1
},
{
"token": "中国人",
"start_offset": 2,
"end_offset": 5,
"type": "CN_WORD",
"position": 2
},
{
"token": "中国",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 3
},
{
"token": "国人",
"start_offset": 3,
"end_offset": 5,
"type": "CN_WORD",
"position": 4
}
]
}
1.7.API简介
Elasticsearch提供了Rest风格的HTTP请求API,即http请求接口,而且也提供了各种语言的客户端API
1.7.1.Rest风格API
文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html
1.7.2.客户端API
Elasticsearch支持的客户端非常多:https://www.elastic.co/guide/en/elasticsearch/client/index.html
1.8 ElasticSearch相关概念
Elasticsearch是基于Lucene的全文检索库,本质也是存储数据,很多概念与MySQL类似的。
对比关系:
索引库(indexes)---------------------------------Databases 数据库
类型(type)表----------------------------------Table 数据表
文档(Document)--------------------------Row 行
字段(Field)---------------------Columns 列
映射配置(mappings)--------- 表结构
详细说明:
概念 | 说明 |
---|---|
索引库(indexes) | 索引库包含一堆相关业务,结构相似的文档document数据,比如说建立一个商品product索引库,里面可能就存放了所有的商品数据。 |
类型(type) | type是索引库中的一个逻辑数据分类,一个type下的document,都有相同的field,类似于数据库中的表。比如商品type,里面存放了所有的商品document数据。6.0版本以后一个index只能有1个type,6.0版本以前每个index里可以是一个或多个type。 |
文档(document) | 文档是es中的存入索引库最小数据单元,一个document可以是一条客户数据,一条商品数据,一条订单数据,通常用JSON数据结构表示。document存在索引库下的type类型中。 |
字段(field) | Field是Elasticsearch的最小单位。一个document里面有多个field,每个field就是一个数据字段 |
映射配置(mappings) | 类型对文档结构的约束叫做映射(mapping) ,用来定义document的每个字段的约束。如:字段的数据类型、是否分词、是否索引、是否存储等特性。类型是模拟mysql中的table概念。表是有结构的,也就是表中每个字段都有约束信息; |
2.索引库操作
2.1.创建索引库
Elasticsearch采用Rest风格API,因此其API就是一次http请求,你可以用任何工具发起http请求
语法:
PUT /索引库名
请求参数:json格式:
{
"settings": {
"属性名": "属性值"
}
}
settings:就是索引库设置,其中可以定义索引库的各种属性,目前我们可以不设置,都走默认
可以看到索引创建成功了。
2.2.查看索引库
语法
Get请求可以帮我们查看索引信息,格式:
GET /索引库名
GET /_all 查看所有索引库
2.3.删除索引库
删除索引使用DELETE请求
语法
DELETE /索引库名
示例
再次查看,返回索引不存在
3.类型及映射操作
有了索引库
,等于有了数据库中的database
。接下来就需要索引库中的类型
了,也就是数据库中的表
。创建数据库表需要设置字段约束,索引库也一样,在创建索引库的类型时,需要知道这个类型下有哪些字段,每个字段有哪些约束信息,这就叫做字段映射(mapping)
字段的约束我们在学习Lucene中我们都见到过,包括但不限于:
- 字段的数据类型
- 是否要存储
- 是否要索引
- 是否分词
- 分词器是什么
我们一起来看下创建的语法。
3.1.创建映射字段
语法
请求方式依然是PUT
PUT /索引库名/_mapping/类型名称 或 PUT /索引库名/类型名称/_mapping
{
"properties": {
"字段名": {
"type": "类型",
"index": true,
"store": true,
"analyzer": "分词器"
}
}
}
- 类型名称:就是前面将的type的概念,类似于数据库中的表
字段名:任意填写,下面指定许多属性,例如:- type:类型,可以是text、long、short、date、integer、object等
- index:是否索引,默认为true
- store:是否存储,默认为false
- analyzer:分词器,这里的
ik_max_word
即使用ik分词器
示例
发起请求:
PUT heima/_mapping/goods
{
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"subtitle": {
"type": "text",
"analyzer": "ik_max_word"
},
"images": {
"type": "keyword",
"index": "false"
},
"price": {
"type": "float"
}
}
}
响应结果:
{
"acknowledged": true
}
上述案例中,就给heima这个索引库添加了一个名为goods
的类型,并且在类型中设置了3个字段:
- title:商品标题
- subtitle: 商品子标题
- images:商品图片
- price:商品价格
并且给这些字段设置了一些属性,至于这些属性对应的含义,我们在后续会详细介绍。
3.2.映射属性详解
1)type
Elasticsearch中支持的数据类型非常丰富:
我们说几个关键的:
-
String类型,又分两种:
- text:可分词,不可参与聚合
- keyword:不可分词,数据会作为完整字段进行匹配,可以参与聚合
-
Numerical:数值类型,分两类
- 基本数据类型:long、interger、short、byte、double、float、half_float
- 浮点数的高精度类型:scaled_float
- 需要指定一个精度因子,比如10或100。elasticsearch会把真实值乘以这个因子后存储,取出时再还原。
-
Date:日期类型
elasticsearch可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为long,节省空间。
-
Array:数组类型
- 进行匹配时,任意一个元素满足,都认为满足
- 排序时,如果升序则用数组中的最小值来排序,如果降序则用数组中的最大值来排序
-
Object:对象
{
name:"Jack",
age:21,
girl:{
name: "Rose", age:21
}
}
如果存储到索引库的是对象类型,例如上面的girl,会把girl编程两个字段:girl.name和girl.age
2)index
index影响字段的索引情况。
- true:字段会被索引,则可以用来进行搜索。默认值就是true
- false:字段不会被索引,不能用来搜索
index的默认值就是true,也就是说你不进行任何配置,所有字段都会被索引。
但是有些字段是我们不希望被索引的,比如商品的图片信息,就需要手动设置index为false。
3)store
是否将数据进行额外存储。
在学习lucene时,我们知道如果一个字段的store设置为false,那么在文档列表中就不会有这个字段的值,用户的搜索结果中不会显示出来。但是在Elasticsearch中,即便store设置为false,也可以搜索到结果。
原因是Elasticsearch在创建文档索引时,会将文档中的原始数据备份,保存到一个叫做_source
的属性中。而且我们可以通过过滤_source
来选择哪些要显示,哪些不显示。
而如果设置store为true,就会在_source
以外额外存储一份数据,多余,因此一般我们都会将store设置为false,事实上,store的默认值就是true。
3.3.查看映射关系
语法:
GET /索引库名/_mapping
查看某个索引库中的所有类型的映射。如果要查看某个类型映射,可以再路径后面跟上类型名称。即:
GET /索引库名/_mapping/类型名
示例:
GET /heima/_mapping
响应:
{
"heima": {
"mappings": {
"goods": {
"properties": {
"images": {
"type": "keyword",
"index": false
},
"price": {
"type": "float"
},
"title": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
}
3.4.一次创建索引库和类型(常用)
刚才 的案例中我们是把创建索引库和类型分开来做,其实也可以在创建索引库的同时,直接制定索引库中的类型,基本语法:
put /索引库名
{
"settings":{
"索引库属性名":"索引库属性值"
},
"mappings":{
"类型名":{
"properties":{
"字段名":{
"映射属性名":"映射属性值"
}
}
}
}
}
来试一下吧:
PUT /heima2
{
"settings": {},
"mappings": {
"goods": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
结果:
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "heima2"
}
4.文档操作
文档,即索引库中某个类型下的数据,会根据规则创建索引,将来用来搜索。可以类比做数据库中的每一行数据。
4.1.新增文档
4.1.1.新增并随机生成id
通过POST请求,可以向一个已经存在的索引库中添加文档数据。
语法:
POST /索引库名/类型名
{
"key":"value"
}
示例:
POST /heima/goods/
{
"title":"小米手机",
"images":"http://image.leyou.com/12479122.jpg",
"price":2699.00
}
响应:
{
"_index": "heima",
"_type": "goods",
"_id": "r9c1KGMBIhaxtY5rlRKv",
"_version": 1,
"result": "created",
"_shards": {
"total": 3,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 2
}
可以看到结果显示为:created
,应该是创建成功了。
另外,需要注意的是,在响应结果中有个_id
字段,这个就是这条文档数据的唯一标示
,以后的增删改查都依赖这个id作为唯一标示。
可以看到id的值为:r9c1KGMBIhaxtY5rlRKv
,这里我们新增时没有指定id,所以是ES帮我们随机生成的id。
4.2.查看文档
根据rest风格,新增是post,查询应该是get,不过查询一般都需要条件,这里我们把刚刚生成数据的id带上。
GET /heima/goods/r9c1KGMBIhaxtY5rlRKv
查看结果:
{
"_index": "heima",
"_type": "goods",
"_id": "r9c1KGMBIhaxtY5rlRKv",
"_version": 1,
"found": true,
"_source": {
"title": "小米手机",
"images": "http://image.leyou.com/12479122.jpg",
"price": 2699
}
}
_source
:源文档信息,所有的数据都在里面。_id
:这条文档的唯一标示
4.3.新增文档并自定义id
如果我们想要自己新增的时候指定id,可以这么做:
POST /索引库名/类型/id值
{
...
}
示例:
POST /heima/goods/2
{
"title":"大米手机",
"images":"http://image.leyou.com/12479122.jpg",
"price":2899.00
}
得到的数据:
{
"_index": "heima",
"_type": "goods",
"_id": "2",
"_score": 1,
"_source": {
"title": "大米手机",
"images": "http://image.leyou.com/12479122.jpg",
"price": 2899
}
}
4.4.修改数据
把刚才新增的请求方式改为PUT,就是修改了。不过修改必须指定id,
- id对应文档存在,则修改
- id对应文档不存在,则新增
比如,我们把使用id为3,不存在,则应该是新增:
PUT /heima/goods/3
{
"title":"超米手机",
"images":"http://image.leyou.com/12479122.jpg",
"price":3899.00
}
结果:
{
"_index": "heima",
"_type": "goods",
"_id": "3",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
可以看到是created
,是新增。
我们再次执行刚才的请求,不过把数据改一下:
PUT /heima/goods/3
{
"title":"超大米手机",
"images":"http://image.leyou.com/12479122.jpg",
"price":3299.00
}
查看结果:
{
"_index": "heima",
"_type": "goods",
"_id": "3",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}
可以看到结果是:updated
,显然是更新数据
4.5.删除数据
1)根据id进行删除:
语法
DELETE /索引库名/类型名/id值
5.查询
我们从4块来讲查询:
- 基本查询
_source
过滤- 结果过滤
- 高级查询
- 排序
5.1.基本查询
基本语法
Post /索引库名/_search
{
"query":{
"查询类型":{
"查询条件":"查询条件值"
}
}
}
这里的query代表一个查询对象,里面可以有不同的查询属性
- 查询类型:
- 例如:
match_all
,match
,term
,range
等等
- 例如:
- 查询条件:查询条件会根据类型的不同,写法也有差异,后面详细讲解
5.1.1 查询所有(match_all)
示例:
GET /heima/_search
{
"query":{
"match_all": {}
}
}
query
:代表查询对象match_all
:代表查询所有
结果:
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "heima",
"_type": "goods",
"_id": "2",
"_score": 1,
"_source": {
"title": "大米手机",
"images": "http://image.leyou.com/12479122.jpg",
"price": 2899
}
},
{
"_index": "heima",
"_type": "goods",
"_id": "r9c1KGMBIhaxtY5rlRKv",
"_score": 1,
"_source": {
"title": "小米手机",
"images": "http://image.leyou.com/12479122.jpg",
"price": 2699
}
}
]
}
}
- took:查询花费时间,单位是毫秒
- time_out:是否超时
- _shards:分片信息
- hits:搜索结果总览对象
- total:搜索到的总条数
- max_score:所有结果中文档得分的最高分
- hits:搜索结果的文档对象数组,每个元素是一条搜索到的文档信息
- _index:索引库
- _type:文档类型
- _id:文档id
- _score:文档得分
- _source:文档的源数据
5.1.2 匹配查询(match)
我们先加入一条数据,便于测试:
PUT /heima/goods/3
{
"title":"小米电视4A",
"images":"http://image.leyou.com/12479122.jpg",
"price":3899.00
}
现在,索引库中有2部手机,1台电视:
- or关系
match
类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是or的关系
GET /heima/_search
{
"query":{
"match":{
"title":"小米电视"
}
}
}
结果:
在上面的案例中,不仅会查询到电视,而且与小米相关的都会查询到,多个词之间是or
的关系。
- and关系
某些情况下,我们需要更精确查找,我们希望这个关系变成and
,可以这样做:
GET /goods/_search
{
"query":{
"match":{
"title":{"query":"小米电视","operator":"and"}
}
}
}
结果:
本例中,只有同时包含小米
和电视
的词条才会被搜索到。
5.1.3 多字段查询(multi_match)
multi_match
与match
类似,不同的是它可以在多个字段中查询
POST /heima/_search
{
"query":{
"multi_match": {
"query": "小米",
"fields": [ "title", "subTitle" ]
}
}
}
本例中,我们会假设在title字段和subtitle字段中查询小米
这个词
5.1.4 词条匹配(term)
term
查询被用于精确值 匹配,这些精确值可能是数字、时间、布尔或者那些未分词的字符串
GET /heima/_search
{
"query":{
"term":{
"price":2699.00
}
}
}
结果:
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "heima",
"_type": "goods",
"_id": "r9c1KGMBIhaxtY5rlRKv",
"_score": 1,
"_source": {
"title": "小米手机",
"images": "http://image.leyou.com/12479122.jpg",
"price": 2699
}
}
]
}
}
5.1.5 多词条精确匹配(terms)
terms
查询和 term 查询一样,但它允许你指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件,类似于mysql的in:
GET /heima/_search
{
"query":{
"terms":{
"price":[2699.00,2899.00,3899.00]
}
}
}
结果:
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1,
"hits": [
{
"_index": "heima",
"_type": "goods",
"_id": "2",
"_score": 1,
"_source": {
"title": "大米手机",
"images": "http://image.leyou.com/12479122.jpg",
"price": 2899
}
},
{
"_index": "heima",
"_type": "goods",
"_id": "r9c1KGMBIhaxtY5rlRKv",
"_score": 1,
"_source": {
"title": "小米手机",
"images": "http://image.leyou.com/12479122.jpg",
"price": 2699
}
},
{
"_index": "heima",
"_type": "goods",
"_id": "3",
"_score": 1,
"_source": {
"title": "小米电视4A",
"images": "http://image.leyou.com/12479122.jpg",
"price": 3899
}
}
]
}
}
5.2.结果过滤
默认情况下,elasticsearch在搜索的结果中,会把文档中保存在_source
的所有字段都返回。
如果我们只想获取其中的部分字段,我们可以添加_source
的过滤
5.2.1.直接指定字段
示例:
POST /heima/_search
{
"_source": ["title","price"],
"query": {
"term": {
"price": 2699
}
}
}
返回的结果:
{
"took": 12,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "heima",
"_type": "goods",
"_id": "r9c1KGMBIhaxtY5rlRKv",
"_score": 1,
"_source": {
"price": 2699,
"title": "小米手机"
}
}
]
}
}
5.2.2.指定includes和excludes
我们也可以通过:
- includes:来指定想要显示的字段
- excludes:来指定不想要显示的字段
二者都是可选的。
示例:
GET /heima/_search
{
"_source": {
"includes":["title","price"]
},
"query": {
"term": {
"price": 2699
}
}
}
与下面的结果将是一样的:
GET /heima/_search
{
"_source": {
"excludes": ["images"]
},
"query": {
"term": {
"price": 2699
}
}
}
5.3 高级查询
5.3.1 布尔组合(bool)
bool
把各种其它查询通过must
(与)、must_not
(非)、should
(或)的方式进行组合
GET /heima/_search
{
"query":{
"bool":{
"must": { "match": { "title": "大米" }},
"must_not": { "match": { "title": "电视" }},
"should": { "match": { "title": "手机" }}
}
}
}
结果:
{
"took": 10,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.5753642,
"hits": [
{
"_index": "heima",
"_type": "goods",
"_id": "2",
"_score": 0.5753642,
"_source": {
"title": "大米手机",
"images": "http://image.leyou.com/12479122.jpg",
"price": 2899
}
}
]
}
}
5.3.2 范围查询(range)
range
查询找出那些落在指定区间内的数字或者时间
Post /heima/_search
{
"query":{
"range": {
"price": {
"gte": 1000.0,
"lt": 2800.00
}
}
}
}
range
查询允许以下字符:
操作符 | 说明 |
---|---|
gt | 大于 |
gte | 大于等于 |
lt | 小于 |
lte | 小于等于 |
5.3.3 模糊查询(fuzzy)(了解)
我们新增一个商品:
POST /heima/goods/4
{
"title":"apple手机",
"images":"http://image.leyou.com/12479122.jpg",
"price":6899.00
}
fuzzy自动将拼写错误的搜索文本,进行纠正,纠正以后去尝试匹配索引中的数据
它允许用户搜索词条与实际词条出现偏差,但是偏差的编辑距离不得超过2:
GET /heima/_search
{
"query": {
"fuzzy": {
"title": "appla"
}
}
}
上面的查询,也能查询到apple手机
fuzziness,你的搜索文本最多可以纠正几个字母去跟你的数据进行匹配,默认如果不设置,就是2我们可以通过
GET /heima/_search
{
"query": {
"fuzzy": {
"title": {
"value":"appla",
"fuzziness":1
}
}
}
}
5.4 过滤(filter)
条件查询中进行过滤
所有的查询都会影响到文档的评分及排名。如果我们需要在查询结果中进行过滤,并且不希望过滤条件影响评分,那么就不要把过滤条件作为查询条件来用。而是使用filter
方式:
POST /heima/_search
{
"query":{
"bool":{
"must":{ "match": { "title": "小米手机" }},
"filter":{
"range":{"price":{"gt":2000.00,"lt":3800.00}}
}
}
}
}
5.5 排序
5.5.1 单字段排序
sort
可以让我们按照不同的字段进行排序,并且通过order
指定排序的方式
POST /heima/_search
{
"query": {
"match": {
"title": "小米手机"
}
},
"sort": [
{
"price": {
"order": "desc"
}
}
]
}
5.5.2 多字段排序
假定我们想要结合使用 price和 _score(得分) 进行查询,并且匹配的结果首先按照价格排序,然后按照相关性得分排序:
GET /goods/_search
{
"query":{
"bool":{
"must":{ "match": { "title": "小米手机" }},
"filter":{
"range":{"price":{"gt":200000,"lt":300000}}
}
}
},
"sort": [
{ "price": { "order": "desc" }},
{ "_score": { "order": "desc" }}
]
}
5.6.高亮
elasticsearch中实现高亮的语法比较简单:
POST /heima/_search
{
"query": {
"match": {
"title": "手机"
}
},
"highlight": {
"pre_tags": "<em>",
"post_tags": "</em>",
"fields": {
"title": {}
}
}
}
在使用match查询的同时,加上一个highlight属性:
- pre_tags:前置标签
- post_tags:后置标签
- fields:需要高亮的字段
- title:这里声明title字段需要高亮,后面可以为这个字段设置特有配置,也可以空
结果:
{
"took": 8,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 0.2876821,
"hits": [
{
"_index": "heima",
"_type": "goods",
"_id": "2",
"_score": 0.2876821,
"_source": {
"title": "大米手机",
"images": "http://image.leyou.com/12479122.jpg",
"price": 2899
},
"highlight": {
"title": [
"大米<em>手机</em>"
]
}
},
{
"_index": "heima",
"_type": "goods",
"_id": "JP6xa2kBtq36Pzvxpjaf",
"_score": 0.19856805,
"_source": {
"title": "小米手机",
"images": "http://image.leyou.com/12479122.jpg",
"price": 2699
},
"highlight": {
"title": [
"小米<em>手机</em>"
]
}
},
{
"_index": "heima",
"_type": "goods",
"_id": "3",
"_score": 0.16853254,
"_source": {
"title": "超大米手机",
"images": "http://image.leyou.com/12479122.jpg",
"price": 3299,
"stock": 200,
"saleable": true,
"subTitle": "哈哈"
},
"highlight": {
"title": [
"超大米<em>手机</em>"
]
}
}
]
}
}
5.7.分页
elasticsearch中实现分页的语法非常简单:
POST /heima/_search
{
"query": {
"match_all": {}
},
"size": 2,
"from": 0
}
size:每页显示多少条
from:当前页从第几条开始, int start = (page - 1) * size;
6. 聚合aggregations
聚合可以让我们极其方便的实现对数据的统计、分析。例如:
- 什么品牌的手机最受欢迎?
- 这些手机的平均价格、最高价格、最低价格?
- 这些手机每月的销售情况如何?
实现这些统计功能的比数据库的sql要方便的多,而且查询速度非常快,可以实现近实时搜索效果。
6.1 基本概念
Elasticsearch中的聚合,包含多种类型,最常用的两种,一个叫桶
,一个叫度量
:
桶(bucket)
桶的作用,是按照某种方式对数据进行分组,每一组数据在ES中称为一个桶
,例如我们根据国籍对人划分,可以得到中国桶
、英国桶
,日本桶
……或者我们按照年龄段对人进行划分:010,1020,2030,3040等。
Elasticsearch中提供的划分桶的方式有很多:
- Date Histogram Aggregation:根据日期阶梯分组,例如给定阶梯为周,会自动每周分为一组
- Histogram Aggregation:根据数值阶梯分组,与日期类似,需要知道分组的间隔(interval)
- Terms Aggregation:根据词条内容分组,词条内容完全匹配的为一组
- Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组
- ……
综上所述,我们发现bucket aggregations 只负责对数据进行分组,并不进行计算,因此往往bucket中往往会嵌套另一种聚合:metrics aggregations即度量
度量(metrics)
分组完成以后,我们一般会对组中的数据进行聚合运算,例如求平均值、最大、最小、求和等,这些在ES中称为度量
比较常用的一些度量聚合方式:
- Avg Aggregation:求平均值
- Max Aggregation:求最大值
- Min Aggregation:求最小值
- Percentiles Aggregation:求百分比
- Stats Aggregation:同时返回avg、max、min、sum、count等
- Sum Aggregation:求和
- Top hits Aggregation:求前几
- Value Count Aggregation:求总数
- ……
为了测试聚合,我们先批量导入一些数据
创建索引:
PUT /car
{
"mappings": {
"orders": {
"properties": {
"color": {
"type": "keyword"
},
"make": {
"type": "keyword"
}
}
}
}
}
注意:在ES中,需要进行聚合、排序、过滤的字段其处理方式比较特殊,因此不能被分词,必须使用keyword
或数值类型
。这里我们将color和make这两个文字类型的字段设置为keyword类型,这个类型不会被分词,将来就可以参与聚合
导入数据,这里是采用批处理的API,大家直接复制到kibana运行即可:
POST /car/orders/_bulk
{ "index": {}}
{ "price" : 10000, "color" : "红", "make" : "本田", "sold" : "2014-10-28" }
{ "index": {}}
{ "price" : 20000, "color" : "红", "make" : "本田", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 30000, "color" : "绿", "make" : "福特", "sold" : "2014-05-18" }
{ "index": {}}
{ "price" : 15000, "color" : "蓝", "make" : "丰田", "sold" : "2014-07-02" }
{ "index": {}}
{ "price" : 12000, "color" : "绿", "make" : "丰田", "sold" : "2014-08-19" }
{ "index": {}}
{ "price" : 20000, "color" : "红", "make" : "本田", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 80000, "color" : "红", "make" : "宝马", "sold" : "2014-01-01" }
{ "index": {}}
{ "price" : 25000, "color" : "蓝", "make" : "福特", "sold" : "2014-02-12" }
6.2 聚合为桶
首先,我们按照 汽车的颜色color来
划分桶
,按照颜色分桶,最好是使用TermAggregation类型,按照颜色的名称来分桶。
Post /car/_search
{
"size" : 0,
"aggs" : {
"popular_colors" : {
"terms" : {
"field" : "color"
}
}
}
}
- size: 查询条数,这里设置为0,因为我们不关心搜索到的数据,只关心聚合结果,提高效率
- aggs:声明这是一个聚合查询,是aggregations的缩写
- popular_colors:给这次聚合起一个名字,可任意指定。
- terms:聚合的类型,这里选择terms,是根据词条内容(这里是颜色)划分
- field:划分桶时依赖的字段
- terms:聚合的类型,这里选择terms,是根据词条内容(这里是颜色)划分
- popular_colors:给这次聚合起一个名字,可任意指定。
结果:
{
"took": 33,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 8,
"max_score": 0,
"hits": []
},
"aggregations": {
"popular_colors": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "红",
"doc_count": 4
},
{
"key": "绿",
"doc_count": 2
},
{
"key": "蓝",
"doc_count": 2
}
]
}
}
}
- hits:查询结果为空,因为我们设置了size为0
- aggregations:聚合的结果
- popular_colors:我们定义的聚合名称
- buckets:查找到的桶,每个不同的color字段值都会形成一个桶
- key:这个桶对应的color字段的值
- doc_count:这个桶中的文档数量
通过聚合的结果我们发现,目前红色的小车比较畅销!
6.3 桶内度量
前面的例子告诉我们每个桶里面的文档数量,这很有用。 但通常,我们的应用需要提供更复杂的文档度量。 例如,每种颜色汽车的平均价格是多少?
因此,我们需要告诉Elasticsearch使用哪个字段
,使用何种度量方式
进行运算,这些信息要嵌套在桶
内,度量
的运算会基于桶
内的文档进行
现在,我们为刚刚的聚合结果添加 求价格平均值的度量:
GET /car/_search
{
"size" : 0,
"aggs" : {
"popular_colors" : {
"terms" : {
"field" : "color"
},
"aggs":{
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
- aggs:我们在上一个aggs(popular_colors)中添加新的aggs。可见度量也是一个聚合
- avg_price:聚合的名称
- avg:度量的类型,这里是求平均值
- field:度量运算的字段
结果:
{
"took": 23,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 8,
"max_score": 0,
"hits": []
},
"aggregations": {
"popular_colors": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "红",
"doc_count": 4,
"avg_price": {
"value": 32500
}
},
{
"key": "绿",
"doc_count": 2,
"avg_price": {
"value": 21000
}
},
{
"key": "蓝",
"doc_count": 2,
"avg_price": {
"value": 20000
}
}
]
}
}
}
可以看到每个桶中都有自己的avg_price
字段,这是度量聚合的结果
6.4 桶内嵌套桶
刚刚的案例中,我们在桶内嵌套度量运算。事实上桶不仅可以嵌套运算, 还可以再嵌套其它桶。也就是说在每个分组中,再分更多组。
比如:我们想统计每种颜色的汽车中,分别属于哪个制造商,按照make
字段再进行分桶
Post /car/_search
{
"size" : 0,
"aggs" : {
"popular_colors" : {
"terms" : {
"field" : "color"
},
"aggs":{
"avg_price": {
"avg": {
"field": "price"
}
},
"maker":{
"terms":{
"field":"make"
}
}
}
}
}
}
- 原来的color桶和avg计算我们不变
- maker:在嵌套的aggs下新添一个桶,叫做maker
- terms:桶的划分类型依然是词条
- filed:这里根据make字段进行划分
部分结果:
{
"took": 16,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 8,
"max_score": 0,
"hits": []
},
"aggregations": {
"popular_colors": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "红",
"doc_count": 4,
"maker": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "本田",
"doc_count": 3
},
{
"key": "宝马",
"doc_count": 1
}
]
},
"avg_price": {
"value": 32500
}
},
{
"key": "绿",
"doc_count": 2,
"maker": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "丰田",
"doc_count": 1
},
{
"key": "福特",
"doc_count": 1
}
]
},
"avg_price": {
"value": 21000
}
},
{
"key": "蓝",
"doc_count": 2,
"maker": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "丰田",
"doc_count": 1
},
{
"key": "福特",
"doc_count": 1
}
]
},
"avg_price": {
"value": 20000
}
}
]
}
}
}
- 我们可以看到,新的聚合
maker
被嵌套在原来每一个color
的桶中。 - 每个颜色下面都根据
make
字段进行了分组 - 我们能读取到的信息:
- 红色车共有4辆
- 红色车的平均售价是 $32,500 美元。
- 其中3辆是 Honda 本田制造,1辆是 BMW 宝马制造。
7.Elasticsearch集群
在之前的课程中,我们都是使用单点的elasticsearch,接下来我们会学习如何搭建Elasticsearch的集群。
7.1.单点的问题
单台服务器,往往都有最大的负载能力,超过这个阈值,服务器性能就会大大降低甚至不可用。单点的elasticsearch也是一样,那单点的es服务器存在哪些可能出现的问题呢?
- 单台机器存储容量有限
- 单服务器容易出现单点故障,无法实现高可用
- 单服务的并发处理能力有限
所以,为了应对这些问题,我们需要对elasticsearch搭建集群
7.2.1.数据分片
首先,我们面临的第一个问题就是数据量太大,单点存储量有限的问题。
大家觉得应该如何解决?
没错,我们可以把数据拆分成多份,每一份存储到不同机器节点(node),从而实现减少每个节点数据量
7.2.2.数据备份
数据分片解决了海量数据存储的问题,但是如果出现单点故障,那么分片数据就不再完整,这又该如何解决呢?
没错,就像大家为了备份手机数据,会额外存储一份到移动硬盘一样。我们可以给每个分片数据进行备份,存储到其它节点,防止数据丢失,这就是数据备份,也叫数据副本(replica)
。
数据备份可以保证高可用,但是每个分片备份一份,所需要的节点数量就会翻一倍,成本实在是太高了!
为了在高可用和成本间寻求平衡,我们可以这样做:
- 首先对数据分片,存储到不同节点
- 然后对每个分片进行备份,放到对方节点,完成互相备份
这样可以大大减少所需要的服务节点数量,如图,我们以3分片,每个分片备份一份为例:
在这个集群中,如果出现单节点故障,并不会导致数据缺失,所以保证了集群的高可用,同时也减少了节点中数据存储量。并且因为是多个节点存储数据,因此用户请求也会分发到不同服务器,并发能力也得到了一定的提升。
7.3.搭建集群
集群需要多台机器,我们这里用一台机器来模拟,因此我们需要在一台虚拟机中部署多个elasticsearch节点,每个elasticsearch的端口都必须不一样。
我们计划集群名称为:heima-elastic,部署3个elasticsearch节点,分别是:
- node-01:http端口9201,TCP端口9301
- node-02:http端口9202,TCP端口9302
- node-03:http端口9203,TCP端口9303
接下来的所有操作,记得要使用elastic
用户来操作!
另外,建议先对当前虚拟机进行快照
,以后好恢复成单点结构。
7.3.1.清空elasticsearch中的数据
首先把已经启动的elasticsearch关闭,然后通过命令把之前写入的数据都删除。
rm -rf /elasticsearch/data
7.3.2.修改elasticsearch配置
进入/home/elastic/elasticsearch/config
目录,修改elasticsearch.yml
文件
vim elasticsearch.yml
内容修改成这样:
# ======================== Elasticsearch Configuration =========================
# 集群名称,三台服务器保持一致
cluster.name: heima-elastic
#
# 节点名称,每个节点唯一标识
node.name: node-01
#
# 数据目录
path.data: /home/elastic/elasticsearch-01/data
#
# 日志目录
path.logs: /home/elastic/elasticsearch-01/logs
#
# 绑定的ip,0.0.0.0,代表任意ip地址均可访问
network.host: 0.0.0.0
#
# http协议的对外端口
http.port: 9201
#
# TCP协议对外端口,一般是集群内部通讯使用
transport.tcp.port: 9301
#
# 集群中其它节点的ip及端口
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301", "127.0.0.1:9302","127.0.0.1:9303"]
#
# 选举主节点时需要看到最少多少个具有master资格的活节点,才能进行选举。官方的推荐值是(N/2)+1
discovery.zen.minimum_master_nodes: 2
#
# 解决内核版本太低问题
bootstrap.system_call_filter: false
#解决head插跨域访问问题
http.cors.enabled: true
http.cors.allow-origin: "*"
7.3.3.复制elasticsearch节点
回到 /home/elastic 目录,将
elasticsearch目录修改为
ealsticsearch-01`:
mv elasticsearch elasticsearch-01
然后输入下面命令,拷贝两份elasticsearch实例:
cp elasticsearch-01 elasticsearch-02 -R
cp elasticsearch-01 elasticsearch-03 -R
进入elasticsearch-02/config目录,修改elasticsearch.yml中的下列配置:
# 节点名称
node.name: node-02
#
# 数据目录
path.data: /home/elastic/elasticsearch-02/data
#
# 日志目录
path.logs: /home/elastic/elasticsearch-02/logs
#
# http协议的对外端口
http.port: 9202
#
# TCP协议对外端口
transport.tcp.port: 9302
注意到,注意是把01
改成了02
,但不是所有,切勿自动全局替换!!!
同理,进入elasticsearch-3/config/,修改elasticsearch.yml文件,与上面类似,不过修改成03
.
7.3.4.启动并测试
分别启动3台elasticsearch,可以用后台启动方式(要使用elastic
用户来操作):
./elasticsearch-01/bin/elasticsearch -d
./elasticsearch-02/bin/elasticsearch -d
./elasticsearch-03/bin/elasticsearch -d
通过启动运行chrome的 elasticsearch-head插件,可以查看到节点信息:
7.3.5 启动错误
启动错误1:
原因是:是因为复制的elasticsearch文件夹下包含了data文件中示例一的节点数据,需要把示例二data文件下的文件清空。删除es集群data数据库文件夹下所有文件即可
启动错误2:
于elasticsearch6.2默认分配jvm空间大小为1g,虚拟机内存不够大,修改jvm空间分配512m,256m
7.3.6 集群健康
可以通过elasticsearch-head插件查看集群健康状态,有以下三个状态:
green
所有的主分片和副本分片都已分配。你的集群是 100% 可用的。
yellow
所有的主分片已经分片了,但至少还有一个副本是缺失的。不会有数据丢失,所以搜索结果依然是完整的。不过,你的高可用性在某种程度上被弱化。如果 更多的 分片消失,你就会丢数据了。把 yellow
想象成一个需要及时调查的警告。
red
至少一个主分片(以及它的全部副本)都在缺失中。这意味着你在缺少数据:搜索只能返回部分数据,而分配到这个分片上的写入请求会返回一个异常。
7.4.测试集群中创建索引库
搭建集群以后就要创建索引库了,那么问题来了,当我们创建一个索引库后,数据会保存到哪个服务节点上呢?如果我们对索引库分片,那么每个片会在哪个节点呢?
这个要亲自尝试才知道。
还记得创建索引库的API吗?
-
请求方式:PUT
-
请求路径:/索引库名
-
请求参数:json格式:
{ "settings": { "属性名": "属性值" } }
settings:就是索引库设置,其中可以定义索引库的各种属性,目前我们可以不设置,都走默认。
这里给搭建看看集群中分片和备份的设置方式,示例:
PUT /heima
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
这里有两个配置:
- number_of_shards:分片数量,这里设置为3
- number_of_replicas:副本数量,这里设置为1,每个分片一个备份,一个原始数据,共2份。
通过chrome浏览器的head查看,我们可以查看到分片的存储结构:
可以看到,heima这个索引库,有三个分片,分别是0、1、2,每个分片有1个副本,共6份。
- node-01上保存了0号分片和1号分片的副本
- node-02上保存了1号分片和2号分片的副本
- node-03上保存了0号分片和2号分片的副本
mv elasticsearch elasticsearch-01
然后输入下面命令,拷贝两份elasticsearch实例:
cp elasticsearch-01 elasticsearch-02 -R
cp elasticsearch-01 elasticsearch-03 -R
进入elasticsearch-02/config目录,修改elasticsearch.yml中的下列配置:
# 节点名称
node.name: node-02
#
# 数据目录
path.data: /home/elastic/elasticsearch-02/data
#
# 日志目录
path.logs: /home/elastic/elasticsearch-02/logs
#
# http协议的对外端口
http.port: 9202
#
# TCP协议对外端口
transport.tcp.port: 9302
注意到,注意是把01
改成了02
,但不是所有,切勿自动全局替换!!!
同理,进入elasticsearch-3/config/,修改elasticsearch.yml文件,与上面类似,不过修改成03
.
7.3.4.启动并测试
分别启动3台elasticsearch,可以用后台启动方式(要使用elastic
用户来操作):
./elasticsearch-01/bin/elasticsearch -d
./elasticsearch-02/bin/elasticsearch -d
./elasticsearch-03/bin/elasticsearch -d
通过启动运行chrome的 elasticsearch-head插件,可以查看到节点信息:
7.3.5 启动错误
启动错误1:
[外链图片转存中…(img-wLWAqQli-1567175371498)]
原因是:是因为复制的elasticsearch文件夹下包含了data文件中示例一的节点数据,需要把示例二data文件下的文件清空。删除es集群data数据库文件夹下所有文件即可
启动错误2:
[外链图片转存中…(img-htJ46Bvg-1567175371500)]
[外链图片转存中…(img-M0ZtTuFO-1567175371500)]
于elasticsearch6.2默认分配jvm空间大小为1g,虚拟机内存不够大,修改jvm空间分配512m,256m
7.3.6 集群健康
可以通过elasticsearch-head插件查看集群健康状态,有以下三个状态:
[外链图片转存中…(img-MCta6167-1567175371501)]
green
所有的主分片和副本分片都已分配。你的集群是 100% 可用的。
yellow
所有的主分片已经分片了,但至少还有一个副本是缺失的。不会有数据丢失,所以搜索结果依然是完整的。不过,你的高可用性在某种程度上被弱化。如果 更多的 分片消失,你就会丢数据了。把 yellow
想象成一个需要及时调查的警告。
red
至少一个主分片(以及它的全部副本)都在缺失中。这意味着你在缺少数据:搜索只能返回部分数据,而分配到这个分片上的写入请求会返回一个异常。
7.4.测试集群中创建索引库
搭建集群以后就要创建索引库了,那么问题来了,当我们创建一个索引库后,数据会保存到哪个服务节点上呢?如果我们对索引库分片,那么每个片会在哪个节点呢?
这个要亲自尝试才知道。
还记得创建索引库的API吗?
-
请求方式:PUT
-
请求路径:/索引库名
-
请求参数:json格式:
{ "settings": { "属性名": "属性值" } }
settings:就是索引库设置,其中可以定义索引库的各种属性,目前我们可以不设置,都走默认。
这里给搭建看看集群中分片和备份的设置方式,示例:
PUT /heima
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
这里有两个配置:
- number_of_shards:分片数量,这里设置为3
- number_of_replicas:副本数量,这里设置为1,每个分片一个备份,一个原始数据,共2份。
通过chrome浏览器的head查看,我们可以查看到分片的存储结构:
[外链图片转存中…(img-IBGeWKqL-1567175371502)]
可以看到,heima这个索引库,有三个分片,分别是0、1、2,每个分片有1个副本,共6份。
- node-01上保存了0号分片和1号分片的副本
- node-02上保存了1号分片和2号分片的副本
- node-03上保存了0号分片和2号分片的副本
x’x’x’xsa