ClickHouse与Elasticsearch比较总结

news2025/2/28 12:32:08

目录

背景

分布式架构

存储架构

写入链路设计

Elasticsearch

再谈Schemaless

查询架构

计算引擎

数据扫描

再谈高并发

性能测试

日志分析场景

access_log(数据量197921836)

trace_log(数据量569816761)

官方Ontime测试集

用户画像场景(数据量262933269)

二级索引点查场景(数据量1000000000)

数据导入性能对比

结语

优点

缺点

ClickHouse替换ES的可行性方案

参考链接


背景

Clickhouse是俄罗斯搜索巨头Yandex开发的完全列式存储计算的分析型数据库。ClickHouse在这两年的OLAP领域中一直非常热门,国内互联网大厂都有大规模使用。

Elasticsearch是一个近实时的分布式搜索分析引擎,它的底层存储完全构建在Lucene之上。简单来说是通过扩展Lucene的单机搜索能力,使其具有分布式的搜索和分析能力。Elasticsearch通常会和其它两个开源组件Logstash(日志采集)和Kibana(仪表盘)一起提供端到端的日志/搜索分析的功能,常常被简称为ELK。

分布式架构

Elasticsearch和ClickHouse都是支持分布式多机的数据产品,这里作者首先要比对的就是两者的分布式架构差异,分布式结构设计对产品的易用性和可扩展性具有非常重要的影响。在分布式架构上,核心要解决的几个问题包括:节点发现、Meta同步、副本数据同步。Elasticsearch作为一个老牌的开源产品,在这块上做的相对比较成熟。原生的节点发现、Meta同步协议,给用户非常好的易用性体验。Elasticsearch的Meta同步协议需要解决的问题其实和开源的Raft协议非常相似,只不过在Elasticsearch诞生的时候还没有Raft出现,所以就只能自己动手搞一个了。经过这么多年的打磨,Elasticsearch的Meta同步协议也是相当成熟了。依托于此,Elasticsearch具有非常易用的多角色划分,auto schema inference等功能。值得一提的是Elasticsearch的多副本数据同步,并没有复用Meta同步协议,而是采用传统的主备同步机制,由主节点负责同步到备节点,这种方式会更加简单高效。

ClickHouse的分布式架构能力相对会简单一些,这也是因为ClickHouse还是一个比较年轻的开源产品,还处在分布式易用性不断迭代上升的阶段。ClickHouse引入了外置的ZooKeeper集群,来进行分布式DDL任务(节点Meta变更)、主备同步任务等操作的下发。多副本之间的数据同步(data shipping)任务下发也是依赖于ZooKeeper集群,但最终多副本之间的数据传输还是通过Http协议来进行点对点的数据拷贝,同时多副本都可写,数据同步是完全多向的。至于节点发现,ClickHouse目前都没有这方面的能力,都是需要通过手动配置集群节点地址来解决。ClickHouse目前这种脚手架式的分布式架构,导致它具有极强的灵活部署能力和运维介入能力,对用户的易用性略差,用户门槛相对较高,但是在能力上限方面,ClickHouse的分布式部署扩展性并没有短板,集群规模上限对比Elasticsearch没有差异。ClickHouse架构扁平,没有前端节点和后端节点之分,可部署任意规模集群。同时ClickHouse在多副本功能上有更细粒度的控制能力,可以做到表级别副本数配置,同一物理集群可划分多个逻辑集群,每个逻辑机器可任意配置分片数和副本数。

存储架构

写入链路设计

Elasticsearch

写入吞吐能力是大数据场景下的一项核心指标,用户对大数据产品的要求不光是要存的下,还要写得快。这里首先介绍Elasticsearch的实时写入链路设计:在Elasticsearch的每一个Shard中,写入流程分为两部分,先写入Lucene,再写入TransLog。写入请求到达Shard后,先写Lucene内存索引,此时数据还在内存里面,接着去写TransLog,写完TransLog后,刷新TransLog数据到磁盘上,写磁盘成功后,请求返回给用户。这里有几个关键点,一是把写Lucene放在了最前面,主要是防止用户的写入请求包含“非法”的数据。二是写Lucene索引后,并不是可被搜索的,需要通过refresh把内存的对象转成完整的Segment后,然后再次reopen后才能被搜索,这个refresh时间间隔是用户可设定的。可以看出Lucene索引并没有写入实时可见的能力,所以Elasticsearch是一个近实时(Near Real Time)的系统。最后是每隔一段比较长的时间,比如30分钟后,Lucene会把内存中生成的新Segment刷新到磁盘上,刷新后索引文件已经持久化了,历史的TransLog就没用了,才会清空掉旧的TransLog。

Elasticsearch单Shard写入链路

ClickHouse单Shard写入链路

