微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ES集群

检测集群状态

通过curl命令获取集群的状态

[root@elkstack02 ~]# curl –sXGET http://10.0.0.51:9200/_cluster/health?pretty=true

执行结果如下图所示: 获取到的是一个json格式的返回值,那就可以通过python对其中的信息进行分析,例如对status进行分析,如果等于green(绿色)就是运行在正常,等于yellow(黄色)表示副本分片丢失,red(红色)表示主分片丢失。

[root@es01-51 elasticsearch-head]# curl –sXGET http://10.0.0.51:9200/_cluster/health?pretty=true
curl: (6) Could not resolve host: xn--sxget-xu3b; UnkNown error
{
  "cluster_name" : "elk_cluster",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 2,
  "number_of_data_nodes" : 2,
  "active_primary_shards" : 16,
  "active_shards" : 32,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}

2)python脚本检测

#编写python脚本
[root@elkstack01 ~]# vim es_cluster_status.py
#!/usr/bin/env python
#coding:utf-8
#Author:_DriverZeng_
#Date:2017.02.12
 
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
import subprocess
body = ""
false = "false"
clusterip = "10.0.0.51"
obj = subprocess.Popen(("curl -sXGET http://"+clusterip+":9200/_cluster/health?pretty=true"),shell=True, stdout=subprocess.PIPE)
data =  obj.stdout.read()
data1 = eval(data)
status = data1.get("status")
if status == "green":
    print "\033[1;32m 集群运行正常 \033[0m"
elif status == "yellow":
    print "\033[1;33m 副本分片丢失 \033[0m"
else:
    print "\033[1;31m 主分片丢失 \033[0m"
 
#执行结果如下
[root@elkstack01 ~]# python es_cluster_status.py
集群运行正常

ES 单机部署

RPM安装

es地址:https://www.elastic.co/cn/downloads/elasticsearch

插件安装

添加环境变量

vim .bash_profile
/usr/share/elasticsearch/bin/
source .bash_profile
  1. install(失效)
elasticsearch/bin/plugin install mobz/elasticsearch-head
  1. 或者下载安装

下载node.js: https://nodejs.org/en/download/

tar -xf node-v12.16.3-linux-x64.tar.xz               #解压
mv node-v12.16.3-linux-x64 /usr/local/node          #重命名
cd /usr/bin                                    #切换到环境变量目录下
ln -s /usr/local/node/bin/node node                 #添加执行软链
ln -s /usr/local/node/bin/npm npm
node -v        #查看版本,验证是否安装成功
npm-v

插件地址:https://github.com/mobz/elasticsearch-head

git clone git://github.com/mobz/elasticsearch-head.git`
cd elasticsearch-head
npm install
npm run start
curl http://localhost:9100/

Docker安装

拉取镜像

docker pull docker.io/elasticsearch:7.6.2

运行Elasticsearch

docker run --name elasticsearch -p 9200:9200 -p 9300:9300 -d \
-e  "discovery.type=single-node" \
-e ES_JAVA_OPTS="-xms64m -Xmx256m" \
-v /data/es/data:/usr/share/elasticsearch/data \
-v /data/es/plugins:/usr/share/elasticsearch/plugins \
docker.elastic.co/elasticsearch/elasticsearch:7.6.2

如果用docker-compose:

version: '3.4'
services:
  es-cluster:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
    volumes:
      - /data/es/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
      - /data/es/data:/usr/share/elasticsearch/data
      - /data/es/plugins:/usr/share/elasticsearch/plugins
      - /data/es/logs:/usr/share/elasticsearch/logs
    ports:
      - 9200:9200
      - 9300:9300
      - 10010:10010
    environment:
      - "ES_JAVA_OPTS=-xms512m -Xmx512m"
      #- "discovery.type=single-node"
  kibana:
    image: docker.elastic.co/kibana/kibana:7.6.2
    environment:
      - ELASTICSEARCH_URL=http://192.168.3.210:9200
      - ELASTICSEARCH_HOSTS=http://192.168.3.210:9200
    ports:
      - 5601:5601

参数解释

  • 9200:http协议 外部通讯 外部访问

  • 9300:tcp协议 内部通讯 用于集群

  • discovery.type=single-node:单机运行,如果启动不了,可以加大内存设置:-e ES_JAVA_OPTS="-xms512m -Xmx512m"

访问 Elasticsearch 地址

注:访问前先关闭防火墙

curl ip:9200

安装 ik 分词器

docker exec -it es bash
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.1.1/elasticsearch-analysis-ik-7.1.1.zip

安装 ik 分词器后重启 Elasticsearch

postman 测试 ik 分词器

(注意:把 Content-Type 设置为 application/json )

img

安装 es-head插件

docker pull mobz/elasticsearch-head:5

启动容器

docker run --name elasticsearch-head -p 9100:9100 -d mobz/elasticsearch-head:5

访问 es-head

img

es-head 连接 Elasticsearch

当输入 http://192.168.232.128:9200/ 点击连接时,会发现无法连接。是因为前后端分离开发,存在跨域问题,需要在服务端做 CORS 的配置。我们再次进入 Elasticsearch 容器内部,修改 elasticsearch.yml 配置。

docker exec -it es /bin/bash
[root@7f213e9fb6bb elasticsearch]# vi config/elasticsearch.yml
#添加如下两条配置,注意冒号后面有空格,保存并退出。
http.cors.enabled: true 
http.cors.allow-origin: "*"

