前言

在工作场景中,我们会采集工厂设备数据用于智能控制,数据的存储用了 InfluxDB,随着数据规模越来越大,InfluxDB 的性能越来越差,故考虑引入 ClickHouse 分担 InfluxDB 大数据分析的压力,再加上我们业务上也用到了 MySQL ,所以本文就来对比下 MySQL、InfluxDB、ClickHouse 在千万数据量下的写入耗时、聚合查询耗时、磁盘占用等各方面性能指标。

结论先行

最终的结论是,直接使用 ClickHouse 官网提供的 6600w 数据集来做对比测试,在 MySQL、InfluxDB、ClickHouse 同样分配 4c16g 资源的情况下,ClickHouse 无论是导入速度、磁盘占用、查询性能都完全碾压 MySQL 和 InfluxDB,具体对比指标如以下表格:

MySQL InfluxDB ClickHouse
导入耗时 大概耗时70分钟 大概耗时35分钟 75秒
磁盘空间 12.35 G 5.9 G 2.66 G
全表count 24366 ms 11674 ms 100 ms
全表max/min 27023 ms 26829 ms 186 ms
全表平均值 24841 ms 12043 ms 123 ms
全表方差 24600 ms OOM 113 ms
复杂查询1 30260 ms OOM 385 ms
复杂查询2 470 ms 200 ms 8 ms

为了确保测试结果相对准确,以上每条sql起码执行5次,然后取中间值。其中 InfluxDB 表现比想象中的要差,甚至还不如 MySQL,可能是数据样本和测试用例不太适合 InfluxDB 场景导致的,如果大家对测试结果有疑问,可以 git clone [https://github.com/stone0090/clickhouse-test.git](https://github.com/stone0090/clickhouse-test.git)项目,完整验证以上对比全过程。

数据库简介

MySQL

MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,属于 Oracle 旗下产品,是最流行的关系型数据库管理系统之一。它所使用的 SQL 语言是用于访问数据库的最常用标准化语言。它采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型和大型网站的开发都选择 MySQL 作为网站数据库。《高性能MySQL》一书中开篇明义讲到的最核心的一句话是“MySQL并不完美,但是却足够灵活”,它是架构中的万金油,庞杂非单一的项目中总会有它的用武之地。

InfluxDB

InfluxDB 是一个由 InfluxData 公司开发的开源时序型数据库,专注于海量时序数据的高性能读、高性能写、高效存储与实时分析,在 DB-Engines Ranking 时序型数据库排行榜上位列榜首,广泛应用于DevOps监控、IoT监控、实时分析等场景。

传统数据库通常记录数据的当前值,时序型数据库则记录所有的历史数据,在处理当前时序数据时又要不断接收新的时序数据,同时时序数据的查询也总是以时间为基础查询条件,并专注于解决以下海量数据场景的问题:

  • 时序数据的写入:如何支持千万级/秒数据的写入;
  • 时序数据的读取:如何支持千万级/秒数据的聚合和查询;
  • 成本敏感:海量数据存储带来的是成本问题,如何更低成本地存储这些数据。

ClickHouse

ClickHouse 是 Yandex(俄罗斯最大的搜索引擎)开源的一个用于实时数据分析的基于列存储的数据库,其处理数据的速度比传统方法快 100-1000 倍。ClickHouse 的性能超过了目前市场上可比的面向列的 DBMS,每秒钟每台服务器每秒处理数亿至十亿多行和数十千兆字节的数据。它是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS),简单介绍一下 OLTP 和 OLAP。

  • OLTP:是传统的关系型数据库,主要操作增删改查,强调事务一致性,比如银行系统、电商系统。
  • OLAP:是仓库型数据库,主要是读取数据,做复杂数据分析,侧重技术决策支持,提供直观简单的结果。

那 ClickHouse OLAP 适用场景有:1)读多于写;2)大宽表,读大量行但是少量列,结果集较小;3)数据批量写入,且数据不更新或少更新;4)无需事务,数据一致性要求低;5)灵活多变,不适合预先建模。

环境准备

在阿里云买一台 16c64g 的服务器,操作系统 centos 7.8,使用 sealos 一键安装 k8s,使用 helm 一键安装 mysql(5.7)、influxdb(1.8)、clickhouse(22.3) ,每个应用各分配 4c16g 的资源。