对比Elasticsearch的写入链路,ClickHouse的写入方式更加“简单直接”、极致,上面已经讲过Elasticsearch是一个近实时系统,内存存储引擎中新写入的数据需要定时flush才可见。而ClickHouse则是干脆彻底放弃了内存存储引擎这一功能,所有的数据写入时直接落盘,同时也就省略了传统的写redo日志阶段。在极高写入吞吐要求的场景下,Elasticsearch和ClickHouse都需要为了提升吞吐而放弃部分写入实时可见性。只不过ClickHouse主推的做法是把数据延迟攒批写入交给客户端来实现。另外在多副本同步上,Elasticsearch要求的是实时同步,也就是写入请求必须写穿多个副本才会返回,而ClickHouse是依赖于ZooKeeper做异步的磁盘文件同步(data shipping)。在实战中ClickHouse的写入吞吐能力可以远远超过同规格的Elasticsearch。

Segment vs DataPart

Elasticsearch和ClickHouse的存储设计外表上看起来非常相似,但能力却又截然不同。Elasticsearch的磁盘文件由一个个Segment组成,Segment实际上是一份最小单位的Lucene索引,关于Segment内部的存储格式这里不展开讨论。而Segment又会在后台异步合并,这里合并主要解决两个问题:1)让二级索引更加有序;2)完成主键数据变更。二级索引是一种“全局”有序的索引,全部数据构建到一个索引里面比构建到多个索引里对查询的加速更明显。Elasticsearch是支持主键删除更新的,这都是依托于Lucene索引的删除功能来实现的,更新操作会被转换成删除操作加写入操作。当Lucene索引的Segment里存在多条删除记录时,系统就需要通过Segment合并来剔除这些记录。在多个Segment进行合并的时候,Lucene索引中的存储数据表现出的是append-only的合并,这种方式下二级索引的合并就不需要进行“重排序”。

对比Elasticsearch中的Segment,ClickHouse存储中的最小单位是DataPart,一次批量写入的数据会落盘成一个DataPart。DataPart内部的数据存储是完全有序的状态(按照表定义的order by排序),这种有序存储就是一种默认聚簇索引可以用来加速数据扫描。ClickHouse也会对DataPart进行异步合并,其合并也是用来解决两个问题:1)让数据存储更加有序;2)完成主键数据变更。DataPart在合并存储数据时表现出的是merge-sorted的方式,合并后产生的DataPart仍然处于完全有序状态。依赖于DataPart存储完全有序的设定,ClickHouse实现主键数据更新的方式和Elasticsearch截然不同。Elasticsearch在变更主键时,采用的是“先查原纪录-生成新记录-删除原纪录-写入新纪录”的方式,这种方式完全限制住了主键更新的效率,主键更新写入和append-only写入的效率差异非常大。而ClickHouse的主键更新是完全异步进行的,主键相同的多条记录在异步合并的时候会产生最新的记录结果。这种异步批量的主键更新方式比Elasticsearch更加高效。

最后总结一下Segment和DataPart内部文件存储的能力差异,Segment完全就是Lucene索引的存储格式,Lucene索引在倒排文件上的存储毋庸置疑是做到极致的,Lucene索引同时也提供了行存、列存等不同格式的原数据存储。Elasticsearch默认都会把原数据存两份,一份在行存里,一份在列存里。Elasticsearch会根据查询的pattern,选择扫描的合适的存储文件。原生ClickHouse的DataPart中并没有任何二级索引文件,数据完全按列存储,ClickHouse实现的列存在压缩率、扫描吞吐上都做到了极致。相对而言Elasticsearch中的存储比较中庸,并且成本至少翻倍。

再谈Schemaless

讲到Elasticsearch的特性,大家都会提到Schemaless这个词,Elasticsearch可以自动推断写入数据的json-shema,根据写入数据的json-schema调整存储表的Meta结构,这可以帮助用户节省很多建表、加列的麻烦。但是在作者看来,Elasticsearch的这种能力其实叫auto schema inference更为恰当,这都得益于Elasticsearch的分布式Meta同步能力。而Elasticsearch的存储其实是需要schema的,甚至是强绑定schema的,因为它是以二级索引为核心的存储,没有类型的字段又如何能构建索引呢?真正的Schemaless应该是可以灵活高效变更字段类型,同时保证查询性能不会大幅下降的能力。今天用户想变更Elasticsearch index中的某个字段类型,那只有一种方法:就是把整份数据数据reindex。相对比,ClickHouse的存储反而不是强绑定schema的,因为ClickHouse的分析能力是以存储扫描为核心的,它是可以在数据扫描进行动态类型转换,也可以在DataPart合并的时候慢慢异步调整字段的类型,在查询的时候字段类型变更引起的代价也就是运行时增加cast算子的开销,用户不会感受到急剧的性能下降。作者认为Schemeless绝对不是Elasticsearch的护城河能力,相对反而是它的弱项。至于auto schema inference,这是对小规模用户非常友好的能力,但它永远不可能能帮用户创建出性能最佳的Schema,在大数据量场景下大家还是需要根据具体的查询需求来创建Schema,所有的便利最后都是有成本代价的。

查询架构

计算引擎