如图:

img

最后退出容器,并重启 Elasticsearch。

再次访问 es-head,连接成功。

img

ElasticSearch集群部署

鉴于es集群要想提供中文索引和检索的服务,离不开中文分词功能。选择了效果较好的ik分词组件,嵌入到es中,以提供分词功能。考虑到es版本是7.8.0,ik分词组件版本必须与其一致。因此Ik分词程序包是从ik分词官网git网址:https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.8.0下载的v7.8.0版本。

环境准备

分布式es集群至少要求运行在3台或以上服务器上。本文讲述是基于安装在3台vmware虚拟机上,各虚拟机机器结点如下表:
image.png
本节使用的所有vmware虚拟机配置均为cpu:8核,内存6G,硬盘足够。

创建用户

useradd es
passwd es

创建es集群的basedir路径

在3台机器上统一特定路径位置创建es集群的basedir。

mkdir /opt/es/app
mkdir /opt/es/data
mkdir /opt/es/tmp
mkdir /opt/es/logs

端口准备

es集群需要认开放3个端口:9200和9300和54328。其中:

9200:  作为 Http 协议,主要用于外部通讯
9300:  数据传输端口:9300 用于集群之间交换数据
54328: 组播端口(UDP)

firewall-cmd --zone=public --add-port=19200/tcp --permanent
firewall-cmd --zone=public --add-port=19300/tcp --permanent
firewall-cmd --zone=public --add-port=54328/udp --permanent
firewall-cmd --reload

关闭selinux

setenforce 0

内核配置

可参考:https://www.elastic.co/guide/en/elasticsearch/reference/current/system-config.html

vim /etc/security/limits.conf:

#打开最大文件描述符的数量
es		-		nofile			65535
#用户可以创建的线程数至少为 4096
es		-		nproc			4096
es		-		memlock			unlimited

vim /etc/sysctl.conf:

#禁用swap
vm.swappiness=1
#虚拟内存最大使用量
vm.max_map_count=262144

至此,es集群安装所需的全部准备工作全部完成。下面开始安装es集群。

单机Docker集群

vm.max_map_count设置应在以下位置永久设置/etc/sysctl.conf

#永久生效
echo "vm.max_map_count=262144" >> /etc/sysctl.conf
sysctl -p

配置docker网络

为了模拟我们的es是独立服务器,我们可以使用docker网络IP指定隔离;docker 创建容器时认采用的bridge网络,自行分配IP,不允许我们自己指定。而在实际部署中,我们需要指定容器IP,不允许其自行分配IP,尤其是搭建集群时,固定IP时必须的。所以我们可以创建自己的bridge网络:mynet,创建容器的时候指定网络为mynet并指定IP即可

#查看网络模式 
docker network ls
#创建一个新的bridge网络-mynet
docker network create --driver bridge --subnet=172.18.12.0/16 --gateway=172.18.1.1 mynet
#查看网络详情
docker network inspect mynet
#以后使用--network=mynet --ip 172.18.12.x 指定IP

创建mster节点

port=1
mkdir -p ~/mydata/elasticsearch/master-${port}/config
mkdir -p ~/mydata/elasticsearch/master-${port}/data
chmod -R 777 ~/mydata/elasticsearch/master-${port}
cat <<EOF >~/mydata/elasticsearch/master-${port}/config/+
cluster.name: my-es #集群名称,同一集群该值必须设置相同
node.name: es-master-${port} #该节点的名字
node.master: true #该节点有机会成为master节点
node.data: false #该节点可以存储数据
network.host: 0.0.0.0
http.host: 0.0.0.0 #所有http均可访问
http.port: 920${port}
transport.tcp.port: 930${port}
discovery.zen.ping_timeout: 10s #设置集群中自动发现其他节点时ping连接的超时时间
discovery.seed_hosts: ["172.18.12.21:9301","172.18.12.22:9302","172.18.12.23:9303"] #设置集群中的master节点的初始化列表,可以通过这些节点来自动发现其他新加入集群的节点,es7的新增配置
cluster.initial_master_nodes: ["172.18.12.21"] # 新集群初始时的候选主节点,es7的新增配置
EOF
docker run --name es-master-${port} \
-p 920${port}:920${port} -p 930${port}:930${port} \
--network=mynet --ip 172.18.12.2${port} \
-e ES_JAVA_OPTS="-xms300m -Xmx300m" \
-v ~/mydata/elasticsearch/master-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v ~/mydata/elasticsearch/master-${port}/data:/usr/share/elasticsearch/data \
-v ~/mydata/elasticsearch/master-${port}/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.9.0

创建work节点