# 下载 sealos
$ wget https://github.com/labring/sealos/releases/download/v4.0.0/sealos_4.0.0_linux_amd64.tar.gz \
&& tar zxvf sealos_4.0.0_linux_amd64.tar.gz sealos && chmod +x sealos && mv sealos /usr/bin # 初始化一个单节点 Kubernetes
$ sealos run labring/kubernetes:v1.24.0 labring/calico:v3.22.1 --masters [xxx.xxx.xxx.xxx] -p [your-ecs-password] # 去掉 master 的污点,允许安装应用到 master 和 control-plane
$ kubectl taint nodes --all node-role.kubernetes.io/master-
$ kubectl taint nodes --all node-role.kubernetes.io/control-plane- # 获取 mysql、influxdb、clickhouse 一键安装 Helm-Charts
$ wget https://github.com/stone0090/clickhouse-test/archive/refs/tags/v1.0.0.tar.gz
$ tar -zxvf v1.0.0.tar.gz # 安装 Kubernetes 包管理工具 Helm,以及 mysql、influxdb、clickhouse 3大数据库
$ sealos run labring/helm:v3.8.2
$ helm install mysql clickhouse-test-1.0.0/helm-charts/mysql/
$ helm install influxdb clickhouse-test-1.0.0/helm-charts/influxdb/
$ helm install clickhouse clickhouse-test-1.0.0/helm-charts/clickhouse/

数据导入

直接使用 ClickHouse 官方提供的测试数据 https://clickhouse.com/docs/zh/getting-started/example-datasets/opensky,此数据集中的数据是从完整的 OpenSky 数据集中派生和清理而来的,以说明 COVID-19 新冠肺炎大流行期间空中交通的发展情况。它涵盖了自2019年1月1日以来该网络超过2500名成员看到的所有航班,总数据量有6600w。

# 在服务器 /home/flightlist 目录执行以下命令,该目录会被挂载到 mysql-pod、influxdb-pod、clickhouse-pod 内
$ wget -O- https://zenodo.org/record/5092942 | grep -oP 'https://zenodo.org/record/5092942/files/flightlist_\d+_\d+\.csv\.gz' | xargs wget # 批量解压 flightlist.gz 数据
$ for file in flightlist_*.csv.gz; do gzip -d "$file"; done # 将 csv 处理成 influxdb 导入所需的 txt 格式(此过程大概耗时1小时)
$ python clickhouse-test-1.0.0/influxdb_csv2txt.py

MySQL