这里把ClickHouse和Elasticsearch摆在一起讲计算引擎其实有些荒谬的味道,因为Elasticsearch实现的只是一个通用化搜索引擎。而搜索引擎能处理的查询复杂度是确定的、有上限的,所有的搜索查询经过确定的若干个阶段就可以得出结果,但是计算引擎则不然。Elasticsearch虽然也有SQL支持的插件,但是这种插件的实现逻辑就是把简单的SQL查询翻译到确定的搜索模式上面。对于搜索引擎原来就不支持的数据分析行为,Elasticsearch-SQL也无济于事。另外Elasticsearch-SQL当前的翻译能力看起来并不是非常完备和智能,为了获得最高的搜索性能用户还是需要尝试Elasticsearch原生的查询API。对于习惯使用SQL的用户而言,Elasticsearch的查询API是完全陌生的一套体系,复杂查询非常难写。

Elasticsearch的搜索引擎支持三种不同模式的搜索方式:query_and_fetch,query_then_fetch,dfs_query_then_fetch。第一种模式很简单,每个分布式节点独立搜索然后把得到的结果返回给客户端,第二种模式是每个分布式存储节点先搜索到各自TopN的记录Id和对应的score,汇聚到查询请求节点后做重排得到最终的TopN结果,最后再请求存储节点去拉取明细数据。这里设计成两轮请求的目的就是尽量减少拉取明细的数量,也就是磁盘扫描的次数。最后一种方式是为了均衡各个存储节点打分的标准,先统计全局的TF(Term Frequency)和DF(Document Frequency),再进行query_then_fetch。Elasticsearch的搜索引擎完全不具备数据库计算引擎的流式处理能力,它是完全回合制的request-response数据处理。当用户需要返回的数据量很大时,就很容易出现查询失败,或者触发GC。一般来说Elasticsearch的搜索引擎能力上限就是两阶段的查询,像多表关联这种查询是完全超出其能力上限的。

ClickHouse的计算引擎特点则是极致的向量化,完全用c++模板手写的向量化函数和aggregator算子使得它在聚合查询上的处理性能达到了极致。配合上存储极致的并行扫描能力,轻松就可以把机器资源跑满。ClickHouse的计算引擎能力在分析查询支持上可以完全覆盖住Elasticsearch的搜索引擎,有完备SQL能力的计算引擎可以让用户在处理数据分析时更加灵活、自由。

数据扫描

ClickHouse是完全列式的存储计算引擎,而且是以有序存储为核心,在查询扫描数据的过程中,首先会根据存储的有序性、列存块统计信息、分区键等信息推断出需要扫描的列存块,然后进行并行的数据扫描,像表达式计算、聚合算子都是在正规的计算引擎中处理。从计算引擎到数据扫描,数据流转都是以列存块为单位,高度向量化的。而Elasticsearch的数据扫描如上一节所述,主要发生在query和fetch阶段。其中query阶段主要是扫描Lucene的索引文件获取查询命中的DocId,也包括扫描列存文件进行聚合计算。而fetch阶段主要是点查Lucene索引中的行存文件读取明细结果。表达式计算和聚合计算在两个阶段都有可能发生,其计算逻辑都是以行为单位进行运算。总的来说Elasticsearch的数据扫描和计算都没有向量化的能力,而且是以二级索引结果为基础,当二级索引返回的命中行数特别大时(涉及大量数据的分析查询),其搜索引擎就会暴露出数据处理能力不足的短板。

再谈高并发

很多用户谈到ClickHouse,都会有一个错误的映像,ClickHouse查询跑得快,但是并发不行。但这背后的原因其实是ClickHouse的并行太牛逼了,这是ClickHouse的一大强项,一个查询就可以把磁盘吞吐都打满,查询并行完全不依赖于shard,可以任意调整。不可否认处理并发请求的吞吐能力是衡量一个数据系统效率的最终指标,ClickHouse的架构上并没有什么天然的并发缺陷,只不过它是个耿直boy,查询需要扫描的数据量和计算复杂度摆在那,ClickHouse只是每次都老老实实计算而已,机器的硬件能力就决定了它的并发上限。ClickHouse的并发能力事实上是不错的,认为它并发不行是个误区。只是默认情况下ClickHouse的目标是保证单个query的latency足够低;部分场景下用户可以通过设置合适的系统参数来提升并发能力,比如max_threads等。反过来,在这里介绍一下为什么有些场景下Elasticsearch的并发能力会很好。首先从Cache设计层面来看,Elasticsearch的Cache包括Query Cache, Request Cache,Data Cache,Index Cache,从查询结果到索引扫描结果层层的Cache加速,就是因为Elasticsearch认为它的场景下存在热点数据,可能被反复查询。反观ClickHouse,只有一个面向IO的UnCompressedBlockCache和系统的PageCache,为什么呢?因为ClickHouse立足于分析查询场景,分析场景下的数据和查询都是多变的,查询结果等Cache都不容易命中,所以ClickHouse的做法是始终围绕磁盘数据,具备良好的IO Cache能力。其次回到数据扫描粒度,Elasticsearch具备全列的二级索引能力,这些索引一般都是预热好提前加载到内存中的,即使在多变的查询条件下索引查询得到结果的代价也很低,拿到索引结果就可以按行读取数据进行计算。而原生ClickHouse并没有二级索引的能力,在多变的查询条件下只能大批量地去扫描数据过滤出结果(阿里云ClickHouse已经具备二级索引能力,解决了这一问题,性能水平和Elasticsearch相当,后续性能测评部分会进行详细介绍)。但是Elasticsearch具备二级索引,并发能力就一定会好么?也不尽然,当二级索引搜索得到的结果集很大时,查询还是会伴随大量的IO扫描,高并发就无从谈起,除非Elasticsearch的Data Cache足够大,把所有原数据都加载到内存里来。

