在5000亿数据中大海捞针,需要怎样的硬件做支撑?

  1. 云栖社区>
  2. 博客>
  3. 正文

在5000亿数据中大海捞针,需要怎样的硬件做支撑?

nauu 2020-06-01 15:31:25 浏览454

最近看到一篇由Alexander Zaitsev撰写的,关于ClickHouse性能对比的文章(文末会有报告的原文地址),觉得很有意思,所以拿来与大家分享一下。它有趣的地方在于,这是一篇针对性能对标的,性能对标测评。这算不算螳螂捕蝉,黄雀在后呢?
这篇文章的对标场景,来自于ScyllaDB的一篇测评报告,而在这篇报告中,ScyllaDB历数了从2001年至今,NoSql数据库性能达到的几个关键里程碑。而如今他们宣称,ScyllaDB能够在5000亿的数据量,以 10亿行/每秒 的性能处理数据。

ScyllaDB的测试方案

在ScyllaDB的测试方案中,他们模拟了物联网的使用场景:

测试数据

数据来自于家庭安装的温度传感器,这些温度传感器每隔 1 分钟读数一次。
所以,1百万个传感器,1年下来的数据量将会是:

1,000,000 24 60 * 365 = 5256 亿

硬件配置

在服务器硬件方面,他们采用了packet.com的云服务,使用了83个 n2.xlarge.x86 实例作为database节点,以及24个 c2.medium.x86 实例作为辅助的worker节点。
这真可谓是超豪华阵容,节点配置分别如下:

database work
类型 n2.xlarge.x86 c2.medium.x86
CPU 28 Physical Cores @ 2.2 GHz 24 Physical Cores @ 2.2 GHz
Memory 384 GB of DDR4 ECC RAM 64 GB of ECC RAM
Storage 3.8TB of NVMe Flash 960 GB of SSD
Network 4 × 10GBPS 2 × 10GBPS

测试目标

解答这么几个问题:

  1. 从时间跨度为3个月的数据中,分别找到温度最高和最低的那一天,以及这些读数来自于哪个传感器;
  2. 从整年的数据中,分别找到温度最高和最低的那一天,以及这些读数来自于哪个传感器;
  3. 在测试数据中,事先埋入了一些坏的测点,在查询中排除这些坏的测点(像不像大海捞针)。

ClickHouse的黑科技

Alexander在看到这篇文章之后,觉得它们的测试方案太不经济了,如果使用ClickHouse的黑魔法,能够在达到同样业务需求的背景下,拥有更好的成本效益。一不做二不休,Alexander决定使用一台NUC迷你主机作为ClickHouse的测试硬件。
丧心病狂的硬件对比如下:

ClickHouse ScyllaDB
模式 单节点 集群
服务器 1 台 Intel NUC 83台 n2.xlarge.x86 database节点, 24台 c2.medium.x86 worker 节点
CPU 4 cores (Intel i7-6770HQ) 2900 cores = 83 x 28 cores (Intel Xeon Gold 5120) + 24 x 24 cores (AMD EPYC 7401p)
Memory 32 GB 33408 GB = 83 x 384 GB + 24 x 64 GB
Storage 1 TB NVMe SSD 338 TB = 83 x 3.8TB NVMe SSD + 24 x 960GB SSD

在ClickHouse的对比方案中,它与对手的配置差距总是如此之大。
然而,即便在内存相差1000倍的情况下,ClickHouse仍然比对手拥有10~100倍的性能。

# 耳听为虚眼见为实

ClickHouse真的有这么夸张的性能吗? 光道听途说可不行, 咱必须得亲自试试,可是我身边也没迷你主机啊,只能拿笔记本 + 虚拟机凑活

虚拟机系统Centos 7, 配置4 cores, 32G:

uname -a    
Linux ch6.nauu.com 3.10.0-1062.el7.x86_64 #1 SMP Wed Aug 7 18:08:02 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
[root@ch6 ~]# 
[root@ch6 ~]# free -g
              total        used        free      shared  buff/cache   available
Mem:             31           0          30           0           0          30
Swap:             1           0           1
[root@ch6 ~]# grep 'core id' /proc/cpuinfo | sort -u | wc -l
4

ClickHouse版本19.17.4

按照Alexander的例子,首先需要创建一张保存传感器数据的测试表:

CREATE TABLE sensors_data (
   sensor_id Int32 Codec(DoubleDelta, LZ4),
   time DateTime Codec(DoubleDelta, LZ4),
   date ALIAS toDate(time),
   temperature Decimal(5,2) Codec(T64, LZ4)
) Engine = MergeTree
PARTITION BY toYYYYMM(time)
ORDER BY (sensor_id, time);

在这张数据表的定义中,可谓是做到了极致的优化。包括:

  • 针对每一个列字段,都单独声明了它的encode编码算法和compresss压缩算法,这是ClickHouse的一个新特性。简而言之,如果是时间序列的数据,推荐使用DoubleDelta和LZ4的组合;而在数据模式不明确的情况下,可以使用T64和LZ4的组合。关于算法的这一块的介绍,以后有时间可以专门写一篇说明;
  • date类型使用了 ALIAS 计算字段,降低了存储开销;
  • 分区Key和主键Key,很好的匹配了业务的查询条件。如此一来,在后续的查询中,就能够充分利用MergeTree的分区索引和一级索引。

有了数据表之后,就可以开始模拟测试数据了,Alexander使用了numbers_mt函数,模拟了5256亿温度数据:


INSERT INTO sensors_data (sensor_id, time, temperature) \
WITH \
 toDateTime(toDate('2019-01-01')) as start_time,  \
 1000000 as num_sensors, \
 365 as num_days, \
 24*60 as num_minutes, \
 num_days * num_minutes as total_minutes \
SELECT \
 intDiv(number, num_minutes) % num_sensors as sensor_id,  \
 start_time + (intDiv(number, num_minutes*num_sensors) as day)*24*60*60 + (number % num_minutes as minute)*60 time,  \
 60 + 20*sin(cityHash64(sensor_id)) \
 + 15*sin(2*pi()/num_days*day)  \
 + 10*sin(2*pi()/num_minutes*minute)*(1 + rand(1)%100/2000)  \
 + if(sensor_id = 473869 and  \
      time between '2019-08-27 13:00:00' and '2019-08-27 13:05:00', -50 + rand(2)%100, 0)  \
 as temperature \
FROM numbers_mt(525600000000) \
SETTINGS max_block_size=1048576;

接下来干什么呢?接下来可以去睡觉了。由于是单线程写入,我粗略计算了一下,大概只要40~50个小时,数据就能全部写进去了 !!!

在这段等待的时间,我们继续往下分析。当数据写完之后,这张 sensors_data 数据表并不能直接作为查询表使用,还需要进一步为它创建物化视图:

CREATE MATERIALIZED VIEW sensors_data_daily( \
  sensor_id Int32 Codec(DoubleDelta, LZ4), \
  date Datetime Codec(DoubleDelta, LZ4), \
  temp_min SimpleAggregateFunction(min, Decimal(5,2)), \
  temp_max SimpleAggregateFunction(max, Decimal(5,2)) \
) Engine = AggregatingMergeTree \
PARTITION BY toYYYYMM(date) \
ORDER BY (sensor_id, date) \
POPULATE \
AS  \
SELECT sensor_id, date,  \
   min(temperature) as temp_min, \
   max(temperature) as temp_max \
FROM sensors_data \
GROUP BY sensor_id, date;

物化视图会自动同步sensors_data的数据。

由于使用了AggregatingMergeTree表引擎,数据在AggregatingMergeTree合并分区的过程中,会以分区目录为单位,按照 sensor_id和date预先聚合。
所以,这里其实是玩了一个ClickHouse的常用技巧,那就是利用物化视图进行了预聚合的优化。使用物化视图和MergeTree组合使用,是ClickHouse的杀手锏之一。

在这个例子中,有点类似数据立方体的意思,通过预聚合, 将聚合结果预先存在表内,在之后查询的过程中,可以从结果直接返回。与此同时,预先聚合还能有效的减少数据行,在这个例子中,最终能将视图内的数据行减少1400倍之多。
正如ScyllaDB的文章中所说,他们的测试并不是一场 apples to apples 的比较。同样的,我想Alexander拿ClickHouse做的这场比较,也不是针尖对麦芒的。它们两者之间有着太多的不同,它们使用了不同的数据模型、不同的架构等等。
但是通过这个案例的对比,大家可以认识到ClickHouse确实是经济和性能的完美平衡。如果你的业务也有类似的场景,使用ClickHouse将会是一种不错的选择。

由于我的数据还没写完。。。 关于论证的结果,只能留在下一篇了。