分布式系统的可用性与扩展性
ES集群架构的优势:
至关重要的作用
# 指定索引的主分片和副本分片数
PUT /blogs
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
对于生产环境中分片的设定,需要提前做好容量规划
#查看集群的健康状况
GET _cluster/health
GET /_cat/nodes?v #查看节点信息
GET /_cat/health?v #查看集群当前状态:红、黄、绿
GET /_cat/shards?v #查看各shard的详细情况
GET /_cat/shards/{index}?v #查看指定分片的详细情况
GET /_cat/master?v #查看master节点信息
GET /_cat/indices?v #查看集群中所有index的详细信息
GET /_cat/indices/{index}?v #查看集群中指定index的详细信息
操作系统: CentOS7,准备用户es
elasticsearch:elasticsearch-7.17.3
切换到root用户,修改/etc/hosts
vim /etc/hosts
ip1 es-node1
ip1 es-node2
ip1 es-node3
# 指定集群名称3个节点必须一致
cluster.name: es-cluster
#指定节点名称,每个节点名字唯一
node.name: node-1
#是否有资格为master节点,默认为true
node.master: true
#是否为data节点,默认为true
node.data: true
# 绑定ip,开启远程访问,可以配置0.0.0.0
network.host: 0.0.0.0
#指定web端口
#http.port: 9200
#指定tcp端口
#transport.tcp.port: 9300
#用于节点发现
discovery.seed_hosts: ["es-node1", "es-node2", "es-node3"]
#7.0新引入的配置项,初始仲裁,仅在整个集群首次启动时才需要初始仲裁。
#该选项配置为node.name的值,指定可以初始化集群节点的名称
cluster.initial_master_nodes: ["node-1","node-2","node-3"]
#解决跨域问题
http.cors.enabled: true
http.cors.allow-origin: "*"
三个节点配置如下:
#node1的配置
cluster.name: es-cluster
node.name: node-1
node.master: true
node.data: true
network.host: 0.0.0.0
discovery.seed_hosts: ["es-node1", "es-node2", "es-node3"]
cluster.initial_master_nodes: ["node-1","node-2","node-3"]
http.cors.enabled: true
http.cors.allow-origin: "*"
#node2的配置
cluster.name: es-cluster
node.name: node-3
node.master: true
node.data: true
network.host: 0.0.0.0
discovery.seed_hosts: ["es-node1", "es-node2", "es-node3"]
cluster.initial_master_nodes: ["node-1","node-2","node-3"]
http.cors.enabled: true
http.cors.allow-origin: "*"
#node3的配置
cluster.name: es-cluster
node.name: node-2
node.master: true
node.data: true
network.host: 0.0.0.0
discovery.seed_hosts: ["es-node1", "es-node2", "es-node3"]
cluster.initial_master_nodes: ["node-1","node-2","node-3"]
http.cors.enabled: true
http.cors.allow-origin: "*"
Cerebro 可以查看分片分配和通过图形界面执行常见的索引操作。 完全开源,并且它允许添加用户,密码或 LDAP 身份验证问网络界面。
Cerebro 基于 Scala 的Play 框架编写,用于后端 REST 和 Elasticsearch 通信。 它使用通过 AngularJS 编写的单页应用程序(SPA)前端。
项目网址
下载地址
cerebro-0.9.4/bin/cerebro
#后台启动
nohup bin/cerebro > cerebro.log &
访问地址: http://主机ip:9000/
修改kibana配置
vim config/kibana.yml
server.port: 5601
server.host: "node1"
elasticsearch.hosts: ["http://node1:9200","http://node2:9200","http://node3:9200"]
i18n.locale: "zh-CN"
提示:Kibana对外的 tcp 端口是5601,使用netstat -tunlp|grep 5601即可查看进程
#后台启动
nohup bin/kibana &
访问 Kibana: http://ip:5601/
参考文档
ElasticSearch集群内部的数据是通过9300进行传输的,如果不对数据加密,可能会造成数据被抓包,敏感信息泄露。
解决方案: 为节点创建证书
TLS 协议要求Trusted Certificate Authority (CA)签发x.509的证书。证书认证的不同级别:
# 为集群创建一个证书颁发机构
bin/elasticsearch-certutil ca
# 为集群中的每个节点生成证书和私钥
bin/elasticsearch-certutil cert --ca elastic-stack-ca.p12
# 移动到config目录下
mv *.p12 config/
将如上命令生成的两个证书文件拷贝到另外两个节点作为通信依据。
# 拷贝到192.168.65.192
scp *.p12 [email protected]:/home/es/elasticsearch-7.17.3/config
三个ES节点增加如下配置:
## elasticsearch.yml 配置
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.client_authentication: required
xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12
xpack.security.enabled: true # 开启xpack认证机制
测试:
#使用Curl访问ES,返回401错误
curl 'localhost:9200/_cat/nodes?pretty'
浏览器访问 http://ip:9200/ 需要输入用户名密码
ES中内置了几个管理其他集成组件的账号即:apm_system, beats_system, elastic, kibana,
logstash_system, remote_monitoring_user,使用之前,首先需要添加一下密码。
bin/elasticsearch-setup-passwords interactive
interactive:给用户手动设置密码。
auto:自动生成密码。
开启了安全认证之后,kibana连接es以及访问es都需要认证。
修改kibana.yml
elasticsearch.username: "kibana_system"
elasticsearch.password: "123456"
启动kibana服务
nohup bin/kibana &
修改配置文件
vim conf/application.conf
hosts = [
{
host = "http://192.168.65.174:9200"
name = "es-cluster"
auth = {
username = "elastic"
password = "123456"
}
}
]
启动cerebro服务
nohup bin/cerebro > cerebro.log &
不同角色的节点:Master eligible / Data / Ingest / Coordinating /Machine Learning
在开发环境中,一个节点可承担多种角色。
在生产环境中:
一个节点只承担一个角色的配置
#Master节点
node.master: true
node.ingest: false
node.data: false
#data节点
node.master: false
node.ingest: false
node.data: true
#ingest 节点
node.master: false
node.ingest: true
node.data: false
#coordinate节点
node.master: false
node.ingest: false
node.data: false
种单一角色职责分离的好处:
生产环境中,建议为一些大的集群配置Coordinating Only Nodes
从高可用&避免脑裂的角度出发:
全局流量管理(GTM)和负载均衡(SLB)的区别:
GTM 是通过DNS将域名解析到多个IP地址,不同用户访问不同的IP地址,来实现应用服务流量的分配。
同时通过健康检查动态更新DNS解析IP列表,实现故障隔离以及故障切换。
最终用户的访问直接连接服务的IP地址,并不通过GTM。
而 SLB 是通过代理用户访问请求的形式将用户访问请求实时分发到不同的服务器,最终用户的访问流量必须要经过SLB。
一般来说,相同Region使用SLB进行负载均衡,不同region的多个SLB地址时,则可以使用GTM进行负载均衡。
两类数据节点,不同的硬件配置:
用于数据的写入:
用于保存只读的索引,比较旧的数据。通常使用大容量的磁盘
设置 | 分配索引到节点,节点的属性规则 |
---|---|
index.routing.allocation.include.{attr} | 至少包含一个值 |
index.routing.allocation.exclude.{attr} | 不能包含任何一个值 |
index.routing.allocation.require.{attr} | 所有值都需要包含 |
使用 Shard Filtering,步骤分为以下几步:
需要通过node.attr
来标记一个节点
# 标记一个 Hot 节点
elasticsearch.bat -E node.name=hotnode -E cluster.name=tulingESCluster -E http.port=9200 -E path.data=hot_data -E node.attr.my_node_type=hot
# 标记一个 warm 节点
elasticsearch.bat -E node.name=warmnode -E cluster.name=tulingESCluster -E http.port=9201 -E path.data=warm_data -E node.attr.my_node_type=warm
# 查看节点
GET /_cat/nodeattrs?v
创建索引时候,指定将其创建在hot节点上
# 配置到 Hot节点
PUT /index-2022-05
{
"settings":{
"number_of_shards":2,
"number_of_replicas":0,
"index.routing.allocation.require.my_node_type":"hot"
}
}
POST /index-2022-05/_doc
{
"create_time":"2022-05-27"
}
#查看索引文档的分布
GET _cat/shards/index-2022-05?v
Index.routing.allocation 是一个索引级的dynamic setting,可以通过API在后期进行设定
# 配置到 warm 节点
PUT /index-2022-05/_settings
{
"index.routing.allocation.require.my_node_type":"warm"
}
GET _cat/shards/index-2022-05?v
一个集群总共需要多少个节点?一个索引需要设置几个分片?规划上需要保持一定的余量,当负载出现波动,节点出现丢失时,还能正常运行。
做容量规划时,一些需要考虑的因素:
评估业务的性能需求:
ES集群常见应用场景:
硬件配置:
内存大小要根据Node 需要存储的数据来进行估算
假设总数据量1T,设置一个副本就是2T总数据量
部署方式:
集群扩容:
特性:
估算索引的的数据量,然后确定分片的大小:
拆分索引
如果在单个索引有大量的数据,可以考虑将索引拆分成多个索引:
es分片路由的规则:
shard_num = hash(_routing) % num_primary_shards
_routing字段的取值,默认是_id字段,可以自定义。
PUT /users
{
"settings": {
"number_of_shards":2
}
}
POST /users/_create/1?routing=fox
{
"name":"fox"
}
相关场景:
特性:
创建基于时间序列的索引:
这样做的好处:更加合理的组织索引,例如随着时间推移,便于对索引做的老化处理。
基于Date Math方式建立索引
比如:假设当前日期 2022-05-27
indexName-2022.05.27 | |
---|---|
indexName-2022.05 |
# PUT //_search
POST /%3Clogs-%7Bnow%2Fd%7D%3E/_search
PUT /logs_2022-05-27
PUT /logs_2022-05-26
#可以每天晚上定时执行
POST /_aliases
{
"actions": [
{
"add": {
"index": "logs_2022-05-27",
"alias": "logs_write"
}
},
{
"remove": {
"index": "logs_2022-05-26",
"alias": "logs_write"
}
}
]
}
GET /logs_write
早期Tribe Node 的方案存在一定的问题,现已被弃用。Elasticsearch 5.3引入了跨集群搜索的功能(Cross Cluster Search),推荐使用
//启动3个集群
elasticsearch.bat -E node.name=cluster0node -E cluster.name=cluster0 -E path.data=cluster0_data -E discovery.type=single-node -E http.port=9200 -E transport.port=9300
elasticsearch.bat -E node.name=cluster1node -E cluster.name=cluster1 -E path.data=cluster1_data -E discovery.type=single-node -E http.port=9201 -E transport.port=9301
elasticsearch.bat -E node.name=cluster2node -E cluster.name=cluster2 -E path.data=cluster2_data -E discovery.type=single-node -E http.port=9202 -E transport.port=9302
//在每个集群上设置动态的设置
PUT _cluster/settings
{
"persistent": {
"cluster": {
"remote": {
"cluster0": {
"seeds": [
"127.0.0.1:9300"
],
"transport.ping_schedule": "30s"
},
"cluster1": {
"seeds": [
"127.0.0.1:9301"
],
"transport.compress": true,
"skip_unavailable": true
},
"cluster2": {
"seeds": [
"127.0.0.1:9302"
]
}
}
}
}
}
CCS的配置:
1)seeds
配置的远程集群的remote cluster的一个node。
2)connected
如果至有少一个到远程集群的连接则为true。
3)num_nodes_connected
远程集群中连接节点的数量。
4)max_connections_per_cluster
远程集群维护的最大连接数。
5)transport.ping_schedule
设置了tcp层面的活性监听
6)skip_unavailable
设置为true的话,当这个remote cluster不可用的时候,就会忽略,默认是false,当对应的remote cluster不可用的话,则会报错。
7)cluster.remote.connections_per_cluster
gateway nodes数量,默认是3
8)cluster.remote.initial_connect_timeout
节点启动时等待远程节点的超时时间,默认是30s
9)cluster.remote.node.attr:
一个节点属性,用于过滤掉remote cluster中 符合gateway nodes的节点,比如设置cluster.remote.node.attr=gateway,那么将匹配节点属性node.attr.gateway: true 的node才会被该node连接用来做CCS查询。
10)cluster.remote.connect:
默认情况下,群集中的任意节点都可以充当federated client并连接到remote cluster,cluster.remote.connect可以设置为 false(默认为true)以防止某些节点连接到remote cluster
11)在使用api进行动态设置的时候每次都要把seeds带上
单个分片
两个分片
集群增加一个节点后,Elasticsearch 会自动进行分片的移动,也叫 Shard Rebalancing
算分不准的原因
相关性算分在分片之间是相互独立的,每个分片都基于自己的分片上的数据进行相关度计算。这会导致打分偏离的情况,特别是数据量很少时。
当文档总数很少的情况下,如果主分片大于1,主分片数越多,相关性算分会越不准
解决算分不准的方法:
耗费更加多的CPU和内存,执行性能低下,—般不建议使用
当分片数>节点数时
多分片的好处: 一个索引如果分布在不同的节点,多个节点可以并行执行
Shard是Elasticsearch 实现集群水平扩展的最小单位。过多设置分片数会带来一些潜在的问题:
从存储的物理角度看:
为什么要控制分片存储大小:
副本是主分片的拷贝:
对性能的影响:
ES的分片策略会尽量保证节点上的分片数大致相同,但是有些场景下会导致分配不均匀:
可以通过调整分片总数,避免分配不均衡
如果目标Node的Shard数超过了配置的上限,则不允许分配Shard到该Node上。注意:index级别的配置会覆盖cluster级别的配置。
写请求是写入 primary shard,然后同步给所有的 replica shard;读请求可以从 primary shard 或 replica shard 读取,采用的是随机轮询算法。
根据 doc id 进行 hash,判断出来当时把 doc id 分配到了哪个 shard 上面去,从那个 shard 去查询。
核心概念
segment file
: 存储倒排索引的文件,每个segment本质上就是一个倒排索引,每秒都会生成一个segment文件,当文件过多时es会自动进行segment merge(合并文件),合并时会同时将已经标注删除的文档物理删除。
commit point
:记录当前所有可用的segment,每个commit point都会维护一个.del文件,即每个.del文件都有一个commit point文件(es删除数据本质是不属于物理删除),当es做删改操作时首先会在.del文件中声明某个document已经被删除,文件内记录了在某个segment内某个文档已经被删除,当查询请求过来时在segment中被删除的文件是能够查出来的,但是当返回结果时会根据commit point维护的那个.del文件把已经删除的文档过滤掉
translog日志文件
: 为了防止elasticsearch宕机造成数据丢失保证可靠存储,es会将每次写入数据同时写到translog日志中。
os cache
:操作系统里面,磁盘文件其实都有一个东西,叫做os cache,操作系统缓存,就是说数据写入磁盘文件之前,会先进入os cache,先进入操作系统级别的一个内存缓存中去
Refresh
Translog
Flush
数据建模
#避免查询时脚本
GET blogs/_search
{
"query": {
"bool": {
"must": [
{"match": {
"title": "elasticsearch"
}}
],
"filter": {
"script": {
"script": {
"source": "doc['title.keyword'].value.length()>5"
}
}
}
}
}
}
GET /es_db/_search
{
"query": {
"wildcard": {
"address": {
"value": "*白云*"
}
}
}
}
优化分片
#手动force merge
POST /my_index/_forcemerge
如果需要追求极致的写入速度,可以牺牲数据可靠性及搜索实时性以换取性能:
PUT /my_index/_settings
{
"index" : {
"refresh_interval" : "10s"
}
}