总结来说,Elasticsearch只有在完全搜索场景下面(where过滤后的记录数较少),并且内存足够的运行环境下,才能展现出并发上的优势。而在分析场景下(where过滤后的记录数较多),ClickHouse凭借极致的列存和向量化计算会有更加出色的并发表现。两者的侧重不同而已,同时ClickHouse并发处理能力立足于磁盘吞吐,而Elasticsearch的并发处理能力立足于内存Cache。ClickHouse更加适合低成本、大数据量的分析场景,它能够充分利用磁盘的带宽能力。

性能测试

ClickhouseElasticsearch节点数
CPU:8coreMemory:32GB存储:ESSD PL1 1500GBCPU:8coreMemory:32GB存储:ESSD PL1 1500GB4

日志分析场景

日志分析场景中选取了两个具有代表性的查询场景进行对比测试,结果如下所示。从结果分析来看ClickHouse和Elasicsearch在两个场景中的性能差距随着where条件过滤后的记录数增大而扩大,在数据量更大的trace_log场景中,两者的分析查询性能差距一目了然。Elasticsearch和ClickHouse完整版建表语句和查询下载:日志分析场景

access_log(数据量197921836)

ClickHouse中的建表语句如下:

CREATE TABLE access_log_local on cluster default
(
  `sql` String, 
  `schema` String, 
  `type` String, 
  `access_ip` String, 
  `conn_id` UInt32, 
  `process_id` String, 
  `logic_ins_id` UInt32, 
  `accept_time` UInt64, 
  `_date` DateTime, 
  `total_time` UInt32, 
  `succeed` String, 
  `inst_name` String
) 
ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(_date)
ORDER BY (logic_ins_id, accept_time);

CREATE TABLE access_log on cluster default as access_log_local
engine = Distributed(default, default, access_log_local, rand());

ClickHouse中的查询语句如下:

--Q1
select _date, accept_time, access_ip, type, total_time, concat(toString(total_time),'ms') as total_time_ms, sql,schema,succeed,process_id,inst_name from access_log where _date >= '2020-12-27 00:38:31' and _date <= '2020-12-28 00:38:31' and logic_ins_id = 502680264 and accept_time <= 1609087111000 and accept_time >= 1609000711000 and positionCaseInsensitive(sql, 'select') > 0 order by accept_time desc limit 50,50;
--Q2
select 
case 
when total_time <=100 then 1 
when total_time > 100 and total_time <= 500 then 2 
when total_time > 500 and total_time <= 1000 then 3 
when total_time > 1000 and total_time <= 3000 then 4 
when total_time > 3000 and total_time <= 10000 then 5 
when total_time > 10000 and total_time <= 30000 then 6 
else 7 
end as reorder, 
case 
when total_time <=100 then '0~100ms' 
when total_time > 100 and total_time <= 500 then '100ms~500ms' 
when total_time > 500 and total_time <= 1000 then '500ms~1s' 
when total_time > 1000 and total_time <= 3000 then '1s~3s' 
when total_time > 3000 and total_time <= 10000 then '3s~10s' 
when total_time > 10000 and total_time <= 30000 then '10s~30s' 
else '30s以上' 
end as label, 
case 
when total_time <= 100 then '0~100' 
when total_time > 100 and total_time <= 500 then '100~500' 
when total_time > 500 and total_time <= 1000 then '500~1000' 
when total_time > 1000 and total_time <= 3000 then '1000~3000' 
when total_time > 3000 and total_time <= 10000 then '3000~10000' 
when total_time > 10000 and total_time <= 30000 then '10000~30000' 
else '30000~10000000000' 
end as vlabel, 
count() as value
from access_log
where logic_ins_id = 502867976 and _date >= '2020-12-27 00:38:31' and _date <= '2020-12-28 00:38:31' and accept_time <= 1609087111000 and accept_time >= 1609000711000 
group by label,vlabel,reorder 
order by reorder;
--Q3
select toStartOfMinute(_date) as time, count() as value 
from access_log 
where logic_ins_id = 500152868 and accept_time <= 1609087111000 and accept_time >= 1609000711000  
group by time 
order by time;
--Q4
select count(*) as c from (
  select _date, accept_time, access_ip, type, total_time, concat(toString(total_time),'ms') as total_time_ms, sql, schema, succeed, process_id, inst_name 
  from access_log 
  where logic_ins_id = 501422856 and _date >= '2020-12-27 00:38:31' and _date <= '2020-12-28 00:38:31' and accept_time <= 1609087111000 and accept_time >= 1609000711000
);

性能对比如下:

trace_log(数据量569816761)

ClickHouse中的建表语句如下:

CREATE TABLE trace_local on cluster default
(
  `serviceName` LowCardinality(String), 
  `host` LowCardinality(String), 
  `ip` String, 
  `spanName` String, 
  `spanId` String, 
  `pid` LowCardinality(String), 
  `parentSpanId` String, 
  `ppid` String, 
  `duration` Int64, 
  `rpcType` Int32, 
  `startTime` Int64, 
  `traceId` String, 
  `tags.k` Array(String), 
  `tags.v` Array(String), 
  `events` String,
  KEY trace_idx traceId TYPE range
) ENGINE = MergeTree() 
PARTITION BY intDiv(startTime, toInt64(7200000000)) 
PRIMARY KEY (serviceName, host, ip, pid, spanName) 
ORDER BY (serviceName, host, ip, pid, spanName, tags.k);

CREATE TABLE trace on cluster default as trace_local
engine = Distributed(default, default, trace_local, rand());

ClickHouse中的查询语句如下:

--Q1
select *
from trace
prewhere
traceId ='ccc6084420b76183'
where startTime > 1597968000300000  and startTime <  1598054399099000 settings max_threads = 1;
--Q2
select count(*) count, spanName as name from trace
where serviceName ='conan-dean-user-period'
and startTime > 1597968000300000  and startTime <  1598054399099000
group by spanName
order by count desc limit 1000;
--Q3
select host as name, count(*) count
from trace
where serviceName ='conan-dean-user-period'
and startTime > 1597968000300000  and startTime <  1598054399099000
group by host;
--Q4
select count(*) count, tags.k as name  from trace
array join tags.k
where serviceName ='conan-dean-user-period'
and startTime > 1597968000300000  and startTime <  1598054399099000
group by tags.k;
--Q5
select count(*) spancount, 
sum(duration) as sumDuration, intDiv(startTime, 1440000000) as timeSel
from trace
where serviceName ='conan-dean-user-period'
and startTime > 1597968000300000  and startTime <  1598054399099000
group by timeSel;
--Q6
select count(*) spanCount, 
countIf(duration  <=1000000), countIf(duration > 1000000),  countIf(duration > 3000000)
from trace
where serviceName ='conan-dean-user-period'
and startTime > 1597968000300000  and startTime <  1598054399099000;
--Q7
select  host, startTime,traceId,spanName,duration,tags.k,tags.v
from trace
where serviceName ='conan-dean-user-period'
and startTime > 1597968000300000  and startTime <  1598054399099000 limit 1000000;

性能对比如下:

官方Ontime测试集

Ontime测试集是ClickHouse官网上推荐的一个分析型查询benchmark,为了更加公证公开地对比ClickHouse和Elasticsearch在分析查询上的性能差异。作者也引入了这个数据集进行测试比对,结果如下所示,ClickHouse在纯分析型查询场景下具有巨大性能优势。Elasticsearch和ClickHouse完整版建表语句和查询下载:聚合分析场景

用户画像场景(数据量262933269)

用户画像场景也是用户比较难选择使用Elasticsearch还是ClickHouse的一个典型场景,该场景的具体特点是超大宽表,大批量更新写入,查询返回的数据量大,筛选条件复杂多变。用户在使用Elasticsearch时遇到的难点问题主要有两个:数据写不进去,导入慢;数据拉不出来,返回大规模明细数据非常慢。针对这个场景,作者根据真实用户的业务场景,mock了一张接近150列的大宽表进行相关的查询测试,具体的查询如下所示,每条查询返回的结果集在10万到100万行级别。Elasticsearch和ClickHouse完整版建表语句和查询下载:用户画像场景

ClickHouse中的查询语句如下:

--Q1
select user_id
from person_tag
where mock3d_like > 8 and mock3d_consume_content_cnt > 8 and mock_10_day_product_avg_amt < 1 settings append_squashing_after_filter = 1;
--Q2
select user_id
from person_tag
where mock_7_day_receive_cnt > 8 and like_fitness = 1 and mock14d_share_cnt > 8 settings append_squashing_after_filter = 1;
--Q3
select user_id
from person_tag
where home_perfer_mock_score > 8 and mock7d_access_homepage_cnt > 8 settings append_squashing_after_filter = 1;
--Q4
select user_id
from person_tag
where is_send_register_coupon > 8 and mock1d_like > 8 settings append_squashing_after_filter = 1;
--Q5
select user_id
from person_tag
where like_sports = 1 and like_3c = 1 and sex = 1 and like_dance = 1 and mock1d_share_cnt > 6 settings append_squashing_after_filter = 1;
--Q6
select user_id
from person_tag
where mock14d_access_homepage_cnt > 8 and like_anime = 1 settings append_squashing_after_filter = 1;
--Q7
select user_id,offline_ver,is_visitor,mock1d_comment_like,reg_days,mock14d_share_cnt,mock_30_order_avg_delivery_time_cnt,mock7d_comment_cnt,performance_rate,mock3d_valid_user_follow_cnt,mock30d_consume_content_cnt,like_cnt,like_photo,ls90_day_access_days,mock3d_release_trend_cnt,mock14d_access_homepage_range,qutdoor_perfer_mock_score,mock3d_access_homepage_cnt,mock_15_order_avg_delivery_time_cnt,mock7d_release_trend_cnt,like_food,mock30d_follow_topic_cnt,mock7d_is_access_topic,like_music,mock3d_interactive_cnt,mock14d_valid_user_follow_cnt,reg_platform,mock_7_day_lottery_participate_cnt,pre_churn_users,etl_time,like_anime,mock14d_access_homepage_cnt,mock14d_consume_content_cnt,like_travel,like_watches,mock14d_comment_like,ls30_day_access_days,mock14d_release_trend_cnt,ftooeawr_perfer_mock_score,mock7d_valid_user_follow_cnt,beauty_perfer_mock_score
from person_tag
where mock3d_like > 8 and mock3d_consume_content_cnt > 8 and mock_10_day_product_avg_amt < 1 settings append_squashing_after_filter = 1;