for port in $(seq 4 6); \
do \
mkdir -p ~/mydata/elasticsearch/node-${port}/config
mkdir -p ~/mydata/elasticsearch/node-${port}/data
chmod -R 777 ~/mydata/elasticsearch/node-${port}
cat <<EOF >~/mydata/elasticsearch/node-${port}/config/elasticsearch.yml
cluster.name: my-es #集群名称,同一集群该值必须设置相同
node.name: es-node-${port} #该节点的名字
node.master: false #该节点有机会成为master节点
node.data: true #该节点可以存储数据
network.host: 0.0.0.0
http.host: 0.0.0.0 #所有http均可访问
http.port: 920${port}
transport.tcp.port: 930${port}
discovery.zen.ping_timeout: 10s #设置集群中自动发现其他节点时ping连接的超时时间
discovery.seed_hosts: ["172.18.12.21:9301","172.18.12.22:9302","172.18.12.23:9303"] #设置集群中的master节点的初始化列表,可以通过这些节点来自动发现其他新加入集群的节点,es7的新增配置
cluster.initial_master_nodes: ["172.18.12.21"] # 新集群初始时的候选主节点,es7的新增配置
EOF
docker run --name es-node-${port} \
-p 920${port}:920${port} -p 930${port}:930${port} \
--network=mynet --ip 172.18.12.2${port} \
-e ES_JAVA_OPTS="-xms300m -Xmx300m" \
-v ~/mydata/elasticsearch/node-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v ~/mydata/elasticsearch/node-${port}/data:/usr/share/elasticsearch/data \
-v ~/mydata/elasticsearch/node-${port}/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.9.0 
done

curl http://192.168.0.100:9202/_cat/nodes 查看各个节点信息 ,其中带* 为认主节点。

本地部署es集群

下载软件包

如前所述,部署es7.8集群还同时需要安装配套的分词组件包:ik7.8。分别从官网地址:

https://www.elastic.co/cn/
https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.8.0

安装和部署ES

elasticsearch-7.8.0-linux-x86_64.tar.gz 放到 /opt/es/app目录下,3台机器都要放。

/opt/es/app/elasticsearch-7.8.0/plugins目录下新建ik目录,然后将ik的压缩包解压至该目录。

mkdir /opt/es/app/elasticsearch-7.8.0/plugins/ik

jvm相关配置

设置最小堆空间和最大堆空间,建议都设置成一样的值。 本次只设置了3g 堆内存空间。实际生产环境下为保证es能发挥正常性能,该2值很关键,强烈设置在5g以上,但最大不应超过32g。在此范围内越大越好,越大es能发挥越高的性能

vim /opt/es/app/elasticsearch-7.8.0/config/jvm.options

-xms3g
-Xmx3g
-XX:HeapDumpPath=/opt/es/logs

Es集群配置

elasticsearch.yml文件是es集群的主要配置文件3台机器都要配置
vim /opt/es/app/elasticsearch-7.8.0/config/elasticsearch.yml依次设置如下:

#ES 集群名,所有节点一致
cluster.name: job_search_cluster   
# ES节点名称
node.name: es-node1
# 定义节点为master还是work,如果是work节点则反过来
node.master: true
node.data: false
# 集群的索引数据存放目录
path.data: /opt/es/data
#日志存放目录
path.logs: /opt/es/logs
# es服务启动时是否锁住内存,为es发挥较高的性能
bootstrap.memory_lock: true
#当前节点的ip
network.host: 192.168.0.110
# http端口,用于http访问,restful接口都使用此端口
http.port: 19200
#tcp端口,外界和es集群的tcp通讯端口
transport.tcp.port: 19300
# ES集群节点之间相互发现的配置,填写所有节点ip
discovery.seed_hosts: ["192.168.0.110.19300","192.168.0.111.19300","192.168.0.112.19300"]
#集群master节点,只能配置一个
cluster.initial_master_nodes: ["es-node1"]
# 新建文档索引时,如果index不存在是否自动创建,一般不建议自动创建,可以设置为false
# 但es7.8需要支持kibinao组件等,需要对某个特定名称的索引支持自动创建。
action.auto_create_index: .watches,.triggered_watches,.watcher-history-*
#开启跨域支持认为false(必须配置否则head插件无法使用)
http.cors.enabled: true
# 跨域访问允许的域名地址,此处允许所有域名(使用正则)(必须配置否则head插件无法使用)
http.cors.allow-origin: "*"

启动/关闭es集群服务

启动,依次在3台机器上执行:

/opt/es/app/elasticsearch-7.8.0/bin/elasticsearch -d -p /opt/es/app/pid

-d 表示以后台服务方式运行, -p表示pid存储的文件

然后在每个机器上执行:jps –lm和cat /opt/es/app/pid 即可看到es进程和进程号。

关闭es:

pkill -F /opt/es/app/pid

查看集群节点:

curl -XGET 'localhost:9200/_cat/health?v&pretty'

ES集群构建索引

Es集群已经部署好了,接下来我们要新建索引,并展示如何构建全量和增量数据到索引中去的详尽实践方法论。

本文针对项目的测试数据假定来源都存储在MysqL数据库中。如下图所示:
image.png

数据库data库中的position表中共计有91644条数据。我们以此91644条数据为例逐步demo实践如果将该表中所有数据构建到es索引中去。

新建索引和配置mapping

Es restful API

接下来我们在叙述es新建索引和配置mapping的过程中会大量地用到es提供的restful API。以下罗列最常用的es restful API:

#查看es集群健康状态:
curl http://192.168.0.110:19200/_cat/health?

#查看es集群节点:
curl http://192.168.0.110:19200/_cat/nodes?v

#查看es集群有哪些索引:
curl http://192.168.0.110:19200/_cat/indices?v

说明:v是用来要求在结果中返回表头

