一、Elasticsearch 简介

1. 什么是Elasticsearch

Elasticsearch是一个基于Lucene的搜索服务器。它基于RESTful web接口提供了一个分布式多用户能力的全文搜索引擎。Elasticsearch是用Java语言开发的,是一种目前流行的企业级搜索引擎。

2. Elasticsearch特点

近乎实时搜索、稳定、可靠、快速、安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby、Go和许多其他语言中都是可用的。可以作为一个大型分布式集群(数百台服务器)技术,处理PB级数据,服务大公司;也可以运行在单机上,服务小公司。

3. 应用场景

  • 类似于维基百科和百度百科,手机维基百科,全文检索,高亮,搜索推荐等场景。
  • 电商网站,检索商品的功能。
  • 日志数据分析(金融,证券等行业常见),Filebeat采集日志,Logstash清洗日志,Elasticsearch查询分析日志(ELK技术,Elasticsearch+Filebeat+Logstash+Kibana)。
  • The Guardian(国外新闻网站),类似搜狐新闻,用户行为日志(点击、浏览、收藏、评论)+社交网络数据、数据分析,给到每篇新闻文章的作者,让他知道他的文章的公众反馈(好、坏、热门、垃圾、鄙视、崇拜)。

二、金融机构 Elasticsearch 应用场景介绍与架构选型思路

1. 金融机构Elasticsearch应用场景介绍

  • 金融机构Elasticsearch使用场景多见于如下场景:机构的日志中心系统,使用Elasticsearch进行日志存储及查询;
  • 业务侧AI模型的数据分析类系统,使用Elasticsearch提供系统所采集到的用户行为日志进行数据分析及模型训练;
  • 金融机构的影像平台系统,使用Elasticsearch存储非结构化数据的元数据,再将对应的非结构化数据存储至底层存储介质,并提供查询以及分析的功能,如:音视频流数据,办公文档类数据;
  • 金融机构自身的电商类业务系统,使用Elasticsearch提供商品信息的检索功能。

2. 金融机构Elasticsearch架构选型

(1)各应用场景下Elasticsearch架构选型简介

以上只列举了部分应用场景,不同场景下Elasticsearch的架构选型及优化思路的侧重点亦不同。

  • 日志类系统,侧重点在于日志写入Elasticsearch是否存在堆积,查询是否可近实时进行查询。故而在进行架构选型时,应侧重于底层存储介质(SSD、SATA、对象存储)、日志保存策略、日志查询间隔等方面考虑。构建多大规模的Elasticsearch集群,需要考虑是否要做角色分离、是否进行数据的生命周期管理、是否调整数据刷新落盘时间间隔等措施。
  • AI模型的分析类系统,侧重点在于对Elasticsearch中日志的分析。在笔者接触到的场景中,较多的是将Elasticsearch当做了数据源以及分析平台。此时在进行架构选项时,应侧重于对数据Mapping结构的建模。好的建模有利于提高模型训练的准确度,Elasticsearch集群的整体架构倒无明显可讲之处。
  • 影像平台类系统,侧重点在于存储非结构化数据的元数据,成为连接元数据与底层存储之间的桥梁,相较于数据写入而言,更注重数据的查询。此时在做架构选项时,应对Elasticsearch集群中的节点进行角色分离,以及数据冷热分离,以提供更好的数据查询服务。
  • 电商类系统,侧重点就是商品的查询。此时在做架构选型时,应将数据Mapping建模放在首要位置。好的建模,能帮用户更好的检索出需要的商品,提高用户的体验度。其次需对Elasticsearch集群中的节点进行角色分离,使用专门的节点协调查询流量,充当网关的作用。

(2)日志中心Elasticsearch架构选型

以日志中心系统为例,简单介绍一下针对于该系统曾经做过的架构选型思路以及调优策略。首先,需明确日志中心也有细微区别,大致分为两类:

  • 实时写入及查询,日志中心实时进行日志写入,并对外开放查询功能;
  • 分段写入及查询,日志中心夜晚统一进行日志跑批写入,白天对外开放查询功能。

实时写入及查询场景下,架构选型分析

该场景由于涉及到实时写入,故而写入流量从全天维度来看,集群整体CPU,内存的资源使用情况会保持较高的一个情况,故而在规划时,应对集群各节点进行角色区分,以曾经规划过的集群架构为例:

节点角色角色描述节点数量CPU内存存储存储介质
仲裁节点协调集群选举12C6G50G对象存储
协调节点请求集群的流量处理,网关224C54G50G对象存储
主节点协调技术集群内部资源的分配224C54G50G对象存储
数据节点存储数据824C54G2T对象存储

针对实时写入及查询的场景,在进行架构规划时,设计了两个协调节点的角色,elastic-es-zone-a-coord,elastic-es-zone-b-coord,协调节点相当于是集群的统一网关,发起数据写入请求时,优先通过协调节点进行流量处理,而非直接将请求打到数据节点上,给数据节点较大负载压力。

同样的,查询时,查询请求也会预先经过协调节点进行流量处理,不会直接请求数据节点,数据节点只扮演存储数据以及提供数据的角色。

在此种架构,未做任何集群调优策略的情况下,在面对日增量在10亿级日志数据时,写入速率在每秒10W-12W条数据左右,数据写入无延迟,数据查询也能做到近实时查询,秒级响应。各节点系统资源使用情况如下(此处统计的为日常情况下的均值,仅供参考):

节点角色角色描述CPU 利用率内存利用率
协调节点请求集群的流量处理,网关60%60-70%
主节点协调技术集群内部资源的分配60-70%70%
数据节点存储数据50%50-60%

分段写入及查询场景下,架构选型分析

该场景因为日志的高峰期数据只会在夜晚固定时间段内进行大量写入,故而在一定时间段内,集群的各节点压力都比较大,白天仅提供查询,此时集群的整体压力较轻,仅有涉及查询流量的协调及数据节点压力较大。故而在做架构设计时,相较之前的架构,增加了两个协调节点的角色。

节点角色角色描述节点数量CPU内存存储存储介质
仲裁节点协调集群选举12C6G50G对象存储
协调节点请求集群的流量处理,网关424C54G50G对象存储
主节点协调技术集群内部资源的分配224C54G50G对象存储
数据节点存储数据824C54G2T对象存储

在之前的基础上又增加了两个协调节点,负责应对处理晚上瞬间大流量数据的写入。

此外,增加两个协调节点,在应付白天大量数据查询场景时,也能做到更快的响应。在此种架构,未做任何集群调优策略的情况下,在面对半夜批量写入10亿级别日志数据时,写入速率在每秒15W-20W条数据左右,数据写入无延迟,数据查询也能做到近实时查询,毫秒级响应。各节点系统资源使用情况如下(此处统计的为日常情况下的均值,仅供参考):

节点角色角色描述CPU 利用率内存利用率
协调节点请求集群的流量处理,网关50%50-70%
主节点协调技术集群内部资源的分配

夜间写入60-70%

白天查询 40-50%

夜间数据写入 70%

白天查询 40-50%

数据节点存储数据50%50-60%

3. Elasticsearch 集群 API 层面性能调优

针对上述两种应用场景,也总结出对应的Elasticsearch集群在API层面的调优策略

(1)实时写入及查询场景下,API层面调优策略

  • 设置索引模板,单副本,分片数根据当时的场景设置为4分片