查询性能结果对比如下,可以看出Elasticsearch在扫描导出大量结果数据的场景下,性能非常大,返回的结果集越大越慢,其中Q5是查询命中结果集很小的对比case。

二级索引点查场景(数据量1000000000)

在分析查询业务场景中,用户难免会有几个明细点查case,例如根据日志traceId查询明细信息。开源ClickHouse因为没有二级索引能力,在遇到这种情况时,查询性能对比Elasticsearch完全落后。阿里云ClickHouse自研了二级索引能力,补齐了这方面的短板,作者在这里专门加了一个二级索引点查的场景来进行性能对比测试。Elasticsearch和ClickHouse完整版建表语句和查询下载:二级索引点查场景

ClickHouse中的建表语句如下:

CREATE TABLE point_search_test_local on cluster default (
 `PRI_KEY` String, 
 `SED_KEY` String,  
 `INT_0` UInt32, 
 `INT_1` UInt32, 
 `INT_2` UInt32, 
 `INT_3` UInt32, 
 `INT_4` UInt32, 
 `LONG_0` UInt64, 
 `LONG_1` UInt64, 
 `LONG_2` UInt64, 
 `LONG_3` UInt64, 
 `LONG_4` UInt64, 
 `STR_0` String, 
 `STR_1` String, 
 `STR_2` String, 
 `STR_3` String, 
 `STR_4` String, 
 `FIXSTR_0` FixedString(16), 
 `FIXSTR_1` FixedString(16), 
 `FIXSTR_2` FixedString(16), 
 `FIXSTR_3` FixedString(16), 
 `FIXSTR_4` FixedString(16), 
 KEY SED_KEY_IDX SED_KEY Type range
) ENGINE = MergeTree ORDER BY PRI_KEY 
SETTINGS index_granularity_bytes = 4096, secondary_key_segment_min_rows = 1000000000, min_rows_for_wide_part = 2000000000;

CREATE TABLE point_search_test on cluster default as point_search_test_local
engine = Distributed(default, default, point_search_test_local, rand());

ClickHouse中的查询模板语句如下:

select * from point_search_test where SED_KEY = 'XXX' settings max_threads = 1;

最终的查询性能对比如下,阿里云ClickHouse具备二级索引能力后,其点查能力完全不弱于Elasticsearch,存储原生支持的二级索引能力,具有极致性能。(阿里云ClickHouse二级索引文档)

数据导入性能对比

前面列举的所有数据集数据,作者都使用了ESSD本地文件导入的方式测试对比了Elasticsearch和ClickHouse的导入性能。ClickHouse可以直接使用ClickHouse-Client读取各种格式的本地文件进行导入,而Elasticsearch则是通过配置Logstash任务。具体耗时结果如下:

结语

Elasticsearch最擅长的主要是完全搜索场景(where过滤后的记录数较少),在内存富裕运行环境下可以展现出非常出色的并发查询能力。但是在大规模数据的分析场景下(where过滤后的记录数较多),ClickHouse凭借极致的列存和向量化计算会有更加出色的并发表现,并且查询支持完备度也更好。ClickHouse的并发处理能力立足于磁盘吞吐,而Elasticsearch的并发处理能力立足于内存Cache,这使得两者的成本区间有很大差异,ClickHouse更加适合低成本、大数据量的分析场景,它能够充分利用磁盘的带宽能力。数据导入和存储成本上,ClickHouse更加具有绝对的优势。

优点

  1. ClickHouse写入吞吐量大,单服务器日志写入量在50MB到200MB/s,每秒写入超过60w记录数,是ES的5倍以上。
  2. 查询速度快,官方宣称数据在pagecache中,单服务器查询速率大约在2-30GB/s;没在pagecache的情况下,查询速度取决于磁盘的读取速率和数据的压缩率。。
  3. ClickHouse比ES服务器成本更低。一方面ClickHouse的数据压缩比比ES高,相同数据占用的磁盘空间只有ES的1/3到1/30,节省了磁盘空间的同时,也能有效的减少磁盘IO;另一方面ClickHouse比ES占用更少的内存,消耗更少的CPU资源。。
  4. 相比ES,ClickHouse稳定性更高,运维成本更低。ES中不同的Group负载不均衡,有的Group负载高,会导致写Rejected等问题,需要人工迁移索引;在ClickHouse中通过集群和Shard策略,采用轮询写的方法,可以让数据比较均衡的分布到所有节点。ES中一个大查询可能导致OOM的问题;ClickHouse通过预设的查询限制,会查询失败,不影响整体的稳定性。ES需要进行冷热数据分离,ClickHouse按天分partition,一般不需要考虑冷热分离,特殊场景用户确实需要冷热分离的,数据量也会小很多,ClickHouse自带的冷热分离机制就可以很好的解决。
  5. ClickHouse采用SQL语法,比ES的DSL更加简单,学习成本更低。