elasticsearch 7.x版本之后已经彻底去除了type的概念,elasticsearch7认不在支持指定索引类型,认索引类型是_doc。index下面不再允许设置type。直接给某个index设置mapping即可。相当于index就是MysqL数据库中的dabase.table了。一个index唯一对应一个 mapping且不再支持type是es 7.x以后的正确现状。更详细的细节可参考:Removal of mapping typesMapping

假定对应position招聘职位表数据,我们新建的es的index名称叫:position。

#创建索引名称为position: 
curl -XPUT http://192.168.0.110:19200/position?pretty
#再次查看索引:
curl http://192.168.0.110:19200/_cat/indices?v

可见名称叫position的index已经被创建,它的docs.count为0,目前没有任何文档即数据。

删除索引job:

curl -XDELETE http://192.168.0.110:19200/position?pretty

接下来要为position 索引库新建一个mapping。下面重点叙述es的mapping即映射。

ES映射(mapping)

#查看指定index的mapping命令:
curl -XGET http://192.168.0.110:19200/position/_mapping?pretty
#查询所有index中的mapping: 
curl -XGET http://192.168.0.110:19200/_all/_mapping?pretty

可见此时索引没有任何mapping。Mapping为空。

Es7.x 版本下为position这个index新建一个mapping的restful API语法有如下两种合法的形式:

1) 事先不单独创建index,而将index和mapping一次性在一个命令中创建,语法如下:

curl  -XPUT http://192.168.0.110:19200/position/?pretty  -H 'content-Type:application/json'  -d  ' {
    "mappings":{
        "properties":{
            "title":{
                "type":"text"
            },
            "description":{
                "type":"text"
            },
            "price":{
                "type":"double"
            },
            "onSale":{
                "type":"boolean"
            },
            "type":{
                "type":"integer"
            },
            "createDate":{
                "type":"date"
            }
        }
    }
}'

2) 分阶段创建:先单独创建index, 再基于此index创建mapping,语法如下:

curl  -XPUT http://192.168.0.110:19200/position1/_mappings?pretty  -H 'content-Type:application/json'  -d  ' {
    "properties":{
        "title":{
            "type":"text"
        },
        "description":{
            "type":"text"
        },
        "price":{
            "type":"double"
        },
        "onSale":{
            "type":"boolean"
        },
        "type":{
            "type":"integer"
        },
        "createDate":{
            "type":"date"
        }
    }
}'

以上两种方式建议方式1,一次性创建index和mapping。简单明了。

最后再次查看现在的index和mapping:

#查看index: 
curl http://192.168.0.110:19200/_cat/indices?v
#再次执行查看maping: 
curl -XGET http://192.168.0.110:19200/_all/_mapping?pretty

这里先删除postion和position1两个index如下:

字段数据类型

Es支持非常多的字段数据类型:

  • text:认会进行分词,支持模糊查询(5.x之后版本string类型已废弃,请大家使用text)。
  • keyword:不进行分词;keyword类型认开启doc_values来加速聚合排序操作,占用了大量磁盘io 如非必须可以禁用doc_values。
  • number:如果只有过滤场景 用不到range查询的话,使用keyword性能更佳,另外数字类型的doc_values比字符串更容易压缩。
  • array:es不需要显示定义数组类型,只需要在插入数据时用'[]'表示即可,'[]'中的元素类型需保持一致。
  • range:对数据的范围进行索引;目前支持 number range、date range 、ip range。
  • boolean: 只接受true、false 也可以是字符串类型的“true”、“false”
  • date:支持毫秒、根据指定的format解析对应的日期格式,内部以long类型存储。
  • geo_point:存储经纬度数据对。
  • ip:将ip数据存储在这种数据类型中,方便后期对ip字段的模糊与范围查询
  • nested:嵌套类型,一种特殊的object类型,存储object数组,可检索内部子项。

以上叙述的都是es7.x尤其是es7.8支持的字段数据类型,低版本的es跟这个可能有些许差别,要注意!

核心数据类型

image.png

string类的text和keyword区别一定要重视:把一个string字段指定为text类型说明该字段内容是要经过中文分词器进行分词,然后切分成多个term用于全文检索的。于此相对,如果一个string字段被指定为keyword,就说明内部建索引过程不会对该字段文本内容进行分词,它会作为一个整体被建到索引正排里去。

地理数据类型

image.png
该数据类型主要针对支持地理坐标数据进行快速经纬度查询支持的一种特殊的数据类型。

复合数据类型

image.png

更详细的内容细节可参见网页:Field data types

利用索引模板定义和新建索引

本文采用索引模板index templates的方式来配置索引的schema。es索引可使用预定义的模板进行创建,这个模板称作Index templates。

注意:索引模板支队所有新建立的索引有效,对已经存在的索引无效。模板设置包括settings和mappings,通过模式匹配的方式使得多个索引重用一个模板。关于索引模板的更详细细节可参考网页:

Index templatesCreate or update index template APIDynamic templates
虽然es7.8引入了新的复合索引模板,但仍然支持传统的索引模板。本文使用传统的索引模板格式(legacy index template )。

match_mapping_type:被探测到的数据类型

在索引模板中有一个非常重要的field就是:match_mapping_type,它代表文档字段值被字面探测识别成的数据类型,它的规则如下:
image.png

  1. true 或false 被自动探测识别为boolean类型;
  2. 当date_detection开关打开时,一些符合特定日期格式字符串将会被自动探测识别为date类型;
  3. 所有带有小数部分的数字被自动探测识别为double类型。(注意不是float类型)
  4. 不带有小数部分的数字被自动识别为long类型;(注意不是integer类型。)
  5. 对于对象类型统一识别为object类型;
  6. 最重要的,所有字符串统一识别为string类型;
  7. *代表任意类型。

