性能与容量之间的矛盾由来已久,计算机的多级存储体系就是其中一个经典的例子,同样的问题在Elasticsearch中也存在。为了保证Elasticsearch的读写性能,官方建议磁盘使用SSD固态硬盘。然而Elasticsearch要解决的是海量数据的存储和检索问题,海量的数据就意味需要大量的存储空间,如果都使用SSD固态硬盘成本将成为一个很大的问题,这也是制约许多企业和个人使用Elasticsearch的因素之一。为了解决这个问题,Elasticsearch冷热分离架构应运而生。
传统的Elasticsearch集群中所有节点均采用相同的配置,然而Elasticsearch并没有对节点的规格一致性做要求,换而言之就是每个节点可以是任意规格,当然这样做会导致集群各节点性能不一致,影响集群稳定性。但是如果有规则的将集群的节点分成不同类型,部分是高性能的节点用于存储热点数据,部分是性能相对差些的大容量节点用于存储冷数据,却可以一方面保证热数据的性能,另一方面保证冷数据的存储,降低存储成本,这也是Elasticsearch冷热分离架构的基本思想,如下图为一个3热节点,2冷节点的冷热分离Elasticsearch集群:
其中热节点为16核64GB 1TB SSD盘,用于满足对热数据对读写性能的要求,冷节点为8C32GB 5TB HDD在保证一定读写性能的基础之上提供了成本较低的大存储HDD盘来满足冷节点对数据存储的需求。
集群节点异构后接着要考虑的是数据分布问题,即用户如何对冷热数据进行标识,并将冷数据移动到冷节点,热数据移动到热节点。
仅仅将不同的节点设置为不同的规格还不够,为了能明确区分出哪些节点是热节点,哪些节点是冷节点,需要为对应节点打标签
Elasticsearch支持给节点打标签,具体方式是在elasticsearch.yml文件中增加
node.attr.{attribute}: {value}
配置。其中attribute为用户自定义的任意标签名,value为该节点对应的该标签的值,例如对于冷热分离,可以使用如下设置
node.attr.temperature: hot //热节点 node.attr.temperature: warm //冷节点
ps:中文通常叫冷热,英文叫hot/warm
节点有了冷热属性后,接下来就是指定数据的冷热属性,来设置和调整数据分布。冷热分离方案中数据冷热分布的基本单位是索引,即指定某个索引为热索引,另一个索引为冷索引。通过索引的分布来实现控制数据分布的目的。
Elasticsearch提供了index shard filtering功能(2.x开始),该功能在索引配置中提供了如下几个配置
index.routing.allocation.include.{attribute} Assign the index to a node whose {attribute} has at least one of the comma-separated values. index.routing.allocation.require.{attribute} Assign the index to a node whose {attribute} has all of the comma-separated values. index.routing.allocation.exclude.{attribute} Assign the index to a node whose {attribute} has none of the comma-separated values.
用户可以在创建索引,或后续的任意时刻设置这些配置来控制索引在不同标签节点上的分配动作。
index.routing.allocation.include.{attribute}
表示索引可以分配在包含多个值中其中一个的节点上。
index.routing.allocation.require.{attribute}
表示索引要分配在包含索引指定值的节点上(通常一般设置一个值)。
index.routing.allocation.exclude.{attribute}
表示索引只能分配在不包含所有指定值的节点上。
Elasticsearch的索引分片分配由ShardAllocator
决定,ShardAllocator
通过在索引分片创建或rebalance时对每个节点调用一系列AllocationDecider
来决定是否将节点分配到指定节点上,其中一个AllocationDecider是FilterAllocationDecider
,该decider用于应用集群,富贵论坛节点的一些基于attr的分配规则,涉及到节点级别配置的核心代码如下
private Decision shouldIndexFilter(IndexMetaData indexMd, RoutingNode node, RoutingAllocation allocation) { if (indexMd.requireFilters() != null) { if (indexMd.requireFilters().match(node.node()) == false) { return allocation.decision(Decision.NO, NAME, "node does not match index setting [%s] filters [%s]", IndexMetaData.INDEX_ROUTING_REQUIRE_GROUP_PREFIX, indexMd.requireFilters()); } } if (indexMd.includeFilters() != null) { if (indexMd.includeFilters().match(node.node()) == false) { return allocation.decision(Decision.NO, NAME, "node does not match index setting [%s] filters [%s]", IndexMetaData.INDEX_ROUTING_INCLUDE_GROUP_PREFIX, indexMd.includeFilters()); } } if (indexMd.excludeFilters() != null) { if (indexMd.excludeFilters().match(node.node())) { return allocation.decision(Decision.NO, NAME, "node matches index setting [%s] filters [%s]", IndexMetaData.INDEX_ROUTING_EXCLUDE_GROUP_SETTING.getKey(), indexMd.excludeFilters()); } } return null; }
根据业务数据量及读写性能要求选择合适的冷热节点规格
ES 的计算资源主要消耗在写入和查询过程,而不同业务场景在写入和查询方面的复杂度不同、比重不同,导致计算资源相比存储资源较难评估
- 日志场景:日志属于典型的写多读少类场景,计算资源主要消耗在写入过程中。我们在日志场景的经验是:2核8GB内存的资源最大可支持0.5w/s的写入能力,但注意不同业务场景可能有偏差。由于实例性能基本随计算资源总量呈线性扩容,您可以按实例资源总量估算写入能力。例如6核24GB内存的资源可支持1.5w/s的写入能力,40核160GB内存的资源可支持10w/s的写入能力。 - Metric 及 APM 等结构化数据场景:这也是写多读少类场景,但相比日志场景计算资源消耗较小,2核8GB内存的资源一般可支持1w/s的写入能力,您可参照日志场景线性扩展的方式,评估不同规格实例的实际写入能力。 - 站内搜索及应用搜索等搜索场景:此类为读多写少类场景,计算资源主要消耗在查询过程,由于查询复杂度在不同使用场景差别非常大,计算资源也最难评估,建议您结合存储资源初步选择计算资源,然后在测试过程中验证、调整。2.2 搭建集群自建
按照选定冷热节点规格部署服务器,搭建集群,热节点使用SSD盘,冷节点使用HDD盘,对热节点elasticsearcy.yml增加如下配置
node.attr.temperature: hot
对冷节点增加如下配置
node.attr.temperature: warm
启动集群,冷热分离的Elasticsearch集群即搭建完成
腾讯云预计于12月中旬上线冷热分离集群,用户只需要在创建页面上根据需要即可分钟级拉起一个冷热分离架构的ES集群,方便快速,扩展性好,运维成本低
使用如下命令可以验证节点冷热属性
GET _cat/nodeattrs?v&h=node,attr,value&s=attr:desc node attr value node1 temperature hot node2 temperature hot node3 temperature warm node4 temperature hot node5 temperature warm ...
可以看到该集群为三热二冷的冷热分离集群(当然要注意如果其中有专用主节点或专用协调节点这类无法分配shard的节点,即使设置了冷热属性也不会有分片可以分配到其上)
业务方可以根据实际情况决定索引的冷热属性
PUT hot_data_index/_settings { "index.routing.allocation.require.temperature": "hot" }
PUT hot_data_index/_settings { "index.routing.allocation.require.temperature": "warm" }
创建索引
PUT hot_warm_test_index { "settings": { "number_of_replicas": 1, "number_of_shards": 3 } }
查看分片分配,可以看到分片均匀分配在五个节点上
GET _cat/shards/hot_warm_test_index?v&h=index,shard,prirep,node&s=node index shard prirep node hot_data_index 1 p node1 hot_data_index 0 r node1 hot_data_index 2 r node2 hot_data_index 2 p node3 hot_data_index 1 r node4 hot_data_index 0 p node5
设置索引为热索引
PUT hot_warm_test_index/_settings { "index.routing.allocation.require.temperature": "hot" }
查看分片分配,发现分片均分配在热节点上
GET _cat/shards/hot_warm_test_index?v&h=index,shard,prirep,node&s=node index shard prirep node hot_data_index 1 p node1 hot_data_index 0 r node1 hot_data_index 0 p node2 hot_data_index 2 r node2 hot_data_index 2 p node4 hot_data_index 1 r node4
设置索引为冷索引
PUT hot_warm_test_index/_settings { "index.routing.allocation.require.temperature": "warm" }
查看分片分配,发现分片均分配到冷节点上
GET _cat/shards/hot_warm_test_index?v&h=index,shard,prirep,node&s=node index shard prirep node hot_data_index 1 p node3 hot_data_index 0 r node3 hot_data_index 2 r node3 hot_data_index 0 p node5 hot_data_index 2 p node5 hot_data_index 1 r node5
从ES6.6开始,Elasticsearch提供索引生命周期管理功能,索引生命周期管理可以通过API或者kibana界面配置,详情参考[[index-lifecycle-management], 本文仅通过kibana界面演示如何使用索引生命周期管理结合冷热分离架构实现索引数据的动态管理。
kibana中的索引生命周期管理位置如下图(版本6.8.2):
点击创建create policy,进入配置界面,可以看到索引的生命周期被分为:Hot phrase
,Warm phase
, Cold phase
,Delete phrase
四个阶段
冷热分离架构是Elasticsearch的经典架构之一,使用该架构用户可以在保证热数据良好读写性能的同时,仍可以存储海量的数据,极大地丰富了ES的应用场景,解决了用户的成本问题。再结合ES在6.6推出的索引生命周期管理,使得ES集群在使用性和自动化方面表现出色,真正地解决了用户在性能,存储成本,自动化数据管理等方面的问题。