ES VS CH,成本太高,效率太低?不存在的

2020-12-08 14:22:18 浏览数 (2)

今天无意间一个客户问到CH和ES对比的问题。通常来说,ES并不是一个应该和CH进行横向比较的产品,ES是用综合数据库,一个大数据系统,一个搜索引擎,而CH是一个列式存储数据库的管理系统,两者最主要的使用场景并不特别重合。但因为ES出色的检索性能和丰富的数据分析能力,在数据分析产品预算有限的情况下,会有不少客户选择直接将ES用在OLAP分析的场景,而不是再额外部署一个OLAP系统,因此,自然免不了要被拿出来和CH作比较。

比较就比较吧,虽然在OLAP分析领域以列式存储库为主,ES并不是为这个场景而生的(You know, for search), 但综合各方面的能力之后,既然有很多用户选择使用ES作为OLAP分析工具,那我们就应该给大家以合适的指引。

特别是当互联网上出现如下这些描述不当的文章可能给人以误解时,我们有必要去做一些澄清。

文章原文我放在这,携程ClickHouse日志分析实践,分享实践是好的,但做产品比较以及下定论这种事,还是要严谨,要慎重。

这里我也不做一一的纠正,也不去做ES和CH的比较。我只是针对其中的一些表述,帮经常使用ES的同学来进行一些解惑

ES占用内存多?

ClickHouse相对ES占用更少的内存。 ES为了提高查询效率会将很多数据放在内存中,如:segment的索引数据、filter cache、field data cache、indexing buffer等;ES内存的使用量与索引量、数据量、写入量、查询量等成正比。删除(下线)索引、迁移索引或者扩容是应对ES内存问题的常用手段。但是删除(下线)索引导致用户希望保存更长时间数据的需求无法满足,而服务器扩容导致又了成本上升。 ClickHouse的内存消耗主要包括内存型的engine,数据索引,加载到内存中待计算的数据,搜索的结果等。在ClickHouse中日志的数据量和保存时间主要和磁盘有关。

我们可以看一下这里的描述,似乎侧面的意思同样的数据,ES的内存使用效率会更低?其实并不是。

  • 首先,ES是运行在JVM上的应用程序,而CH是C 编译的可运行程序,两者内存使用和管理的机制不同,不宜直接横向比较。(当然,看起来C 没有JVM的负担,貌似有丢丢优势,但如果垃圾回收没做好,也是有不能合理利用资源的可能的,这块水太深,不聊)
  • 其次,ES不是专门用于OLAP分析的列式库,整个ES节点上面运行了大量的额外功能去支持其他复杂的场景,并不是所有的资源都用于快速读/写,比如,ES对高并发的支持就比CH好很多ES也能做Schema On Read的运行时字段提取和操作。因此,在场景和功能特性不同的情况下,也不适宜做内存的比较
  • 再有,ES有大量的其他数据结构,比如倒排索引支持全文检索和模糊查询、BKD tree支持快速的range排序和地理位置边界查询,将这些数据加载到内存用于加速搜索无可厚非,最主要的是,这些数据是否生成,是否加载都是可控的,用户需要知道合理的数据建模,而不是让系统自动推测数据类型

就好比方说,比较BMW的混动7系和常规动力的丰田陆巡的油耗,得出结论BMW在省油方面做得比丰田强,看似结论成立,但实际上如果不考虑场景和用法,并没有多大的指导意义

Off-Heap

另外,这里说的内存到底是指的JVM使用的内存,还是把整个系统的内存都算到ES上面呢?如果大家了解过最新版本上的Off-Heap的特性,其实新版本的ES在内存使用上已经有很非常巨大的改进:

在这里插入图片描述在这里插入图片描述

结合7版本最新的内存断路器,在最新版本上,已经很少听到用户反馈说 “ES中一个大查询导致OOM的问题”

因此,这里非常重要的一个点,要把ES升级到最新的版本

ES占用存储多?

在这里插入图片描述在这里插入图片描述

熟悉ES的同学应该知道,ES提供了大量的选项可以控制生成数据的大小的,并且不同版本的ES性能和效率上也有不同,这里并没有给出ES具体的版本,也没有给出日志和mapping,所以很容易让人产生误导

ES指标型数据存储压缩

这里,我做了个测试,我用这样的一个数据集来测试:

在这里插入图片描述在这里插入图片描述

原始数据1000多万行,1.4GB。

数据样本如下:

代码语言:txt复制
{"geonameid": 2986043, "name": "Pic de Font Blanca", "latitude": 42.64991, "longitude": 1.53335, "country_code": "AD", "population": 0}
{"geonameid": 2994701, "name": "Roc Mélé", "latitude": 42.58765, "longitude": 1.74028, "country_code": "AD", "population": 0}
{"geonameid": 3007683, "name": "Pic des Langounelles", "latitude": 42.61203, "longitude": 1.47364, "country_code": "AD", "population": 0}
{"geonameid": 3017832, "name": "Pic de les Abelletes", "latitude": 42.52535, "longitude": 1.73343, "country_code": "AD", "population": 0}
{"geonameid": 3017833, "name": "Estany de les Abelletes", "latitude": 42.52915, "longitude": 1.73362, "country_code": "AD", "population": 0}
{"geonameid": 3023203, "name": "Port Vieux de la Coume d’Ose", "latitude": 42.62568, "longitude": 1.61823, "country_code": "AD", "population": 0}
{"geonameid": 3029315, "name": "Port de la Cabanette", "latitude": 42.6, "longitude": 1.73333, "country_code": "AD", "population": 0}
{"geonameid": 3034945, "name": "Port Dret", "latitude": 42.60172, "longitude": 1.45562, "country_code": "AD", "population": 0}
{"geonameid": 3038814, "name": "Costa de Xurius", "latitude": 42.50692, "longitude": 1.47569, "country_code": "AD", "population": 0}
{"geonameid": 3038815, "name": "Font de la Xona", "latitude": 42.55003, "longitude": 1.44986, "country_code": "AD", "population": 0}

这里需要指明的是,这个数据样本因为是geo name,因此每一个字段的值都不可能重复的,这个数据样本对于列式存储的来说,基本没有什么优化空间(因为列值全部不一样)。

使用如下mapping来构建ES的索引,0副本1分片:

代码语言:txt复制
{
  "settings": {
    "index.number_of_replicas": 0,
    "index.number_of_shards": 1

  },
  "mappings": {
      "dynamic": "strict",
      "properties": {
        "geonameid": {
          "type": "long"
        },
        "name": {
          "type": "text"
        },
        "latitude": {
          "type": "double"
        },
        "longitude": {
          "type": "double"
        },
        "country_code": {
          "type": "text"
        },
        "population": {
          "type": "long"
        }
      }
    }
}

最终生成的索引数据在1.6GB左右浮动,数据膨胀比例大概是1.2 (1.6G/1.4G),文件分布为:

在这里插入图片描述在这里插入图片描述

以上是不做任何场景建模优化的数据。

如果我们只是做OLAP的数据统计分析,不做字段内的搜索,我们可以按照列式存储的场景来优化mapping:

  • "enabled": false, 不需要获取原值,只要排序和统计结果{ "settings": { "index.number_of_replicas": 0, "index.number_of_shards": 1 }, "mappings": { "dynamic": "strict", "_source": { "enabled": false }, "properties": { "geonameid": { "type": "keyword", "index": false }, "name": { "type": "keyword", "index": false }, "latitude": { "type": "double", "index": false }, "longitude": { "type": "double", "index": false }, "country_code": { "type": "keyword", "index": false }, "population": { "type": "long", "index": false } } } }最终生成的索引文件比较为:
  • "index": false, 不需要字段内检索,只要排序和统计结果
  • fdt, The stored fields for documents, 变小了
  • tim,tim,倒排索引,变小了
在这里插入图片描述在这里插入图片描述

注意,因为cfs文件的存在(一个 "虚拟/组合 "文件,由所有其他索引文件组成,用于预防耗尽文件句柄,而特意合并的大文件) ,很难直观比较不同索引文件的变化,以下是针对cfs文件的介绍:

在这里插入图片描述在这里插入图片描述

话归正题,现在的存储值由1.6G变为了1G,优化过后,膨胀比变为了0.7,因此,ES,并非那么的没有效率,只是看是否针对应用场景正确的配置了数据模型

我们在存储成本优先的情况下,甚至可以配置"index.codec": "best_compression", 获得更高的压缩比:

  • 约为0.6 (0.8 / 1.4 ) 在这里插入图片描述在这里插入图片描述 要注意,这个场景里的值都是不一样的,因此,即便是CH也不会有太多的压缩空间,而携程帖子里的CDN日志等,可能有些字段会包含相同的信息,比如:某个字段的值都是AAAA,那这些字段就可以被列式存储有效压缩,这时如果ES设置了合理的字段,应该不会和CH差距特别的大。(区别肯定是有的,比如fdt等字段,要用于reindex等操作)

ES日志数据存储压缩

我们再来看看常规的日志,这是一个ES自身的GC日志,这里就有不少的重复值,比如5935,safepoint,gc

  • 日志有118428行,大小是12M 在这里插入图片描述在这里插入图片描述
  • 我们可以快速的用新版的机器学习功能进行日志解析: 在这里插入图片描述在这里插入图片描述
  • 它会生成建议的Grok模式,默认基本是字段都提取:
在这里插入图片描述在这里插入图片描述
  • 然后摄入数据:
在这里插入图片描述在这里插入图片描述
  • 生成的索引大小是6.5mb,在不做任何优化的情况下,压缩比率将近0.5,可见,文本类型比较多的索引,ES的压缩比率还要高一些
在这里插入图片描述在这里插入图片描述
  • 我们可以对同一个数据做优化,我们不提取字段,全部保存在message当中,把index: false配置一下,可以看到索引大小缩小为3.3mb,几乎是原始数据的1/4
在这里插入图片描述在这里插入图片描述

因此,如果我们愿意,我们可以有不同的方式来平衡存储成本和查询性能

ES Schema On Read

通过上面提到的方式,我们几乎只存储原始数据的情况下,可以大幅压缩生成索引的大小(原数据的1/4)。特别是面对一些不是特别频繁做查询和分析的场景,我们先把日志采集到ES,留待将来分析,在将来需要时,动态提取字段,再进行分析

通过这种方式,我们可以:

  • 大幅减少存储成本
  • 提高数据摄入速度
  • 更加灵活的分析数据
在这里插入图片描述在这里插入图片描述

以下是即将发布的新的runtime类型:

在这里插入图片描述在这里插入图片描述

Searchable Snapshot

另外一个和存储大小,成本有关的特性是ES上的新的Searchable Snapshot,通过该功能,我们可以:

  • 节省一半Cold层的成本
  • 以对象存储的成本,存储历史数据,并且可以检索 在这里插入图片描述在这里插入图片描述 也就是说,新的ES架构,可以以低得多的成本,支撑海量数据的存储和查询 在这里插入图片描述在这里插入图片描述

ES的读写速度

我非常的好奇这里的查询场景,ClickHouse经过优化后耗时29.5s,这已经是一个比较不能接受的值了。但上面说,ES直接查不出来,这里,我看到了挺多的问题。因为本身Netflow的日志格式并不复杂,在运维场景下,即便数据量很大,也不应该查不出来

在这里插入图片描述在这里插入图片描述

首先,第一个问题是不合理的ES使用: ES使用的是40核256G的服务器,一台服务器部署一个ES实例,单服务器数据量3T左右,熟悉ES的同学都知道,JVM的内存是有限制的,31G再往上就效率非常低了,在这个例子里面,如果这么大的一台服务器,只给JVM分配了31G内存,然后剩下的内存空载在那不让ES使用,想查得快也很难,通常这样配置的服务器,我们会在上面安装4个ES的节点。在这样的配置上说ES的成本高,性能低,是非常不合理的。

在这里插入图片描述在这里插入图片描述

可以看到的公开压测数据如下:(注意,这里没有针对列式存储场景的优化,同样生成倒排索引)

在这里插入图片描述在这里插入图片描述

其次,还是ES版本问题,ES的新版本本身就比老版本在一些指标、日志场景上快了几倍到上百倍不等,在一个“错误”的配置上去,再使用老版本的ES去比较CH的话,得出来的结论可能没那么有意义

在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述

ES慢速查询的能力

其实现在各个大数据系统,没有范式革命的产品,有的就是在资源、性能、成本、规模上的互有取舍。有时,我们不一定强调快,能在更大的数据集上运行查询也是一个能力,通过ES最新的async search功能,结合searchable snapshot,ES集群可以对数十PB存在在对象存储上的数据进行检索,单次运行默认可以查询5天之久,因此,单纯比较某些场景查得快不一定能满足所有的需求,比如全量的数据审计。

在这里插入图片描述在这里插入图片描述

ES的运维管理

这里不说太多,可能还是因为版本太低的原因

在这里插入图片描述在这里插入图片描述

ES ILM和data stream

没有了解过新版本上的ILM和data stream功能的可以了解下,ES的新版本一直在持续优化数据治理的能力,并简化操作。通过ILM,我们可以自动化的实现数据的迁移,而通过data stream,我们可以更轻松的管理数据流,而不是一个个的索引

ES SQL

在这里插入图片描述在这里插入图片描述

总结

这篇文章并非打算引战ES和CH社区。全文我几乎没有比较过ES和CH,这里,重申一下

这里的重点是:

  1. ES是一个综合数据库和大数据产品,整个Elasitc Stack有:
    • 数据摄入层 (beats, logstash)
    • 有数据规范(ECS)
    • 有机器学习功能
    • 有数据展示层 (kibana)
    • 有解决方案

因此,在做产品或者架构比较的时候,应该把ES看做是一个平台,而不是单一用途的产品,这样会有助于降低成本,提升投资回报率

  1. ES的产品一直在快速的迭代和更新,希望大家能够多了解新版本上的特性,这非常有助于成本的优化,性能的提升和运维的简化
  2. 当数据量过多,集群过多的时候,对ES的运维管理力不从心,可以联系一下售后和咨询,帮忙解决问题,毕竟直接替换产品,重入新坑,成本过高

0 人点赞