此外,_all在7.x版本已经被copy_to所代替,可用于满足特定场景。copy_to将字段数值拷贝到目标字段,实现类似_all的作用。
注意:copy_to的目标字段不出现在_source中

当date_detection被设置为true(认)时,凡是string类型且符合 strict_date_optional_time 设置的日期格式的字段都被识别为类型date。
strict_date_optional_time认格式如下: "strict_date_optional_time":"yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z"

date格式可以在put mapping的时候用 format 参数指定,如果不指定的话,则启用认格式,是"strict_date_optional_time||epoch_millis"。这表明只接受符合"strict_date_optional_time"格式的字符串值,或者long型数字。

match/unmatch/match_pattern:如何匹配与排除匹配

match表征文档的字段名称匹配什么样的模式,是一种过滤选项。认以通配符的形式匹配:即*或?等表示匹配含义。unmatch在上述match匹配的结果集中构建排除集。

如果想支持正则表达式匹配方式,则加上match_pattern:regex即可。

索引别名与0停机时间

一般来说重新索引过程中的会遇到的一个比较头疼的问题是必须更新你的应用,来使用另一个索引名。索引别名正是用来解决这个问题的!

索引别名就像一个快捷方式或软连接, 可以指向一个或多个索引, 也可以给任何需要索引名的API 使用。别名带给我们极大的灵活性,允许我们做到:

  • 一个运行的集群上无缝的从一索引切换到另一个
  • 给多个索引分类
  • 给索引的一个子集创建视图

这里有两种管理别名的途径: _alias 用于单个操作, _aliases 用于原子化多个操作。

我们假设你的应用采用一个叫 my_index 的索引。 而事实上, my_index 是一个指向当前真实索引的别名。真实的索引名将包含一个版本号: my_index_v1 , my_index_v2 等等。开始, 我们创建一个索引 my_index_v1 , 然后将别名 my_index 指向它:

创建一个索引 my_index_v1 , 然后将别名 my_index 指向它:

PUT /my_index_v1 <1>
PUT /my_index_v1/_alis/my_index <2>
  1. 创建索引 my_index_v1 。
  2. 将别名 my_index 指向 my_index_v1 。