# 进入 mysql pod
$ kubectl exec -it [influxdb-podname] -- bash # 连上 mysql 建库、建表
$ mysql -uroot -p123456
$ use test;
$ CREATE TABLE `opensky` (`callsign` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`number` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`icao24` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`registration` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`typecode` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`origin` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`destination` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`firstseen` datetime DEFAULT NULL,`lastseen` datetime DEFAULT NULL,`day` datetime DEFAULT NULL,`latitude_1` double DEFAULT NULL,`longitude_1` double DEFAULT NULL,`altitude_1` double DEFAULT NULL,`latitude_2` double DEFAULT NULL,`longitude_2` double DEFAULT NULL,`altitude_2` double DEFAULT NULL,KEY `idx_callsign` (`callsign`),KEY `idx_origin` (`origin`),KEY `idx_destination` (`destination`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; # 导入数据(大概耗时70分钟)
$ load data local infile 'flightlist_20190101_20190131.csv' into table opensky character set utf8mb4 fields terminated by ',' lines terminated by '\n' ignore 1 lines;
# 省略其他29条导入命令:load data local infile 'flightlist_*_*.csv' into table opensky character set utf8mb4 fields terminated by ',' lines terminated by '\n' ignore 1 lines; # 检查数据是否导入成功
$ select count(*) from test.opensky;

InfluxDB

# 进入 influxdb pod
$ kubectl exec -it [influxdb-podname] -- bash # 导入数据(大概耗时30分钟)
$ influx -username 'admin' -password 'admin123456' -import -path=/tmp/flightlist/flightlist_20190101_20190131.txt -precision=ns;
# 省略其他29条导入命令:influx -username 'admin' -password 'admin123456' -import -path=/tmp/flightlist/flightlist_*_*.txt -precision=ns; # 检查数据是否导入成功
$ influx -username 'admin' -password 'admin123456'
$ select count(latitude_1) from test.autogen.opensky;

ClickHouse

# 进入 clickhouse pod
$ kubectl exec -it [clickhouse-podname] -- bash # 连上 clickhouse 建库、建表
$ clickhouse-client
$ create database test;
$ use test;
$ CREATE TABLE opensky(callsign String,number String,icao24 String,registration String,typecode String,origin String,destination String,firstseen DateTime,lastseen DateTime,day DateTime,latitude_1 Float64,longitude_1 Float64,altitude_1 Float64,latitude_2 Float64,longitude_2 Float64,altitude_2 Float64) ENGINE = MergeTree ORDER BY (origin, destination, callsign);
$ exit # 导入数据(大概耗时75秒)
$ cd /tmp/flightlist
$ for file in flightlist_*.csv; do cat "$file" | clickhouse-client --date_time_input_format best_effort --query "INSERT INTO test.opensky FORMAT CSVWithNames"; done # 检查数据是否导入成功
$ clickhouse-client
$ SELECT count() FROM test.opensky;

测试场景

MySQL

$ mysql -uroot -p123456
$ use test;
-- 开启性能分析
set profiling = 1;
-- 查询磁盘空间
select table_rows as `总行数`, (data_length + index_length)/1024/1024/1024 as `磁盘占用(G)` from information_schema.`TABLES` where table_name = 'opensky';
-- 全表count
select count(latitude_1) from opensky;
-- 全表max/min
select max(longitude_1),min(altitude_1) from opensky;
-- 全表平均值
select avg(latitude_2) from opensky;
-- 全表方差
select var_pop(longitude_2) from opensky;
-- 复杂查询1:全表多个字段聚合查询
select count(latitude_1),max(longitude_1),min(altitude_1),avg(latitude_2) from opensky;
-- 复杂查询2:从莫斯科三个主要机场起飞的航班数量
SELECT origin, count(1) AS c FROM opensky WHERE origin IN ('UUEE', 'UUDD', 'UUWW') GROUP BY origin;
-- 输出分析结果
show profiles;

InfluxDB

$ influx -username 'admin' -password 'admin123456'
$ use test;
-- 耗时统计,queryReqDurationNs 是累计查询时间,2次任务的时间相减就是耗时
select queryReq,queryReqDurationNs/1000/1000,queryRespBytes from _internal."monitor".httpd order by time desc limit 10;
-- 查询磁盘空间
select sum(diskBytes) / 1024 / 1024 /1024 from _internal."monitor"."shard" where time > now() - 10s group by "database";
-- 全表count
select count(latitude_1) from opensky;
-- 全表max/min
select max(longitude_1),min(altitude_1) from opensky;
-- 全表平均值
select mean(latitude_2) from opensky;
-- 全表方差
select stddev(longitude_2) from opensky;
-- 复杂查询1:全表多个字段聚合查询
select count(latitude_1),max(longitude_1),min(altitude_1),mean(latitude_2) from opensky;
-- 复杂查询2:从莫斯科三个主要机场起飞的航班数量
SELECT count(latitude_1) AS c FROM opensky WHERE origin =~/^UUEE|UUDD|UUWW$/ GROUP BY origin;

ClickHouse

$ clickhouse-client
$ use test;
-- 耗时统计
select event_time_microseconds,query_duration_ms,read_rows,result_rows,memory_usage,query from system.query_log where query like '%opensky%' and query_duration_ms <> 0 and query not like '%event_time_microseconds%' order by event_time_microseconds desc limit 5;
-- 查询磁盘空间
SELECT formatReadableSize(total_bytes) FROM system.tables WHERE name = 'opensky';
-- 全表count
select count(latitude_1) from opensky;
-- 全表max/min
select max(longitude_1),min(altitude_1) from opensky;
-- 全表平均值
select avg(latitude_2) from opensky;
-- 全表方差
select var_pop(longitude_2) from opensky;
-- 复杂查询1:全表多个字段聚合查询
select count(latitude_1),max(longitude_1),min(altitude_1),avg(latitude_2) from opensky;
-- 复杂查询2:从莫斯科三个主要机场起飞的航班数量
SELECT origin, count() AS c FROM opensky WHERE origin IN ('UUEE', 'UUDD', 'UUWW') GROUP BY origin;

ClickHouse 为什么快

1、列式存储

数据是按列存储,数据即是索引;查询只访问涉及的列,降低系统I/O;每一列都由一个线程来处理,高效利用CPU资源;还为向量化执行做好了铺垫。

2、数据压缩

数据压缩的本质是按照一定的步长对数据进行匹配扫描,当发现重复数据的时候就进行编码转换。

因为是列式存储,所以数据特征很相似,所以数据中的重复项多,则压缩率越高,则数据体量越小,则磁盘I/O压力越小,则网络中传输越快。

3、向量化执行引擎

SIMD(Single Instruction Multiple Data)即单条指令操作多条数据,它是通过数据并行以提高性能的一种方式,可以简单理解为在寄存器层面对程序中的数据做并行处理,Clickhouse 在能够提升计算效率的地方大量使用了 SIMD,通过使用 SIMD,基本上能带来几倍的性能提升。

4、多线程和分布式

分布式领域存在一条定律,计算移动比数据移动更加划算,这也是其核心所在,将数据的计算直接发放到数据所在的服务器,多机并行处理,再把最终的结果汇集在一起;另外 ClickHouse 也通过线程级别并行的方式为效率进一步提速,极致去利用服务器的资源。

5、多样的表引擎

MergeTree 存储结构对写入的数据做排序然后进行有序存储,有序存储主要有两大优势:

  • 列存文件在按块做压缩时,排序键中的列值是连续或者重复的,使得列存块的数据可以获得极致的压缩比;
  • 存储有序本身可以加速查询的索引结构,根据排序键中列的等值条件或者 rang 条件,我们可以快速找到目标所在的近似位置区间,并且这种索引结构是不会产生额外的存储开销。

MergeTree 是 ClickHouse 表引擎中最核心的引擎,其他引擎均以 MergeTree 引擎为基础,并在数据合并过程中实现了不同的特性,从而构成了 MergeTree 表引擎家族。

ClickHouse 的优缺点

优点:极致的查询分析性能,较低的存储成本,高吞吐的数据写入,多样化的表引擎,完备的 DBMS 功能;

缺点:不支持事务,不支持真正的删除/更新,分布式能力较弱;不支持高并发,官方建议 QPS 为100;非标准的 SQL,join 的实现比较特殊,且性能不好;频繁小批量数据操作会影响查询性能;

目前还没有一个 OLAP 引擎能够满足各种场景的需求,其本质原因是,没有一个系统能同时在查询效率、时效性、可维护性三个方面做到完美,只能说 ClickHouse 是为了极致查询性能做了一些取舍。

ClickHouse 优缺点都很明显,是否采用还是要取决于和实际业务场景的契合度,适合自己的架构才是最好架构。

参考引用

记一次 ClickHouse 性能测试的更多相关文章

  1. 3000本IT书籍下载地址

    http://www.shouce.ren/post/d/id/112300    黑客攻防实战入门与提高.pdfhttp://www.shouce.ren/post/d/id/112299    黑 ...

  2. ClickHouse之简单性能测试

    前面的文章ClickHouse之初步认识已经简单的介绍了ClickHouse,接下来进行简单的性能测试.测试数据来源于美国民用航班的数据,从1987年到2017年,有1.7亿条. 环境: centos ...

  3. 性能测试——记XX银行保全项目性能问题分析优化

    记XX银行保全项目性能问题分析优化 数据库问题也许是大部分性能问题的关注点,但是JAVA应用与数据库交互的关节,JDBC 就像是我们人体的上半身跟下半身的腰椎,支持上半身,协调下半身运动的重要支撑点. ...

  4. 性能测试——记weblogic 连接池满无法链接故障诊断过程

    记weblogic 连接池满无法链接故障诊断过程 前段时间公司负责建行的一个票据系统在,上线前几个分行试运行环境下,每天后台日志都会报oracle.jdbc.xa.OracleXAException, ...

  5. clickhouse 亿级数据性能测试

    clickhouse 在数据分析技术领域早已声名远扬,如果还不知道可以 点这里 了解下. 最近由于项目需求使用到了 clickhouse 做分析数据库,于是用测试环境做了一个单表 6 亿数据量的性能测 ...

  6. 性能测试——记XX银行电票系统上线后宕机问题诊断优化

    四月份我们公司负责的电票系统上线了,这个系统上线比客户方其他系统上线还特殊,是二期改造项目,旧系统数据还要整合抽取到新系统中继续使用,而且该系统不是增量型方式开发上线的,而且全部开发完后全国上线的,这 ...

  7. 性能测试 | 记一次生产数据库sql由451s优化为0.4s的过程

    概述 最近开发说某个接口跑的很慢,排查了下发现其中一条sql,数据量不大,但居然要跑451s,下面简单记录一下优化的过程. 问题sql SELECT l.location_gid ENUMVALUE, ...

  8. Spark踩坑记——Spark Streaming+Kafka

    [TOC] 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark strea ...

  9. 地图性能测试利器PerfQA Analyzer

    PerfQA Analyzer作为一个地图性能测试工具,能针对ArcGIS 技术堆栈的系统进行性能问题的排查,用于系统试运行阶段的压力测试以及在线系统运维.大大减轻了GIS系统管理员的性能调优工作压力 ...

随机推荐

  1. [刷题] IDA*

    BZOJ3041 水叮当的舞步 Description & Solution 见hzw的博客 http://hzwer.com/1507.html Code // Author: wlzhou ...

  2. AI趋势量化系统(Binance升级版)

    更多精彩内容,欢迎关注公众号:数量技术宅,也可添加技术宅个人微信号:sljsz01,与我交流. B圈大跌行情,如何应对? 近期,伴随着美联储持续的加息进程,数字货币市场不论是市场焦点LUNA,还是BT ...

  3. 基于“均态分布”随机数算法的一次性口令OneTimePassword(原创)

    /* 所谓均态分布随机数算法是指:每个数(整数或实数)无序地分布在数轴上,值只出现一次永不重复.体现了香农的一次一密理论. * 均体现在每个数的值是平均概率,即都有出现:态体现在每个数在数轴上的位置是 ...

  4. CabloyJS一站式助力微信、企业微信、钉钉开发 - 钉钉篇

    前言 现在软件开发不仅要面对前端碎片化,还要面对后端碎片化.针对前端碎片化,CabloyJS提供了pc=mobile+pad的跨端自适应方案,参见:自适应布局:pc = mobile + pad 在这 ...

  5. css设置元素背景透明度的2种方式

    更新记录 本文迁移自Panda666原博客,原发布时间:2021年7月9日. 设置元素的背景的透明度可以使用2种方式:方式1:opacity属性.方式2:使用rgba值.两种方式有一点差异,opaci ...

  6. vue大型电商项目尚品汇(后台篇)day04

    昨天太晚就没来得及更新,今天是spu管理界面,这个界面一共有三个界面需要切换,完成了两个界面,而且今天的难度在于最后两个章节,富有一定的逻辑性,当然中间也有很多需要注意的,比如ElementUI的照片 ...

  7. redis如何实现数据同步

    redis如何实现数据同步 两种,1全同步,2部分同步 全备份: 在slave启动时会向master发送sync消息,master收到slave这条消息之后,将启动后台备份进程,备份完成之后,将备份数 ...

  8. JS:null

    null:它表示一个变量是空的或未知的. 看例子! var a = null; var b = 1; var c = "a"; var d; var e = "" ...

  9. 『现学现忘』Docker基础 — 39、实战:自定义Tomcat9镜像

    目录 1.目标 2.准备 3.编写Dockerfile文件 4.构建镜像 5.启动镜像 6.验证容器是否能够访问 7.向容器中部署WEB项目,同时验证数据卷挂载 (1)准备一个简单的WEB项目 (2) ...

  10. 告别单调,Django后台主页改造 - 使用AdminLTE组件

    前言 之前我做了个Django的项目,为了让管理后台更加美观,我对Django(应该说是SimpleUI的)默认的Admin后台主页进行改造,具体可以看这篇文章:项目完成 - 基于Django3.x版 ...