缺点

  1. 由于是列式数据库,无法像ES一样提供全文检索功能。
  2. 无法动态添加字段,需要提前定义好表schema。
  3. 日志无法长期保存,历史数据需定期清理下线,如果有保存历史数据需求,需要通过迁移数据,采用ClickHouse_copier或者复制数据的方式实现。
  4. ClickHouse查询速度快,能充分利用集群资源,但是无法支持高并发查询,默认配置qps为100。
  5. Clickhouse并不适合许多小数据高频插入,批量写入日志会有一定延迟。

携程相同类型日志在ES和ClickHouse占用磁盘空间 

携程相同类型日志在ES和ClickHouse查询时间

ClickHouse替换ES的可行性方案

1. 容灾部署与集群规划

采用多Shards、2 Replicas的方式,通过Zookeeper进行服务器间互相备份,允许一个shard一台服务器down机数据不丢失。为了接入不同规模的日志,可以按日志类型及日志量建立多个集群。

2. 消费数据到ClickHouse采用gohangout工具

a)采用轮询的方式写ClickHouse集群的所有服务器,保证数据基本均匀分布。

b)大批次低频率的写入,减少parts数量,减少服务器merge,避免Too many parts异常。通过两个阈值控制数据的写入量和频次,超过10w记录写一次或者30s写一次。

3. 表结构的设计

按日志类型建立不同的本地表,非标字段可以设置为map类型,有值的用值填充,没有值就直接用 N 填充。

建表时考虑partition的设置,按天分partition。

4. 数据展示

Clickhouse自带的web接面Tabix.

第三方可视化界面可以接入Grafana,kibana

参考链接

https://zhuanlan.zhihu.com/p/368193306

https://www.cnblogs.com/xionggeclub/p/15100707.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1026967.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

爬虫入门基础与Selenium反爬虫策略

目录 一、爬虫入门基础 1、什么是爬虫&#xff1f; 2、爬虫的分类 3、爬虫的基本流程 二、Selenium简介 1、Selenium是什么&#xff1f; 2、Selenium的用途 三、应对反爬虫的Selenium策略 1、使用代理IP 2、模拟用户行为 3、设置合理的请求间隔时间 4、随机化请求参…

社区活跃开发者 Aaron 加入 sCrypt

Aaron&#xff08;周全&#xff09;是资深的 BSV 开发者&#xff0c;前 nChain BSV 基础架构团队成员&#xff0c;也是比特币协会在中国任命的首位技术推广专家。作为 BSV 社区的活跃成员&#xff0c;他多次作为演讲者参与区块链技术会议&#xff0c;开发了 Webot 应用、Witnes…

【完美解决】GitHub连接超时问题 Recv failure: Connection was reset

问题&#xff1a; 已经开了梯子但是在Idea中使用git&#xff08;GitHub&#xff09;还是连接超时Recv failure: Connection was reset。此时需要让git走代理。 解决方案&#xff1a; 1.对右下角网络点击右键 -> 打开网络和Internet设置 2.代理 -> 查看到地址和端口号…

智能生活从这里开始:数字孪生驱动的社区

数字孪生技术&#xff0c;这个近年来备受瞩目的名词&#xff0c;正迅速渗透到社区发展领域&#xff0c;改变着我们居住的方式、管理的方式以及与周围环境互动的方式。它不仅仅是一种概念&#xff0c;更是一种变革&#xff0c;下面我们将探讨数字孪生技术如何推动社区智能化发展…

淘宝分布式文件存储系统( 二 ) -TFS

淘宝分布式文件存储系统( 二 ) ->>TFS 目录 : 大文件存储结构哈希链表的结构文件映射原理及对应的API文件映射头文件的定义 大文件存储结构 : 采用块(block)文件的形式对数据进行存储 , 分成索引块,主块 , 扩展块 。所有的小文件都是存放到主块中的 &#xff0c;扩展块…

湖南湘潭家具3D轮廓扫描测量家居三维数字化外观逆向设计-CASAIM中科广电

随着科技的不断进步&#xff0c;CASAIM三维扫描技术在各个行业中得到了广泛应用&#xff0c;家具行业也不例外。传统的家具设计和展示方式已经无法满足现代消费者的个性化、多元化需求&#xff0c;而三维扫描技术的出现为家具行业带来了新的机遇和可能性。 家具表面有雕刻图案…

Selenium和Requests搭配使用

Selenium和Requests搭配使用 前要1. CDP2. 通过requests控制浏览器2. 1 代码一2. 2 代码2 3. 通过selenium获取cookie, requests携带cookie请求 前要 之前有提过, 用selenium控制本地浏览器, 提高拟人化,但是效率比较低,今天说一种selenium和requests搭配使用的方法 注意: 一定…