你可以检测这个别名指向哪个索引:GET /*/_alias/my_index

或哪些别名指向这个索引:GET /my_index_v1/_alias/*
两者都将返回下列值:
image.png

然后, 我们决定修改索引中一个字段的映射。 当然我们不能修改现存的映射, 索引我们需要重新索引数据。 首先, 我们创建有新的映射的索引 my_index_v2 。
image.png
然后我们从将数据从 my_index_v1 迁移到 my_index_v2 。一旦我们认为数据已经被正确的索引了, 我们就将别名指向新的索引。

别名可以指向多个索引, 所以我们需要在新索引中添加别名的同时从旧索引中删除它。 这个操作需要原子化, 所以我们需要用 _aliases 操作:
image.png
这样,你的应用就从旧索引迁移到了新的,而没有停机时间。

Tips: 即使你认为现在的索引设计已经是完美的了,当你的应用在生产 环境使用时,还是有可能在今后有一些改变的。所以请做好准备:在应用中使用别名而不是索引。然后你就可以在任何时候重建索引。别名的开销很小,应当广泛使用。

Elasticsearch的别名,就类似数据库的视图。

创建别名****:

我们为索引my_index创建一个别名my_index_alias,这样我们对my_index_alias的操作就像对my_index的操作一样

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "my_index",
        "alias": "my_index_alias"
      }
    }
  ]
}

别名不仅仅可以关联一个索引,它能聚合多个索引

我们为索引my_index_1 和 my_index_2 创建一个别名my_index_alias,这样对my_index_alias的操作(仅限读操作),会操作my_index_1和my_index_2,类似于聚合了my_index_1和my_index_2.我们是不能对my_index_alias进行写操作,当有多个索引时alias,不能区分到底操作哪一个

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "my_index_1",
        "alias": "my_index_alias"
      }
    },
    {
      "add": {
        "index": "my_index_2",
        "alias": "my_index_alias"
      }
    }
  ]
}

GET /my_index_alias/_search
{
}

创建***filtered***的别名:

例如对于同一个index,我们给不同人看到不同的数据,

如my_index有个字段是team,team字段记录了该数据是那个team的。team之间的数据是不可见的。

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "my_index",
        "alias": "my_index__teamA_alias",
        "filter":{
            "term":{
                "team":"teamA"
            }
        }
      }
    },
    {
      "add": {
        "index": "my_index",
        "alias": "my_index__teamB_alias",
        "filter":{
            "term":{
                "team":"teamB"
            }
        }
      }
    },
    {
      "add": {
        "index": "my_index",
        "alias": "my_index__team_alias"
      }
    }
  ]
}

GET /my_index__teamA_alias/_search //只能看到teamA的数据
GET /my_index__teamB_alias/_search //只能看到teamB的数据
GET /my_index__team_alias/_search //既能看到teamA的,也能看到teamB的数据

因此在索引模板中我们强烈建议引入索引别名,以应对未来在生产环境中一定会发生的索引升级
下图是索引模板中设置索引别名的一个示例:
image.png

好了,到目前为止,关于索引模板的预备知识我们基本都讲述完毕了。下面开始实战过程:
为position招聘职位表数据定义一个索引模板。

为position数据库表数据定义动态索引模板

MysqL字段处理

CREATE TABLE `position` (
 `id` varchar(64) NOT NULL COMMENT '',
 `recruitment_id` varchar(64) NOT NULL COMMENT '',
 `position_name` varchar(256) DEFAULT NULL COMMENT '',
 `student_type` varchar(256) DEFAULT NULL COMMENT '',
 `student` varchar(255) DEFAULT NULL,
 `majorName` longtext,
 `major` longtext COMMENT '',
 `demand_number` varchar(8) DEFAULT NULL COMMENT '',
 `position_description` longtext COMMENT '',
 `city` text,
 `cityName` text,
 `college` varchar(500) DEFAULT NULL,
 `sut1` varchar(255) DEFAULT NULL,
 `major_standard` text COMMENT '标准专业',
 PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='招聘职位信息';

以上是招聘职位信息的 MysqL数表定义。

为了实现对该数据表实时数据的索引增量,我们统一为所有业务表再 新增一个字段:_updatetime,类型为bigint,并且同时创建该字段的MysqL 索引idx_for_updatetime,建索引的目的是为了快速查找定位。该字段存储前台发送来的对该条招聘职位信息改动(第一次叫创建)的时间戳,记录为自1970.1.1日0时0分0秒到现在经历的毫秒数,其值类似于1594508968000。

接下来,对position 插入最后一列字段为_updatetime,值统一设定为0,MysqL语句如下:

ALTER TABLE position ADD COLUMN _updatetime BIGINT NOT NULL COMMENT 'updated timestamp in epoch milliseconds' after major_standard;

为_updatetime建立索引,sql执行如下:

ALTER TABLE position ADD INDEX idx_for_updatetime(_updatetime) ;

Es 索引mapping数据格式设计

image.png
image.png
以上表格中除了红色标注“是”的项目需要文本分词以支持全文索引,其他文本不需要分词。大部分中文分词器在检索时选择ik_max_word,在检索时选择分词组件:ik_smart。对应如下:

建索引时分词模块:"analyzer": "ik_max_word"
检索时分词模块:   "search_analyzer": "ik_smart"

_updatetime字段es格式为date,format 为epoch_millis。

以动态索引模板创建索引和mapping

根据前节分析,我们可以很容易写出招聘职位信息表数据的动态索引模板,如下:

{
    "index_patterns":[
        "collegejob_*"
    ],
    "order":0,
    "settings":{
        "number_of_shards":5,
        "number_of_replicas":1,
        "analysis":{
            "analyzer":{
                "comma":{
                    "pattern":",",
                    "type":"pattern"
                }
            }
        }
    },
    "mappings":{
        "_source":{
            "enabled":true
        },
        "dynamic":"true",
        "date_detection":false,
        "numeric_detection":true,
        "properties":{
            "id":{
                "type":"keyword"
            },
            "recruitment_id":{
                "type":"keyword"
            },
            "demand_number":{
                "type":"text",
                "index":"true",
                "analyzer":"ik_max_word",
                "search_analyzer":"ik_smart",
                "fields":{
                    "raw":{
                        "type":"keyword",
                        "index":true,
                        "ignore_above":1024
                    }
                }
            },
            "major":{
                "type":"text",
                "analyzer":"comma",
                "search_analyzer":"comma"
            },
            "city":{
                "type":"text",
                "analyzer":"comma",
                "search_analyzer":"comma"
            },
            "updatetime":{
                "type":"date",
                "format":"epoch_millis"
            }
        },
        "dynamic_templates":[
            {
                "string_fields":{
                    "match":"*",
                    "match_mapping_type":"string",
                    "mapping":{
                        "type":"text",
                        "index":"true",
                        "analyzer":"ik_max_word",
                        "search_analyzer":"ik_smart",
                        "fields":{
                            "raw":{
                                "type":"keyword",
                                "index":true,
                                "ignore_above":32766
                            }
                        }
                    }
                }
            }
        ]
    },
    "aliases":{
        "{index}-alias":{
        }
    }
}

image.png
上图是对文字版的动态索引模板的主要内容的简要说明(可能跟文字版有些许出入,以最终的文字版为准)。

添加动态索引模板的es restfulAPI为:

curl -XPUT http://192.168.0.110:19200/_template/collegejob_template_1?pretty -H 'content-Type:application/json' -d '
{STATEMENT} '

将该语句中的{STATEMENT} 替换为上文 文字版的 动态索引模板内容即可。

接下来查询刚才新建的索引模板,执行命令:

curl -XGET http://192.168.0.110:19200/_template/collegejob_template_1?pretty

可见能查到刚才新建的collegejob_template_1索引模板了。

删除索引模板的命令是:

curl -XDELETE http://192.168.0.110:19200/_template/collegejob_template_1?pretty

最后根据这个动态索引模板,我们创建一个索引名称叫做:collegejob_position_v20200712_1.

注意index名称必须要能匹配到上面我们已经创建好的动态索引模板中的index_patterns中的值的pattern。

RestfulAPI:

curl -XPUT http://192.168.0.110:19200/collegejob_position_v20200712_1?pretty

再次查看索引:curl http://192.168.0.110:19200/_cat/indices?v

查看索引collegejob_position_v20200712_1的mapping:

curl http://192.168.0.110:19200/collegejob_position_v20200712_1/_mappings?pretty

可见该index已经被正确关联到我们预期的mapping上了。

至此,索引和mapping已经创建成功了。接下来开始构建全量索引数据和增量索引数据。

构建全量索引数据

准备测试数据

position ---招聘职位表,共计91644条测试数据。为了模拟分别构建全量索引数据和构建增量索引数据的过程,同时也模拟演示生产环境下数据产生与处理的过程,我们对测试数据做如下处理:

  1. 全量和增量数据的区分字段是_updatetime(_updatetime字段是个bigint型数据,存储自1970.1.1 0:0:0 至今的毫秒数,要求所有业务数据表都必须有此字段和定义);

  2. 假定从当前时刻开始建全量索引索引,记录开始建全量索引的当前时刻,记录为S时刻:2020年 07月 12日 星期日 09:51:09 CST, 转换成_updatetime的数据格式为:1594518666324,注意是一个13位长整数。记住这一时刻很重要。后面构建索引时也需要这个分界时间戳S。_

  3. _编写sql语句实现对position 表中一半数据的_updattime设置为早于时刻S的随机位于时间段[1577808000000, S],(1577808000000为2020.1.1) 另一半数据的_updattime设置为晚于时刻S的随机位于时间段[S,1594522719000]。(1594522719000 为S+1小时时刻)。

MysqL生成在i ≤ R ≤ j 这个范围得到一个随机整数R ,公式为:FLOOR(i + RAND() * (j – i + 1))

结果如下:

update position set _updatetime=FLOOR(1577808000000+RAND() \* (1594518666324-1577808000000 + 1)) limit 45822 ;

update position set _updatetime=FLOOR(1594522719000+RAND() \* (1594522719000-1594518666324 + 1)) where _updatetime=0 ;

接下来查询验证一下更新时间戳早于和晚于时刻S的数据条数:

select count(1) from position where _updatetime < 1594518666324 ;

select count(1) from position where _updatetime > 1594518666324 ;

现在表中一半数据早于时刻S,另一半数据晚于时刻S。

接下来构建MysqL2es的全量索引。

Zookeeper迅速安装部署方法

在后面章节全量和增量构建索引阶段为了提高构建索引的效率和保证高可用,我们经常用的一个实现思路是以多机器多进程代替单机单进程。而对于多机器多进程,不可避免涉及多进程之间工作进度的协同,需要用到分布式一致性的工具。建议使用zookeeper。比如分布式锁等。本节迅速简述一下zookeeper的安装部署和使用方法

注意:安装zookeeper之前需要在每台机器上安装好jdk,建议安装至少jdk1.8及以上版本。本文安装的是jdk1.8。

Zookeeper技术简介

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户
ZooKeeper包含一个简单的原语集,提供Java和C的接口。
ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,其中分布锁和队列有Java和C两个版本,选举只有Java版本。

原理

ZooKeeper是以Fast paxos算法为基础的,paxos 算法存在活锁的问题,即当有多个proposer交错提交时,有可能互相排斥导致没有一个proposer能提交成功,而Fast paxos作了一些优化,通过选举产生一个leader (领导者),只有leader才能提交proposer,具体算法可见Fast paxos。因此,要想弄懂ZooKeeper首先得对Fast paxos有所了解。

ZooKeeper的基本运转流程:

  • 1、选举leader
  • 2、同步数据。
  • 3、选举leader过程中算法有很多,但要达到的选举标准是一致的。
  • 4、leader要具有最高的执行ID,类似root权限。
  • 5、集群中大多数的机器得到响应并follow选出的leader

特点

在Zookeeper中,znode是一个跟Unix文件系统路径相似的节点,可以往这个节点存储或获取数据。如果在创建znode时Flag设置为EPHEMERAL,那么当创建这个znode的节点和Zookeeper失去连接后,这个znode将不再存在在Zookeeper里,Zookeeper使用Watcher察觉事件信息。当客户端接收到事件信息,比如连接超时、节点数据改变、子节点改变,可以调用相应的行为来处理数据。Zookeeper的Wiki页面展示了如何使用Zookeeper来处理事件通知,队列,优先队列,锁,共享锁,可撤销的共享锁,两阶段提交。

那么Zookeeper能做什么事情呢,简单的例子:假设我们有20个搜索引擎的服务器(每个负责总索引中的一部分的搜索任务)和一个总服务器(负责向这20个搜索引擎的服务器发出搜索请求并合并结果集),一个备用的总服务器(负责当总服务器宕机时替换总服务器),一个web的cgi(向总服务器发出搜索请求)。搜索引擎的服务器中的15个服务器提供搜索服务,5个服务器正在生成索引。这20个搜索引擎的服务器经常要让正在提供搜索服务的服务器停止提供服务开始生成索引,或生成索引的服务器已经把索引生成完成可以提供搜索服务了。使用Zookeeper可以保证总服务器自动感知有多少提供搜索引擎的服务器并向这些服务器发出搜索请求,当总服务器宕机时自动启用备用的总服务器。

Zookeeper集群快速搭建

从zookeeper官网https://zookeeper.apache.org/releases.html 下载当前最新版本的zookeeper3.6.1。
image.png

分布式zookeeper(简称zk)集群至少要求运行在3台或以上服务器上。本文讲述是基于安装在3台vmware虚拟机上,各虚拟机机器结点如下表:
image.png
本节使用的所有vmware虚拟机配置均为cpu:8核,内存6G,硬盘足够。

创建zk服务的basedir:/opt/zk

chown -R zk:zk /opt/zk

3) 在/opt/zk下分别创建app data logs temp分别作为zk的app/data/logs/temp 目录。
4) 配置zk

解压zk压缩包文件:apache-zookeeper-3.6.1-bin.tar.gz 到/opt/zk/app目录下:
image.png
在cent7a机器上执行:

cd /opt/zk/app/apache-zookeeper-3.6.1-bin/conf
mv zoo_sample.cfg zoo.cfg
vim zoo.cfg 修改如下:

image.png
image.png
其中clientPort 2181是客户端连接zk集群的端口,dataDir和dataLogDir分别是数据目录和日志目录。文件最后的3行是用于zk集群互联。

server.A = B:C:D
A:zookeeper服务器的序号,即第几号服务器.
 注意这个序号要与zookeeper的myid保持一致
B:服务器的 IP 地址
C:服务器跟随者follower与集群中的 leader 服务器交换信息的端口
D:如果集群中的 leader 服务器宕机,需要一个端口通信重新进行选举,选出一个新的 leader。这个端口就是用来做leader选举的端口

注意server.1/server.2/server.3 中的1/2/3是zk 结点的序号,不同结点必须不能相同。

直接将此zoo.cfg一行不用修改原样拷贝到cent7b和cent7c机器上相同目录下。
接下来在cent7a的datadir即:/opt/zk/data下新创建myid文件,并写入1:
image.png

同样地,在cent7b的datadir即:/opt/zk/data下新创建myid文件,并写入2:
image.png

在cent7c的datadir即:/opt/zk/data下新创建myid文件,并写入3:
image.png

至此,zk集群配置结束。启动zk集群之前不要忘记开放3台机器上2181/2888/3888 三个端口:

systemctl start firewalld
firewall-cmd --zone=public --add-port=2181/tcp --permanent
firewall-cmd --zone=public --add-port=2888/tcp --permanent
firewall-cmd --zone=public --add-port=3888/tcp --permanent
firewall-cmd --reload

Zk集群主要操作命令如下:

  • 服务端命令

在所有机器上执行:
/opt/zk/app/apache-zookeeper-3.6.1-bin/bin/zkServer.sh start/stop/status/restart ##启动/停止/查询状态/重启 zk服务
image.png
可见zk集群成功,1个leader和2个follower。

  • 客户端命令

在所有机器上执行:
/opt/zk/app/apache-zookeeper-3.6.1-bin/bin/zkCli.sh ##连接本地服务器,认是2181端口
/opt/zk/app/apache-zookeeper-3.6.1-bin/bin/zkCli.sh -server ip:port ##连接指定zk服务器和端口

交互式命令行使用

ZooKeeper是通过客户端脚本来操作的。客户端脚本:zkCli.sh,存放在ZooKeeper的bin目录下。
认连接本地的ZooKeeper服务器:#zkCli.sh
连接指定的ZooKeeper服务器:#zkCli.sh –server Server IP:port
在cent7a上运行:
/opt/zk/app/apache-zookeeper-3.6.1-bin/bin/zkCli.sh -server 192.168.0.112:2181显示如下:
执行:/opt/zk/app/apache-zookeeper-3.6.1-bin/bin/zkCli.sh -server 192.168.0.112:2181
此时进入zookeeper系统的交互模式。
此时键入h或help命令,可以看到交互模式下支持的命令选项,如下图:
image.png

命令行工具的一些简单操作如下:

  1. 显示根目录下、文件: ls / 使用 ls 命令来查看当前 ZooKeeper 中所包含的内容
  2. 显示根目录下、文件: ls2 / 查看当前节点数据并能看到更新次数等数据
  3. 创建文件,并设置初始内容: create /zk "test" 创建一个新的 znode节点“ zk ”以及与它关联的字符串
  4. 获取文件内容: get /zk 确认 znode 是否包含我们所创建的字符串
  5. 修改文件内容: set /zk "zkbak" 对 zk 所关联的字符串进行设置
  6. 删除文件: delete /zk 将刚才创建的 znode 删除
  7. 退出客户端: quit
  8. 帮助命令: help

Zookeeper可视化工具 ZooInspector使用

Zookeeper 有很多可视化工具,其中一个轻便易用的工具是ZooInspector 。解压后 直接在windows上双击 ZooInspector/build/ zookeeper-dev-ZooInspector.jar 即可打开图形界面如下:
image.png
键入上面搭建好的zookeeper集群192.168.0.112:2181 即可进入可视化界面:
image.png

更详细丰富的ES java search API使用方法,可根据需要参见官方ES文档:Search API

参考

Elasticsearch7.8详尽使用指南(一):ElasticSearch集群部署实践

Elasticsearch7.8详尽使用指南(二):ElasticSearch集群构建索引实践

https://zhuanlan.zhihu.com/p/257867352 Docker 安装 ElasticSearch

https://blog.csdn.net/xia296/article/details/108372102 【集群运维篇】使用docker搭建es(ElasticSearch)集群

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