Elasticsearch是一个建立在全文搜索引擎 Apache Lucene™ 基础上的搜索引擎,可以说 Lucene 是当今最先进,最高效的全功能开源搜索引擎框架。它由Shay Banon开发并于2010年发布,现在是由Elasticsearch BV负责维护。Elasticsearch是一个实时分布式和开源的全文搜索和分析引擎。 它可以从RESTful Web服务接口访问,并使用JSON文档来存储数据。它是基于Java编程语言,这使Elasticsearch能够在不同的平台上运行。使用户能够以非常快的速度来搜索非常大的数据量。
一般被拿来解决一些什么样的问题?
具体的使用场景
针对多表联查的场景,有两种解决方案:
Elastic 会索引所有字段,经过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。
所以,Elastic 数据管理的顶层单位就叫做 Index(索引)。它是单个数据库的同义词。每个 Index (即数据库)的名字必须是小写。
在 Index(索引)中,可以定义一个或多个类型。
类似于 数据库 的 Table,每一种类 型的数据存放在一起。
保存在某个 Index(索引)下,某种 Type(类型)的一个数据,Document(文档)是JSON格式的,Document 就像是 数据库 中某个 Table 里面每一行的数据,字段就是Document里的属性。
保存在某个 Index(索引)下,某种 Type(类型)的一个数据,Document(文档)是JSON格式的,Document 就像是 数据库 中某个 Table 里面每一行的数据,字段就是Document里的属性。
一个集群至少有一个节点,而一个节点就是一个 Elasticsearch 进程,节点可以有多个默认索引,如果创建索引,那么索引将会有5个分片(primary shard 又称主分片)构成的,每一个主分片会有一个副本(replica shard 又称复制分片)。
上图是一个有3个节点的集群,主分片与对应的复制分片都不回在同一个节点内,这样有利于如果某个节点宕机,数据也不至于丢失。
实际上,一个分片就是一个 Lucene 索引,一个包含倒排索引的文件目录,倒排索引的结构使得 Elasticsearch 在不扫描全部文档的情况下,就能检索文档包含的特定关键字。
Elasticsearch 使用的是一种称为倒排索引的结构,采用 Lucene 倒排索引作为底层。
这种结构适用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。
例如,现在有两个文档,每个文档包含如下内容:
# 文档1包含的内容
Study every day, good good up to forever
# 文档2包含的内容
To forever, study every day, good good up
为了创建倒排索引,首先要将每个文档拆分成独立的词(或称为词条或者tokens),然后创建一个包含所有不重复的词条的排序列表,然后列出每个词条出现在哪个文档。
如果搜索 to forever,只需查看包含每个词条的文档。
两个文档都匹配,但是第一个文档比第二个文档的匹配程度更高。
如果没有别的条件,这两个包含关键字的文档都将返回。
我们采用Docker容器来安装ES,首先需要安装Docker。
Docker中每一个容器都是独立运行的,相当于一个独立的linux系统,如果想便捷地修改容器内的文件,我们就需要把容器目录挂载到主机的目录上。容器端口类似,外界无法直接访问容器内部的端口,需要先将容器端口映射到linux主机端口上才能访问。
参考:Docker 安装文档
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
sudo yum install -y yum-utils
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io
sudo systemctl start docker
#1. 检查docker版本
docker -v
#2. 查看docker已有镜像
sudo docker images
#3. 设置docker开机启动
sudo systemctl enable docker
# 创建文件
sudo mkdir -p /etc/docker
# 修改配置, 设置镜像
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://vw9qapdy.mirror.aliyuncs.com"]
}
EOF
# 重启后台线程
sudo systemctl daemon-reload
# 重启docker
sudo systemctl restart docker
# 存储和检索数据
docker pull elasticsearch:7.4.2
# 创建配置文件目录
mkdir -p /mydata/elasticsearch/config
# 创建数据目录
mkdir -p /mydata/elasticsearch/data
# 将/mydata/elasticsearch/文件夹中文件都可读可写
chmod -R 777 /mydata/elasticsearch/
# 配置任意机器可以访问 elasticsearch
echo "http.host: 0.0.0.0" >/mydata/elasticsearch/config/elasticsearch.yml
命令后面的 \是换行符,注意前面有空格
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2
# 当前 Docker 开机自启,所以 ES 现在也是开机自启
docker update elasticsearch --restart=always
# 可视化检索数据
docker pull kibana:7.4.2
docker run --name kibana \
-e ELASTICSEARCH_HOSTS=http://192.168.163.131:9200 \
-p 5601:5601 \
-d kibana:7.4.2
-e ELASTICSEARCH_HOSTS=http://192.168.163.131:9200 这里要设置成自己的虚拟机IP地址
浏览器输入192.168.163.131:5601 测试:
# 当前 Docker 开机自启,所以 kibana 现在也是开机自启
docker update kibana --restart=always
前面介绍说,Elasticsearch 都是通过 REST API 接口来操作数据的,那么下面接通过几个接口的请求来演示它的使用。
GET /_cat/nodes
GET /_cat/health
GET /_cat/master
GET /_cat/indices?v
GET /_cat/indices
put /ems
DELETE /ems
DELETE /*
es7去除了文档类型,默认是_doc,创建 ems/emp{name,age,birth}
PUT /ems
{
"mappings": {
"properties": {
"id":{
"type": "integer"
},
"name":{
"type": "keyword"
},
"age":{
"type": "integer"
},
"birth":{
"type": "date"
}
}
}
}
GET /ems #可以查看到ems索引的所有信息
GET /ems/_mapping #可以查看ems索引的映射信息
插入一条文档put /索引/_doc/唯一id es7默认数据类型是doc
PUT /ems/_doc/1
{
"name":"张三",
"age":24,
"birth":"2021-04-26"
}
POST /ems/_doc/
{
"name":"张三",
"age":24,
"birth":"2021-04-26"
}
get/索引/_doc/文档id
GET /ems/_doc/1
delete /索引/_doc/文档id
DELETE /ems/_doc/1
delete /索引/_doc/文档id
#这种会删除原有数据,重新再次添加,会丢失数据。
POST /ems/_doc/1
{
"name":"李四"
}
POST /ems/_doc/1
{
"doc":{
"name":"李四02"
}
}
PUT /ems/_doc/1
{
"doc":{
"name":"李四03"
}
}
#这种不会丢失数据,直接在原有的数据上修改
POST /ems/_update/1
{
"doc":{
"name":"李四"
}
}
下面的请求都是在Kibana dev-tools 操作
GET /goods/_search
{
"query": {
"match_all": {}
}
}
GET /goods/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"price": {
"order": "desc"
}
}
]
}
GET /goods/_search
{
"query": {
"match_all": {}
},
"size": 5,
"from": 0,
"sort": [
{
"price": {
"order": "desc"
}
}
]
}
GET /goods/_search
{
"query": {
"match_all": {}
},
"_source": ["typeName","id"]
}
注意:text类型会进行分词,es默认使用的对于中文是单字分词器
例如小黑会拆分成小和黑,对于英文是按单词分.keywod、integer、date 不分词
GET /goods/_search
{
"query": {
"term": {
"typeName": {
"value": "java"
}
}
}
}
GET /goods/_search
{
"query": {
"range": {
"price": {
"gte": 100,
"lte": 200
}
}
}
}
GET /goods/_search
{
"query": {
"prefix": {
"describe": {
"value": "effective"
}
}
}
}
GET /goods/_search
{
"query": {
"wildcard": {
"describe": {
"value": "*java*"
}
}
}
}
GET /goods/_search
{
"query": {
"ids": {
"values": [1386524724004339714,1386524723681378306]
}
}
}
GET /goods/_search
{
"query": {
"fuzzy": {
"typeName": "cava"
}
}
}
模糊查询 fuzzy 做大模糊的错误必须在0-2之间
注意:搜索关键词长度为2,不允许存在模糊;如果你的长度在3-5之间允许一次模糊;如果你的长度大于5,最多允许的模糊在0-2之间
#must:意味着条件都必须满足
#should:满足一个条件即可
#not must:必须不满足任何一个条件
GET /goods/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"typeName": {
"value": "java"
}
}
},
{
"term": {
"describe": {
"value": "零基础"
}
}
}
]
}
}
}
#1)如果搜索的字段分词,他会对query进行先分词后搜索
#2).如果搜索的字段不分词,他会直接使用query整体进行搜索
#3).建议fields里放入可分词的字段
GET /goods/_search
{
"query": {
"multi_match": {
"query": "java",
"fields": ["typeName","describe"]
}
}
}
GET /goods/_search
{
"query": {
"query_string": {
"default_field": "describe",
"query": "冰箱"
}
}
}
GET /goods/_search
{
"query": {
"bool": {
"must": [
{
"match_all": {}
}
],
"filter": {
"exists": {
"field": "describe"
}
}
}
}
}
# 查找匹配 price 为 98.0 的数据 非文本推荐使用 term
GET /goods/_search
{
"query": {
"match": {
"price": "98.0"
}
}
}
# 查找匹配 typeName 包含 二手 的数据
#match即全文检索,对检索字段进行分词匹配,
#会按照响应的评分 _score 排序,原理是倒排索引。
GET /goods/_search
{
"query": {
"match": {
"typeName": "二手"
}
}
}
# 查找 typeName 为 二手笔记本 的数据。
GET /goods/_search
{
"query": {
"match": {
"typeName": "二手笔记本"
}
}
}
#将需要匹配的值当成一整个单词(不分词)进行检索
# 这里会检索 typeName 匹配包含短语 笔记本 的数据
GET /goods/_search
{
"query": {
"match_phrase": {
"typeName": "笔记本"
}
}
}
#精确查询typeName为java的
GET /goods/_search
{
"query": {
"term": {
"typeName": {
"value": "java"
}
}
}
}
term用于精确检索(避免使用 term 查询文本字段) 非文本字段精确匹配推荐用term;
#聚合操作,搜索typeName中包含空调的所有商品的价格分布以及平均价格
#txt没法聚合 必须加.keyword精确替代
GET /goods/_search
{
"query": {
"match": {
"typeName": "空调"
}
},
"aggs": {
"priceAgg": {
"terms": {
"field": "price",
"size": 10
}
},
"priceAvg":{
"avg": {
"field": "price"
}
}
},
"size": 0
}
#"aggs": { ---聚合关键字
# "priceAgg": { ---聚合名为priceAgg(自定义)
# "terms": { ---聚合类型为term
# "field": "price", ---聚合字段为price
# "size": 10 ---取聚合后前十个数据
# }
# },
# "priceAvg":{ ---聚合名为priceAvg(自定义)
# "avg": { ---聚合类型为avg 求平均值
# "field": "price" ---聚合字段为price
# }
# }
# },
# "size": 0 ---不显示命中结果,只看聚合信息
#创建索引并指定属性的映射规则(相当于新建表并指定字段和字段类型)
PUT /my_index
{
"mappings": {
"properties": {
"age":{
"type": "integer"
},
"email": {
"type": "keyword"
},
"name": {
"type": "text"
}
}
}
}
#2.给已有映射增加字段
# 这里的 "index": false,表明新增的字段不能被检索。默认是true
PUT /my_index/_mapping
{
"properties": {
"employee-id": {
"type": "keyword",
"index": false
}
}
}
#3.查看映射
GET /my_index/_mapping
#查看某一个字段的映射----查看email的映射
GET /my_index/_mapping/field/email
对于已经存在的字段映射,我们不能更新。更新必须创建新的索引,进行数据迁移。
数据迁移方式:迁移方式分为两种,一种是7和7之后去掉type的情况,一种是包含type 迁移的情况。
对于我们的测试数据,是包含 type 的索引 my_index。
现在我们创建新的索引 newindex 并修改一些字段的类型来演示当需要更新映射时的数据迁移操作。
#1. 创建新的映射规则
PUT /newindex
{
"mappings": {
"properties": {
"age":{
"type": "integer"
},
"email": {
"type": "text"
},
"name": {
"type": "text"
},
"gender": {
"type": "keyword"
}
}
}
}
#2. 进行数据迁移
POST _reindex
{
"source": {
"index": "my_index"
},
"dest": {
"index": "newindex"
}
}
#查询包含Java编程思想关键字的进行高亮显示
GET /goods/_search
{
"query": {
"match_phrase": {
"describe": "Java编程思想"
}
},
"highlight": {
"pre_tags": [""],
"post_tags": [""],
"fields": {
"describe": {}
}
}
}
#利用script脚本批量进行修改
POST /goods/_doc/_update_by_query
{
"query": {
"match_all": {}
},
"script": {
"inline": "ctx._source['typeName'] = 'javc'"
}
}
Analyzer(分词器)的作用是把一段文本中的词按一定规则进行切分。默认的分词器一般都是针对于英文,
例如:whitespace tokenizer遇到空白字符时分割文本。它会将文本“Quick brown fox!”分割为[Quick,brown,fox!]。
对于中文我们需要安装额外的分词器来进行分词。否则中文会一个字一个字的进行拆分,非常不友好。
IK 分词器属于 Elasticsearch 的插件,所以 IK 分词器的安装目录是 Elasticsearch 的 plugins 目录,在我们使用Docker启动 Elasticsearch 时,已经将该目录挂载到主机的 /mydata/elasticsearch/plugins 目录。
IK 分词器的版本需要跟 Elasticsearch 的版本对应,当前选择的版本为 7.4.2,下载地址为:Github Release 或访问:镜像地址
# 进入挂载的插件目录 /mydata/elasticsearch/plugins
cd /mydata/elasticsearch/plugins
# 安装 wget 下载工具
yum install -y wget
# 下载对应版本的 IK 分词器(这里是7.4.2)
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
这里已经在挂载的 plugins 目录安装好了 IK分词器。现在我们进入到 es 容器内部检查是否成功安装。
# 进入容器内部
docker exec -it elasticsearch /bin/bash
# 查看 es 插件目录
ls /usr/share/elasticsearch/plugins
# 可以看到 elasticsearch-analysis-ik-7.4.2.zip
所以我们之后只需要在挂载的目录/mydata/elasticsearch/plugins下进行操作即可。
# 进入到 es 的插件目录
cd /mydata/elasticsearch/plugins
# 解压到 plugins 目录下的 ik 目录
unzip elasticsearch-analysis-ik-7.4.2.zip -d ik
# 删除下载的压缩包
rm -f elasticsearch-analysis-ik-7.4.2.zip
# 修改文件夹访问权限`在这里插入代码片`
chmod -R 777 ik/
# 进入 es 容器内部
docker exec -it elasticsearch /bin/bash
# 进入 es bin 目录
cd /usr/share/elasticsearch/bin
# 执行查看命令 显示 ik
elasticsearch-plugin list
# 退出容器
exit
# 重启 Elasticsearch
docker restart elasticsearch
#重启docker
systemctl restart docker
首先测试默认的分词器
GET /_analyze
{
"text": "我是王贝塔"
}
GET _analyze
{
"analyzer": "ik_smart",
"text":"我是王贝塔"
}
这里对于默认词库中没有的词,不会有词语的组合,所以我们可以通过配置自定义词库或远程词库来实现对词库的扩展。
现有ik分词器无法将这个词切分成一个关键词,但是又希望某个词成为关键词。例如:我想让王贝塔三个字不分开。
#进入ik的配置目录
cd /mydata/elasticsearch/plugins/ik/config
#打开IKAnalyzer.cfg.xml 配置文件
vim IKAnalyzer.cfg.xml
我们将扩展文件写在ext_dict位置
因为需要utf-8编码,我们只需要拷贝一个当前文件夹下的文件然后进行修改
cp stopword.dic ext.dic
#然后在里面添加你需要分词的词语即可
vim ext.dic
#之后重启ES服务
docker restart elasticsearch
同理不需要分词的内容,我们只需按照上面配置配置到stopext.dic中即可。
可在此处配置远程项目地址,实现动态修改。
在单台ES服务器节点上,随着业务量的发展索引文件慢慢增多,会影响到效率和内存存储问题等。
我们可以采用ES集群,将单个索引的分片到多个不同分布式物理机器上存储,从而可以实现高可用、容错性等。
ES集群中索引可能由多个分片构成,并且每个分片可以拥有多个副本。通过将一个单独的索引分为多个分片,我们可以处理不能在一个单一的服务器上面运行的大型索引,简单的说就是索引的大小过大,导致效率问题。不能运行的原因可能是内存也可能是存储。由于每个分片可以有多个副本,通过将副本分配到多个服务器,可以提高查询的负载能力。
此处我们以三台机器来搭建集群环境。
#存放配置文件的文件夹
sudo mkdir -p /mydata/elasticsearch/node-1/config
sudo mkdir -p /mydata/elasticsearch/node-2/config
sudo mkdir -p /mydata/elasticsearch/node-3/config
#存放数据的文件夹
sudo mkdir -p /mydata/elasticsearch/node-1/data
sudo mkdir -p /mydata/elasticsearch/node-2/data
sudo mkdir -p /mydata/elasticsearch/node-3/data
#存放运行日志的文件夹
sudo mkdir -p /mydata/elasticsearch/node-1/log
sudo mkdir -p /mydata/elasticsearch/node-2/log
sudo mkdir -p /mydata/elasticsearch/node-3/log
#存放IK分词插件的文件夹
sudo mkdir -p /mydata/elasticsearch/node-1/plugins
sudo mkdir -p /mydata/elasticsearch/node-2/plugins
sudo mkdir -p /mydata/elasticsearch/node-3/plugins
#授予权限
sudo chmod 777 /mydata/elasticsearch/node-1/data
sudo chmod 777 /mydata/elasticsearch/node-2/data
sudo chmod 777 /mydata/elasticsearch/node-3/data
sudo chmod 777 /mydata/elasticsearch/node-1/plugins
sudo chmod 777 /mydata/elasticsearch/node-2/plugins
sudo chmod 777 /mydata/elasticsearch/node-3/plugins
sudo chmod 777 /mydata/elasticsearch/node-1/log
sudo chmod 777 /mydata/elasticsearch/node-2/log
sudo chmod 777 /mydata/elasticsearch/node-3/log
sudo chmod 777 /mydata/elasticsearch/node-1/config
sudo chmod 777 /mydata/elasticsearch/node-2/config
sudo chmod 777 /mydata/elasticsearch/node-3/config
.
├── node-1
│ ├── config
│ │ └── elasticsearch.yml
│ ├── data
│ │ └── nodes
│ │ └── 0
│ │ ├── node.lock
│ │ └── _state
│ │ ├── manifest-3.st
│ │ ├── node-3.st
│ │ ├── _o.cfe
│ │ ├── _o.cfs
│ │ ├── _o.si
│ │ ├── segments_11
│ │ └── write.lock
│ ├── log
│ └── plugins
├── node-2
│ ├── config
│ │ └── elasticsearch.yml
│ ├── data
│ │ └── nodes
│ │ └── 0
│ │ ├── node.lock
│ │ └── _state
│ │ ├── manifest-4.st
│ │ ├── node-4.st
│ │ ├── _r.cfe
│ │ ├── _r.cfs
│ │ ├── _r.si
│ │ ├── segments_11
│ │ └── write.lock
│ ├── log
│ └── plugins
└── node-3
├── config
│ └── elasticsearch.yml
├── data
│ └── nodes
│ └── 0
│ ├── node.lock
│ └── _state
│ ├── manifest-1.st
│ ├── _n.cfe
│ ├── _n.cfs
│ ├── node-1.st
│ ├── _n.si
│ ├── segments_11
│ └── write.lock
├── log
└── plugins
#进入es的node-1配置文件中
cd /mydata/elasticsearch/node-1/config/
#创建elasticsearch.yml配置文件
vi elasticsearch.yml
将下列配置添加到elasticsearch.yml中
#集群名称
cluster.name: my-es
#当前该节点的名称
node.name: node-1
#是不是有资格竞选主节点
node.master: true
#是否存储数据
node.data: true
#最大集群节点数
node.max_local_storage_nodes: 3
#给当前节点自定义属性(可以省略)
#node.attr.rack: r1
#数据存档位置
path.data: /usr/share/elasticsearch/data
#日志存放位置
path.logs: /usr/share/elasticsearch/log
#是否开启时锁定内存(默认为是)
#bootstrap.memory_lock: true
#设置网关地址,我是被这个坑死了,这个地址我原先填写了自己的实际物理IP地址,
#然后启动一直报无效的IP地址,无法注入9300端口,这里只需要填写0.0.0.0
network.host: 0.0.0.0
#设置其它结点和该结点交互的ip地址,如果不设置它会自动判断,值必须是个真实的ip地址,设置当前物理机地址,
#如果是docker安装节点的IP将会是配置的IP而不是docker网管ip
network.publish_host: 10.64.57.162
#设置映射端口
http.port: 9200
#内部节点之间沟通端口
transport.tcp.port: 9300
#集群发现默认值为127.0.0.1:9300,如果要在其他主机上形成包含节点的群集,如果搭建集群则需要填写
#es7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点,也就是说把所有的节点都写上
discovery.seed_hosts: ["10.64.57.162:9300","10.64.57.162:9301","10.64.57.162:9302"]
#当你在搭建集群的时候,选出合格的节点集群,有些人说的太官方了,
#其实就是,让你选择比较好的几个节点,在你节点启动时,在这些节点中选一个做领导者,
#如果你不设置呢,elasticsearch就会自己选举,这里我们把三个节点都写上
cluster.initial_master_nodes: ["node-1","node-2","node-3"]
#在群集完全重新启动后阻止初始恢复,直到启动N个节点
#简单点说在集群启动后,至少复活多少个节点以上,那么这个服务才可以被使用,否则不可以被使用,
gateway.recover_after_nodes: 2
#删除索引是是否需要显示其名称,默认为显示
#action.destructive_requires_name: true
#进入es的node-2配置文件中
cd /mydata/elasticsearch/node-2/config
#创建elasticsearch.yml配置文件
vi elasticsearch.yml
将下列配置添加到elasticsearch.yml中
#集群名称
cluster.name: my-es
#当前该节点的名称
node.name: node-2
#是不是有资格竞选主节点
node.master: true
#是否存储数据
node.data: true
#最大集群节点数
node.max_local_storage_nodes: 3
#给当前节点自定义属性(可以省略)
#node.attr.rack: r1
#数据存档位置
path.data: /usr/share/elasticsearch/data
#日志存放位置
path.logs: /usr/share/elasticsearch/log
#是否开启时锁定内存(默认为是)
#bootstrap.memory_lock: true
#设置网关地址,我是被这个坑死了,这个地址我原先填写了自己的实际物理IP地址,
#然后启动一直报无效的IP地址,无法注入9300端口,这里只需要填写0.0.0.0
network.host: 0.0.0.0
#设置其它结点和该结点交互的ip地址,如果不设置它会自动判断,值必须是个真实的ip地址,设置当前物理机地址,
#如果是docker安装节点的IP将会是配置的IP而不是docker网管ip
network.publish_host: 10.64.57.162
#设置映射端口
http.port: 9201
#内部节点之间沟通端口
transport.tcp.port: 9301
#集群发现默认值为127.0.0.1:9300,如果要在其他主机上形成包含节点的群集,如果搭建集群则需要填写
#es7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点,也就是说把所有的节点都写上
discovery.seed_hosts: ["10.64.57.162:9300","10.64.57.162:9301","10.64.57.162:9302"]
#当你在搭建集群的时候,选出合格的节点集群,有些人说的太官方了,
#其实就是,让你选择比较好的几个节点,在你节点启动时,在这些节点中选一个做领导者,
#如果你不设置呢,elasticsearch就会自己选举,这里我们把三个节点都写上
cluster.initial_master_nodes: ["node-1","node-2","node-3"]
#在群集完全重新启动后阻止初始恢复,直到启动N个节点
#简单点说在集群启动后,至少复活多少个节点以上,那么这个服务才可以被使用,否则不可以被使用,
gateway.recover_after_nodes: 2
#删除索引是是否需要显示其名称,默认为显示
#action.destructive_requires_name: true
#进入es的node-2配置文件中
cd /mydata/elasticsearch/node-3/config
#创建elasticsearch.yml配置文件
vi elasticsearch.yml
将下列配置添加到elasticsearch.yml中
#集群名称
cluster.name: my-es
#当前该节点的名称
node.name: node-3
#是不是有资格竞选主节点
node.master: true
#是否存储数据
node.data: true
#最大集群节点数
node.max_local_storage_nodes: 3
#给当前节点自定义属性(可以省略)
#node.attr.rack: r1
#数据存档位置
path.data: /usr/share/elasticsearch/data
#日志存放位置
path.logs: /usr/share/elasticsearch/log
#是否开启时锁定内存(默认为是)
#bootstrap.memory_lock: true
#设置网关地址,我是被这个坑死了,这个地址我原先填写了自己的实际物理IP地址,
#然后启动一直报无效的IP地址,无法注入9300端口,这里只需要填写0.0.0.0
network.host: 0.0.0.0
#设置其它结点和该结点交互的ip地址,如果不设置它会自动判断,值必须是个真实的ip地址,设置当前物理机地址,
#如果是docker安装节点的IP将会是配置的IP而不是docker网管ip
network.publish_host: 10.64.57.162
#设置映射端口
http.port: 9202
#内部节点之间沟通端口
transport.tcp.port: 9302
#集群发现默认值为127.0.0.1:9300,如果要在其他主机上形成包含节点的群集,如果搭建集群则需要填写
#es7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点,也就是说把所有的节点都写上
discovery.seed_hosts: ["10.64.57.162:9300","10.64.57.162:9301","10.64.57.162:9302"]
#当你在搭建集群的时候,选出合格的节点集群,有些人说的太官方了,
#其实就是,让你选择比较好的几个节点,在你节点启动时,在这些节点中选一个做领导者,
#如果你不设置呢,elasticsearch就会自己选举,这里我们把三个节点都写上
cluster.initial_master_nodes: ["node-1","node-2","node-3"]
#在群集完全重新启动后阻止初始恢复,直到启动N个节点
#简单点说在集群启动后,至少复活多少个节点以上,那么这个服务才可以被使用,否则不可以被使用,
gateway.recover_after_nodes: 2
#删除索引是是否需要显示其名称,默认为显示
#action.destructive_requires_name: true
#创建镜像
docker run -e ES_JAVA_OPTS="-Xms512m -Xmx512m" -d -p 9200:9200 -p 9300:9300 -v /mydata/elasticsearch/node-1/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /mydata/elasticsearch/node-1/plugins:/usr/share/elasticsearch/plugins -v /mydata/elasticsearch/node-1/data:/usr/share/elasticsearch/data -v /mydata/elasticsearch/node-1/log:/usr/share/elasticsearch/log --name es-node-1 elasticsearch:7.4.2
docker run -e ES_JAVA_OPTS="-Xms512m -Xmx512m" -d -p 9201:9201 -p 9301:9301 -v /mydata/elasticsearch/node-2/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /mydata/elasticsearch/node-2/plugins:/usr/share/elasticsearch/plugins -v /mydata/elasticsearch/node-2/data:/usr/share/elasticsearch/data -v /home/elasticsearch/node-2/log:/usr/share/elasticsearch/log --name es-node-2 elasticsearch:7.4.2
docker run -e ES_JAVA_OPTS="-Xms512m -Xmx512m" -d -p 9202:9202 -p 9302:9302 -v /mydata/elasticsearch/node-3/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /mydata/elasticsearch/node-3/plugins:/usr/share/elasticsearch/plugins -v /mydata/elasticsearch/node-3/data:/usr/share/elasticsearch/data/ -v /mydata/elasticsearch/node-3/log:/usr/share/elasticsearch/log --name es-node-3 elasticsearch:7.4.2
http://10.64.57.162:9201/_cat/nodes?pretty
注意:如果需要配置IK中文分词器,需要在每个节点的plugs文件夹下下载一个IK分词器,请参照IK分词器配置过程。
Caused by: ElasticsearchException[Elasticsearch exception [type=illegal_argument_exception, reason=Result window is too large, from + size must be less than or equal to: [10000] but was [10285]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting.]]
这是因为ES默认支持的最大条数就是10000条,深度分页导致总条数超过10000,就会报这个错。我们可以根据业务进行相关调整。
###########配置ES默认支持的最大条数为15000############
PUT goods/_settings
{
"index.max_result_window":15000
}
OpenJDK 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.
{"type": "deprecation", "timestamp": "2021-04-28T02:26:18,981Z", "level": "WARN", "component": "o.e.d.c.s.Settings", "cluster.name": "my-es", "node.name": "node-1", "message" : "[transport.tcp.port] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version." }
{"type": "deprecation", "timestamp": "2021-04-28T02:26:19,056Z", "level": "WARN", "component": "o.e.d.c.s.Settings", "cluster.name": "my-es", "node.name": "node-1", "message" : "[node.max_local_storage_nodes] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major v ersion." }
{"type": "server", "timestamp": "2021-04-28T02:26:19,185Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "node name [node-1], node ID [hmFzzrNjRLu4kdFhMNYYgg], cluster name [my-es]" }
{"type": "server", "timestamp": "2021-04-28T02:26:19,185Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "version[7. 4.2], pid[1], build[default/docker/2f90bbf7b93631e52bafb59b3b049cb44ec25e96/2019-10-28T20:40:44.881551Z], OS[Linux/4.18.0-240.22.1.el8_3.x86_64/amd64], JVM[AdoptOpenJDK/OpenJ DK 64-Bit Server VM/13.0.1/13.0.1+9]" }
{"type": "server", "timestamp": "2021-04-28T02:26:19,186Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "JVM home [ /usr/share/elasticsearch/jdk]" }
{"type": "server", "timestamp": "2021-04-28T02:26:19,186Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "JVM argume nts [-Xms1g, -Xmx1g, -XX:+UseConcMarkSweepGC, -XX:CMSInitiatingOccupancyFraction=75, -XX:+UseCMSInitiatingOccupancyOnly, -Des.networkaddress.cache.ttl=60, -Des.networkaddress .cache.negative.ttl=10, -XX:+AlwaysPreTouch, -Xss1m, -Djava.awt.headless=true, -Dfile.encoding=UTF-8, -Djna.nosys=true, -XX:-OmitStackTraceInFastThrow, -Dio.netty.noUnsafe=tr ue, -Dio.netty.noKeySetOptimization=true, -Dio.netty.recycler.maxCapacityPerThread=0, -Dio.netty.allocator.numDirectArenas=0, -Dlog4j.shutdownHookEnabled=false, -Dlog4j2.disa ble.jmx=true, -Djava.io.tmpdir=/tmp/elasticsearch-5133258163291923947, -XX:+HeapDumpOnOutOfMemoryError, -XX:HeapDumpPath=data, -XX:ErrorFile=logs/hs_err_pid%p.log, -Xlog:gc*, gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m, -Djava.locale.providers=COMPAT, -Des.cgroups.hierarchy.override=/, -Xms512m, -Xmx512m, -Di o.netty.allocator.type=unpooled, -XX:MaxDirectMemorySize=268435456, -Des.path.home=/usr/share/elasticsearch, -Des.path.conf=/usr/share/elasticsearch/config, -Des.distribution .flavor=default, -Des.distribution.type=docker, -Des.bundled_jdk=true]" }
{"type": "server", "timestamp": "2021-04-28T02:26:24,126Z", "level": "INFO", "component": "o.e.x.s.a.s.FileRolesStore", "cluster.name": "my-es", "node.name": "node-1", "messa ge": "parsed [0] roles from file [/usr/share/elasticsearch/config/roles.yml]" }
{"type": "server", "timestamp": "2021-04-28T02:26:24,543Z", "level": "INFO", "component": "o.e.x.m.p.l.CppLogMessageHandler", "cluster.name": "my-es", "node.name": "node-1", "message": "[controller/105] [Main.cc@110] controller (64 bit): Version 7.4.2 (Build 473f61b8a5238b) Copyright (c) 2019 Elasticsearch BV" }
{"type": "server", "timestamp": "2021-04-28T02:26:24,909Z", "level": "DEBUG", "component": "o.e.a.ActionModule", "cluster.name": "my-es", "node.name": "node-1", "message": "U sing REST wrapper from plugin org.elasticsearch.xpack.security.Security" }
{"type": "server", "timestamp": "2021-04-28T02:26:25,321Z", "level": "INFO", "component": "o.e.d.DiscoveryModule", "cluster.name": "my-es", "node.name": "node-1", "message": "using discovery type [zen] and seed hosts providers [settings]" }
{"type": "server", "timestamp": "2021-04-28T02:26:25,928Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "initialize d" }
{"type": "server", "timestamp": "2021-04-28T02:26:25,928Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "starting . .." }
{"type": "server", "timestamp": "2021-04-28T02:26:26,023Z", "level": "INFO", "component": "o.e.t.TransportService", "cluster.name": "my-es", "node.name": "node-1", "message": "publish_address {10.64.57.162:9300}, bound_addresses {0.0.0.0:9300}" }
{"type": "server", "timestamp": "2021-04-28T02:26:26,029Z", "level": "INFO", "component": "o.e.b.BootstrapChecks", "cluster.name": "my-es", "node.name": "node-1", "message": "bound or publishing to a non-loopback address, enforcing bootstrap checks" }
ERROR: [1] bootstrap checks failed
[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
{"type": "server", "timestamp": "2021-04-28T02:26:26,035Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "stopping . .." }
{"type": "server", "timestamp": "2021-04-28T02:26:26,047Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "stopped" }
{"type": "server", "timestamp": "2021-04-28T02:26:26,047Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "closing .. ." }
{"type": "server", "timestamp": "2021-04-28T02:26:26,058Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "my-es", "node.name": "node-1", "message": "closed" }
{"type": "server", "timestamp": "2021-04-28T02:26:26,060Z", "level": "INFO", "component": "o.e.x.m.p.NativeController", "cluster.name": "my-es", "node.name": "node-1", "messa ge": "Native controller process has stopped - no new native processes can be started" }
解决办法:通过查看日志docker logs es-node-1 找到了
max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
这句话的意思是虚拟内存空间太小了,至少需要262144这么多,意思是elasticsearch拥有的内存权限太小,得扩大
#切换到root用户修改配置sysctl.conf
vi /etc/sysctl.conf
#添加下面配置:
vm.max_map_count=655360
#并执行命令
sysctl -p
然后重启ES的服务即可。
对于对象以及数组类型的测试,ES中不会识别,会默认会将其进行扁平化处理。可能会造成数据查询的差异。
PUT my-index-000001/_doc/1
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}
下面这个查询需要搜索Alice Smith 很显然 我们两个条件都不满足,应该查询不出来数据,但是ES不认识数组和对象类型,会将其进行扁平化处理。
GET my-index-000001/_search
{
"query": {
"bool": {
"must": [
{ "match": { "user.first": "Alice" }},
{ "match": { "user.last": "Smith" }}
]
}
}
}
#先删除映射
DELETE my-index-000001
#重新添加映射
PUT my-index-000001
{
"mappings": {
"properties": {
"user": {
"type": "nested"
}
}
}
}
再次添加数据进行测试
#添加数据
PUT my-index-000001/_doc/1
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}
#查询first为Alice last为Smith的用户
GET my-index-000001/_search
{
"query": {
"bool": {
"must": [
{ "match": { "user.first": "Alice" }},
{ "match": { "user.last": "Smith" }}
]
}
}
}
注意:数组和对象类型必须用nested类型,否则ES会将其进行扁平化处理,会造成查询的数据错乱。
前提:事先准备好1W条数据表方便进行测试,本次测试数据13000条。
--设置词法分析器
--BEGIN
--ctx_ddl.create_preference ('goods_chinese_lexer', 'chinese_lexer');
--END;
--全文索引创建格式
--CREATE INDEX "indexname" ON "tablename" ("fieldname")
--INDEXTYPE IS "CTXSYS"."CONTEXT" PARAMETERS ('lexer cms_chinese_lexer sync(on commit)');
--为数据库表GOODS创建全文索引
--CREATE INDEX "GOODS_DESCRIBE_INDEX" ON "GOODS" ("DESCRIBE")
--INDEXTYPE IS "CTXSYS"."CONTEXT" PARAMETERS ('lexer cms_chinese_lexer sync(on commit)');
select * from Goods where contains(describe,'java')>0
测试结果:最快11ms,最慢350ms。
前提:需要先将Oracle中的数据导入到ES中,13000条数据大概需要20多秒
GET /goods/_search
{
"query": {
"match_phrase": {
"describe": "java"
}
}
}
测试结果:最快0ms,最慢2ms。
代码测试删除1.3w数据 时间 3313ms 3s左右
代码测试添加1.3w数据 时间 60881ms 60s左右
代码测试更新1.3w数据 时间 2876ms 2s左右
这是一种最为简单的方式,在将数据写到数据库时,同时将数据写到ES,实现数据的双写。
说明:双写失败风险,包括以下3种:
ES系统不可用;应用系统和ES之间的网络故障;应用系统重启,导致系统来不及写入ES等。针对这种情况,有数据强一致性要求的,就必须双写放到事物中来处理,但是一旦用上事物,则性能下降更加明显。
针对第一种同步双写的性能和数据丢失问题,可以考虑引入MQ,从而形成了异步双写的方案:由于MQ的性能基本比oracle高出一个数量级,所以性能可以得到显著的提高。
上面两种方案中都存在硬编码问题,也就是有任何对mysq进行增删改查的地方要么植入ES代码,要么替换为MQ代码,代码的侵入性太强。
如果对实时性要求不高的情况下,可以考虑用定时器来处理,具体步骤如下:
数据库的相关表中增加一个字段为timestamp的字段,任何crud操作都会导致该字段的时间发生变化;原来程序中的CURD操作不做任何变化;增加一个定时器程序,让该程序按一定的时间周期扫描指定的表,把该时间段内发生变化的数据提取出来;逐条写入到ES中。
可以利用Completefuture来实现异步任务,让数据库的crud操作和ES操作异步执行。