企业该如何选择数字化转型工具?_光点科技

随着科技的不断进步和数字化的浪潮席卷全球&#xff0c;企业数字化转型已经成为了保持竞争力和持续增长的关键因素之一。无论企业规模大小&#xff0c;数字化转型都可以提高效率、降低成本、改善客户体验&#xff0c;从而实现更好的业务结果。然而&#xff0c;要成功进行数字化…

Unity云原生分布式运行时

// 元宇宙时代的来临对实时3D引擎提出了诸多要求&#xff0c;Unity作为游戏行业应用最广泛的3D实时内容创作引擎&#xff0c;为应对这些新挑战&#xff0c;提出了Unity云原生分布式运行时的解决方案。LiveVideoStack 2023上海站邀请到Unity中国的解决方案工程师舒润萱&#x…

iPhone辐射超标,发布三年突然禁售了

昨晚 iPhone 15 预售大家抢到了吗&#xff1f; 虽然13日发布会后大家的反应十分冷静&#xff0c;但身体还是很诚实&#xff0c;官网都排到6-7周以后了... 在大伙都争着第一波尝鲜的时候&#xff0c;有一个地方正准备禁售 iPhone 。 不用想肯定是欧盟某个国家啦&#xff0c;这…

python正则表达(06)

python正则表达(06) 文章目录 python正则表达(06)1 正则表达式概念2 正则的三个基础方法2.1 match、search、findall三个基础方法2.2 re.match() 函数2.2.1 re.match(匹配规则&#xff0c;被匹配字符串)2.2.2验证是否开头匹配&#xff0c;match是匹配开头&#xff0c;后面的是不…

Ingress Controller

什么是 Ingress Controller &#xff1f; 在云原生生态中&#xff0c;通常来讲&#xff0c;入口控制器( Ingress Controller )是 Kubernetes 中的一个关键组件&#xff0c;用于管理入口资源对象。 Ingress 资源对象用于定义来自外网的 HTTP 和 HTTPS 规则&#xff0c;以控制进…

Ae 效果:CC Ball Action

模拟/CC Ball Action Simulation/CC Ball Action CC Ball Action &#xff08;CC 球体动作&#xff09;基于源图像转换为网格&#xff0c;并基于网格生成一个个继承源图像像素颜色的具有 3D 深度的小球体。 效果名称左侧出现的立方体图标&#xff0c;表示本效果支持 3D 摄像机。…

2023_Spark_实验七:Scala函数式编程部分演示

1、Scala中的函数 在Scala中&#xff0c;函数是“头等公民”&#xff0c;就和数字一样。可以在变量中存放函数&#xff0c;即&#xff1a;将函数作为变量的值&#xff08;值函数&#xff09;。 def myFun1(name:String):String"Hello " nameprintln(myFun1("…

EasyExcel导出转换@ExcelProperty注解中converter不生效,以及EasyExcel导入日期转换失败问题

用EasyExcel做导出&#xff0c;需要用ExcelProperty做格式转换&#xff0c;比如日期转换&#xff0c;枚举类转换 然后新建一个转换类 里面有两个实现方法&#xff0c;converToJavaData是导入时&#xff0c;数据转换定义格式&#xff0c;converToExcelData是导出时做数据转换的。…

Python爬虫基础(三):使用Selenium动态加载网页

文章目录 系列文章索引一、Selenium简介1、什么是selenium&#xff1f;2、为什么使用selenium3、安装selenium&#xff08;1&#xff09;谷歌浏览器驱动下载安装&#xff08;2&#xff09;安装selenium 二、Selenium使用1、简单使用2、元素定位3、获取元素信息4、交互 三、Phan…

3D WEB引擎HOOPS Commuicator助力Naval Architect Jumpstart快速启动船舶信息建模平台开发

行业&#xff1a;造船业 挑战&#xff1a;新公司希望将创新的船舶信息建模产品推向市场-基于浏览器的产品需要支持高级可视化和强大的数据转换&#xff0c;以处理大型、复杂的造船项目-小型开发团队的任务是雄心勃勃的平台发布计划。 解决方案&#xff1a; Tech Soft 3D提供领…

ubuntu x86_64 源码编译 rust 1.48.0

源码地址 GitHub - rust-lang/rust: Empowering everyone to build reliable and efficient software. git clone https://github.com/rust-lang/rust cd rust git checkout 1.48.0 ./configure ./x.py build 安装前执行cargo vendor yeqiangyeqiang-MS-7B23:~/Downloads/sr…

PyTorch框架中torch、torchvision、torchaudio与python之间的版本对应关系(9月最新版)

随着python语言和pytorch框架的更新&#xff0c;torch\torchvision\torchaudio与python之间的版本对应关系也在不断地更新。 最新版本torch与torchvision对应关系如下&#xff1a; 稍旧版本torch与torchvision对应关系如下&#xff1a; 最新版本torch与torchaudio对应关系如下…

js字符串转时间戳

完整代码 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title></head><body><script>// 字符串转时间戳const charToTime (dateStr) > {let date dateStr.substring(0, 19);date dat…