PUT template/event template?includetype_name=true
{
 "order": 2,
"index patterns": "event-*"
"settings":{
"number of shards": 4,
"number_of_replicas": 1,
}
  • 设置索引滚动更新以及生命周期策略,由于为实时写入查询,设置过长的生命周期策略容易占用过的集群资源。故而笔者只采用了hot-delete阶段,这样可以方便快速的对数据进行迭代更新,单条索引大小达到阈值后,滚动更新成新的索引,索引存在7天后,自动删除。
PUT ilm/policy/7days
{
"policy" : {
  "phases" :{
    "hot" : { 
      "min_age" :"Oms"
      "actions" : {
        "rollover" :{
           "max_size" :"50gb"
           "max_age" :"7d"
        },
      "set_priority" : {
        "priority" : 100
      }
    }
  },
  "delete": {
     "min_age":"7d",
    "actions" : {
        "delete":{ }
     }
   }
  }
 }
}
  • 默认为同步写入,修改为异步写入数据
index.translog.durability: async
  • 增加数据刷盘时间间隔,由于实时写入,同时还需要承担一部分查询的功能,且对于查询的诉求可以接受延迟2分钟左右,即查询2分钟前的日志,故而进行如下调整:
index.translog.sync interval: 120s
  • 增大indexing buffer,indexing buffer是为doc建立索引时使用的,当缓存满时会生成一个新的segment并进行刷盘,默认大小为JVM堆内存大小的10%,对于shard来讲,实际最大使用到512mb就够了,笔者在该场景下,适当增大到20%左右。
indices.memoryindex buffer size=20%
  • 降低fielddata缓存,默认是无限制的,在实时写入查询的场景下,如果不限制,容易导致频繁的OOM。
indices.fielddata.cache.size:20%
  • 修改线程池,对于查询以及写入的队列大小进行调整
# Search pool
#thread_pool.search.size: 5
thread_pool.search.queue size:100

# Write pool
# thread_pool.bulk.size: 16
thread pool.bulk.queue size: 300
  • 修改段合并线程数量为1,段合并会占用大量 IO,故而对其进行调整
index.merge.scheduler.max thread_count=1
  • 进行批量写入,请求端使用Elasticsearch提供的bulk API进行批量写入,防止小文件频繁请求写入,造成IO堵塞。

(2)分段写入及查询场景下,API 层面调优策略

  • 因为是夜晚批量写入日志,几乎无查询场景,故而此时索引副本数设置为0。跑批结束后,因为白天为查询场景,故而将副本数设置为原定副本数,可提高查询效率。
PUT template/event template?includetype_name=true
{
 "order": 2,
 "index_patterns":"event-*"
 "settings": {
    "number_of_shards": 4, 
    "number_of_replicas": 0,
}

该场景下,因为数据需要进行归档处理,将生命周期策略调整为 hot-cold-delete 三个阶段,数据在经过 cold 阶段后,会归档到日志中心专用的存储介质,通常为对象存储。

4. Elasticsearch 集群存储层面选择

经实践,Elasticsearch在不同的存储介质下,读写性能差异很大,且在存储介质已决定的情况下,API 层面如何调优,效果都很有限。以下是经过测试后得到的数据,两种架构场景下不同存储介质写入速率对比:

API 调优前写入(NAS )API调优后写入(NAS )API调优前写入(对象存储)API调优后写入(对象存储)
实时写入及查询场景
1.4w-1.6w/s2.0w-2.5w/s10w-12w/s15w-20w/s
分段写入及查询场景
2.0w-3.0w/s3.5w-5.0w/s15w-20w/s25w-45w/s

 三、结语

通过上述表中数据可以看到,不同场景下,影响数据写入的主要因素,一个是存储介质,一个是调优策略。但存储介质的影响更为关键,在实际实践场景里,只用到过NAS与对象存储,故而此处给出了二者的相关数据,可以看出,对象存储的写入速率是要比 NAS 的快很多的。

但 Elasticsearch 尤为推荐 SSD 存储,也曾看过 SSD 场景下,数据写入的情况,若是同样集群配置的情况下,写入速率是会是每秒百万级。此时,限制写入能力的,会是 CPU 跟内存,但是由于成本原因,很多金融领域行业并不能选择此类存储,不失为一种遗憾。

除此之外,Elasticsearch 本身的生命周期策略设置,需要根据实际情况去定。比如在实时场景下,由于数据要做到近实时,集群的主要压力就在于写入与查询,此时若设置过长的生命周期阶段,集群会消耗过的内存与 CPU 资源,去定时更新索引的生命周期状态,反而会影响数据的写入,得不偿失。故而生命周期策略就设置为 hot-delete,在保证场景诉求的同时,亦能满足索引数据管理的需要。

而在分段写入场景下,由于写入与查询是分开进行的,此时集群的压力是有限的,再加上该场景有数据归档要求,故而生命周期策略设置为 hot-cold-delete,数据在 cold 阶段会批量归档到对象存储中,归档完毕后,执行 delete 阶段,自动清理掉数据。由此观之,不同场景下,优化数据写入需考虑的因素是极多的,场景的诉求,硬件资源的限制,API 层面的调整,甚至还可能有网络层面带宽的限制,等方方面面都会影响到数据的写入。