数据库 频道

DTCC 2021 | 滴普基于ClickHouse的实时分析引擎应用实践

  2021年10月18日~20日,由IT168联合旗下ITPUB、ChinaUnix两大技术社区主办的第十二届中国数据库技术大会(DTCC2021)在北京国际会议中心隆重召开。大会以“数造未来”为主题,设置2大主会场,20+技术专场,邀请超百位行业专家,重点围绕数据架构、人工智能与大数据应用、传统企业数据库实践和国产开源数据库等内容展开分享和探讨,为广大数据领域从业人士提供一场年度盛会和交流平台。

  大会第二天上午主会场,滴普科技DataSense首席程序员陈峰老师以《滴普基于ClickHouse的实时分析引擎应用实践》为主题进行了精彩的议题分享。

  ClickHouse自发布以来,就以其独步天下的超高单机性能横扫技术界。但极致的优点也带来了不可避免的缺陷,如何扬长避短,成为了应用ClickHouse的一大挑战。滴普在各种数字化场景深入应用并不断调优,钻研ClickHouse内核,从而形成了一些最佳实践。

  以下是陈峰老师在DTCC大会现场的演讲实录:

  感谢主办方给了我这次机会和大家分享滴普关于ClickHouse实时分析引擎实践,正式开始之前,我想先给大家介绍一下首席程序员这个Title。我们公司其实是没有首席架构师这个Title,因为架构师都是需要写代码的,所以我们认为所有的架构师都应该是一个优秀的程序员,我们公司首席架构师就被首席程序员这个Title取代。正是因为我们是首席程序员,所以我们经常需要去客户现场解决问题。

  简单介绍一下ClickHouse,ClickHouse自从2016年开源之后就是以查询速度快而在业内被广泛使用的,到底快到一个什么程度?滴普科技自己测试来看,单机的情况下数据量在31亿条还能够实现大部分查询1秒以内返回,这是非常惊人的单机性能,所以这里列出了ClickHouse的特点,这是优点特别明显的数据库或者OLAP查询引擎,也是劣势的OLAP数仓,单机查询速度非常快、单机性能非常高,基本上单机处理几十亿数据的话就可以搞定,不需要做什么分布式,也是一个支持SQL的数据库管理引擎,很多业务可以很容易地迁移到ClickHouse。

  我一直认为结构决定功能,ClickHouse具备这么强大的优点,研究以后发现存储引擎的设计在我们看来非常优秀,把数据写入存储引擎的时候对数据做了一些重新的组织,比如通过SM算法把数据写入存储引擎,通过这种方式实现单机速度查询快的能力,但同时这种设计也带来了从底层没有办法解决的问题。因为对数据做了LSM算法以后处理数据的时候默认是按照一批数据8192行,不管是读取还是写入至少是要8192行数据才能完成,所以不是点查,如果只是查一条数据,按照引擎来看也必须把一整块数据,就是8192行数据全部读出来,同时也不适合批量删除和修改,云也是因为只能按照8192行的力度去做数据,目前是不支持事务的,传统数据库做事务是用MVCC,其实对批量的存储引擎也是很难实现的。

  ClickHouse是优点很明显,缺点也非常明显的数据库。这里我列了3个用得比较多的应用,DataSense是基于ClickHouse和AI实现的引擎,DBMS4CK是我们解决客户问题的情况下发生的一些ClickHouse的性质和缺点,我们把一些功能在DBMS4CK做了一个集成,通过这些可以集成我们的一键安装ClickHouse升级和管理工具,DCT是ClickHouse的数据迁移工具,可以把数据迁入DataSense,也可以把ClickHouse迁移到业务数据库中。

  其实ClickHouse自带一些迁移工具,但存在一些问题,就是在某些大数据场景下经常会发生错误,所以DCT在我们使用ClickHouse的过程中也是不可或缺的工具。ClickHouse在滴普就是通过服务很多企业客户,我们对它有了一些沉淀,包括对源码做了修改、引擎做了改动,已经沉淀成了我们的SenseHouse产品。

  ClickHouse在服务客户中经常碰到一些痛点,很常见的一个点就是滴普服务的很多客户已经有了一个大数据集群,在这种情况下,我们怎么把ClickHouse融合到现有的Hadoop生态体系?因为我们客户部署的大数据基本都是Hadoop,如果客户已经部署Hadoop以后,想要把ClickHouse全新部署,其实这对客户来说是不太现实的,因为Hadoop生态体系已经部署完成以后就完成了数据清洗和ODS到DWT的转换,但如果需要重新部署一整套新的ClickHouse,之前的这些工作都必须重新去做,所以对客户来说是很难忍受的事情。

  我们碰到的很多客户都有这样一个需求,就是需要我们把ClickHouse融合到现有的Hadoop生态体系,而且本身存在一些限制,有些场景没有办法单独使用,比如实时计算这个场景,其实ClickHouse是能够支持实时计算的,但我们需要接收数据库的修改,ClickHouse不支持单条数据修改,只能做批处理修改,而且修改不是同步的,支持Update以后就是在未来的某个时间把数据做好修改,不是执行以后立马就修改了。

  ClickHouse更新单条数据是量非常大的工作,但一般实时计算引擎前面跟一个CDC,然后数据库的CDC经常发生一些Update,如果ClickHouse前面需要接上Update语句就没有办法直接使用ClickHouse,因为对单条Update的支持是很差的,在这种情况下就没有办法单独使用ClickHouse,必须要在之前解决刚才提到的Update的问题。

  滴普做了这么多的实验,今天我跟大家分享一个最主要的解决方案,就是对数据做冷热分离存储。冷热分离存储其实很好理解,ClickHouse的优点就是查询快、单机性能强,我们需要把热数据放到ClickHouse里面,不需要经常查询的数据就放到冷存储里面,这是所谓的冷热分离存储,意思就是会存在两套存储,热数据放入ClickHouse,冷数据放到客户现有的Hadoop集群或者其它的磁盘。

  把冷热分离存储分为两块来讲:

  热数据到冷数据的过程,这种场景就是数据刚开始进入的就是ClickHouse,进入以后就是热数据,我们可以配置业务3个月、7天或者1个月,具体来看业务配置,数据到达7天之后就把数据迁移到冷存储里面,其实就是为了解决ClickHouse处理大批量数据,或者分布式的情况下比较难做的问题。刚才我们提到ClickHouse单机能力非常强,几十亿数据单机就可以搞定,如果存量数据几十亿可能不够,要是热点数据几十亿条的数据量就够了,所以通过这种方式,我们可以实现ClickHouse从原先的分布式集群变成比较简单的小的集群,避免ClickHouse分布式运维比较困难的问题。

  其实ClickHouse本身有一套机制来做,我们可以直接借助TTL机制实现,如果我们用了ClickHouse的TTL机制,热数据和冷数据都是可以在里面被查询到的,通过这种方式我们可以实现当数据进入ClickHouse的时候是在热数据里面,能够给大家提供一个非常快的查询速度,数据变冷之后ClickHouse自动迁移到冷存储,并且在某一天需要用,你可以直接使用ClickHouse的能力,不需要传统的方案把数据迁移到冷存储以后,我用的时候需要重新把数据迁回热存储,通过ClickHouse就不需要做这种工作。

  ClickHouse TTL机制就是建表的时候通过指定这样一个表达式,图上列出来的语义就是7天内数据进入VHot,14天数据进入VCode,14天以上的数据删除,可以实现冷热分离,VHot和VCode是事先配好的,Hot可以配置成SSD或者本机磁盘,Code配成Read磁盘或者SSD硬盘,通过这种方式ClickHouse就会自动按照你的规则,把数据进入SSD的进入SSD,进入HDD的进入HDD,但这个机制是作用于单机的,我们可以通过Rate控制器大大提升数据库单机处理能力,官方建议可以使用Raid10或者Raid50的方式。其实这个机制有一个区别就是作用于单机,即使是通过Raid扩充磁盘的容量,但磁盘的大小依然是有限的,滴普碰到的业务中就会出现一些冷数据非常大,比如上PB的这种情况,通过普通磁盘其实是很难处理的。

  在这种情况下滴普就去找了一些解决方案,最终找到了这样一个架构,通过JuiceFS+Redis+MinIO。其实这个客户端可以把底层存储变成我们的POS文件系统,把远端的存储底层变成挂载到某个服务器的本地磁盘。JuiceFS存储引擎支持很多,比如一些耳熟能详的引擎,AWS的S3,阿里云的OSS、BOS和COS之类,各大云厂商的对象存储都支持,并且支持S3协议。MinIO是开源对象存储工具,支持S3协议,所以MinIO可以和JuiceFS配合使用。JuiceFS本身需要存储源数据,可以选择MySQL也可以选择其它的,我们实测下来发现Redis的性能比较强,所以最终使用了这样一个架构,通过JuiceFS将我们的MinIO挂载到本地磁盘,同时把云数据存储到Redis,通过这个架构把VCode做了无限扩展,支持上PB级别的冷数据。

  我们对上面的架构做了性能测试,使用客户一天的数据,某个客户一天的数据大概是400GB,ClickHouse会做一些压缩,压缩之后大概是80GB左右,我们使用32CPU和10GB带宽的内网环境,然后做了一个性能测试。首先是6条语句测试,其中有3条都是在100-200毫秒左右就返回的结果,如果是在SSD上查询就是这样一个结论。数据完全迁移到冷存储以后,这种情况直接通过ClickHouse查询,发现冷数据执行时间就比较长了,最长的是70多秒,但客户是能够忍受这个时间的,因为客户的冷数据很少会去查到。通过这种方案实现冷热分离存储,同时把冷数据和热数据放到同一个ClickHouse里面管理。

  其实这个架构也存在一些限制,比如引入了Redis和JuiceFS,整体系统稳定性下降,非常容易出现单点故障,如果Redis挂了,整个系统就崩了,源数据找不到了,并且我们发现因为ClickHouse存储引擎设计的原因,经常出现崩溃的情况。

  我们还有一个发现的点,冷存储数据是没有办法被其它引擎使用的,因为ClickHouse在做数据迁移的时候把热存储迁移到冷存储,默认的是Partition,ClickHouse默认是按照Part,因为存储引擎已经把刚才提到的数据做了重新组织,有了自己的格式,这种格式是没有办法被其它的引擎使用的,如果我们用一些传统的方案,ClickHouse数据导出成其它的格式,可以直接通过Spark SQL或者Hive查询,如果用了ClickHouse的TTL机制,导出磁盘里的冷数据是没有办法被其它引擎使用的,应用这套架构的时候可能需要按照这两个点去做考虑。

  从热到冷之后就是从冷到热,这是很多企业更倾向的方式,原因就是很多企业已经有大数据系统,图上的数据冷存储过程可以理解为客户已经在大数据里建立好的数据处理流程,客户可能已经在这个过程中做了数据的清洗,ODS到DWD的转换,客户更倾向的是怎样解决传统数据库Hive或者Spark查询速度慢,并发查询比较低的问题,在这种情况下ClickHouse的出现至少可以帮助客户解决这个问题,那么这种架构就需要通过数据迁移机制完成冷存储的数据导入ClickHouse的过程。

  ClickHouse其实也有一些内置功能,通过这个语法可以直接把S3挂载成内部表,就是当成ClickHouse内部表来用。

  我们可以直接查询外部表,好处是简单,限制是并发度不行,可能导致多次数据传输,因为数据毕竟不存储在本身,所以做计算的时候肯定需要把数据从冷存储直接迁过来,如果并发一多,访问的是同一批数据,可能导致同一批数据在2个进程中被传输,那么并发度就高不起来,因为带宽受限,受限于具体的外部表形式可能需要便利数据。

  怎么理解呢?ClickHouse速度快的原因就是对数据做了重新组织,然后做了一些索引,执行某些SQL查询的时候不需要便利所有数据,只要数据有索引,只需要读一部分数据,其实这是ClickHouse速度快重要的原因,因为减少了IO时间,但前提是对数据做了重新组织。大家可以思考一下,如果是一个CSV Excel文件没有做任何索引,必须要对整个表进行便利,也就没有办法使用ClickHouse查询速度快的特点,一般我们只建议客户用在维度表或者自检表,就是这种表格数据经常会变,某个用户的权限表可能经常变,同时也有一个好处就是数据量比较小,不像明细表和订单表可能里面有上百亿条数据,自检表或者维度表经常会发生变更,同时数据量比较小的情况,我们可以直接用外部表的方式去做,避免ClickHouse不支持单条数据更新的情况,直接查询外部表并不是性能不行我们就不用,某些场景下维度表用这种方式去做是非常合适的。

  迁移数据有两种:一种是通过第三方工具迁移,另一种是通过SQL命令迁移。就像刚才提到的DCT,讲一讲拿到ClickHouse怎么去做命令迁移。

  ClickHouse可以直接把Select的结果直接导入新表里面,那么我们就可以通过这种机制实现从外部表导入内部表,只需要Insert Into内部表然后Select外部表就可以直接把外部表导入内部表,所以通过ClickHouse自身携带的机制可以很快地完成数据的迁移,但使用ClickHouse命令迁移,实测中碰到了两个频次非常高的坑:一个就是Too Many Parts,就是当我们的外部数据源返回的顺序和配置的分区顺序不一致的时候会发生,因为ClickHouse使用SLM算法,按照分区的形式来做。

  大家可以考虑一个场景,我的数据插入的顺序是今天的数据、昨天的数据、今天的数据、昨天的数据,极端情况下就是这样的数据,表的分区是按照天,那么对这种情况,第一条数据进来的时候ClickHouse会创建今天的分区,正式默认情况下会等待第二条数据,这是昨天的数据,不是今天的数据,所以必须把之前创建的今天的数据结束,重新创建昨天的数据的分区,第三条数据又是今天的数据,但当前已经是昨天数据的分区,所以今天这条数据还是没有办法插入当前打开的分区,依然需要把之前的分区关闭,创建一个新的分区,那么插入1000条数据就会创建1000个分区,因此出现Too Many Parts的问题。

  其实Too Many Parts不光是出现Insert Into Select的语法,本质的原因就是刚才提到的ClickHouse在处理插入的时候会按照分区创建Part,创建太多Part的时候默认情况下会在未来某个时间合并,如果没有来得及合并的情况下,创建太多的分区,就会出现Too Many Parts,这很像平时我们讲的笑话,一个水池用水管不停地往里加水,同时塞子打开不停地往外漏水,什么时候能满?Too Many Parts也是来自于这个模型,ClickHouse会在某个时间合并Part,但也是需要一定时机的,合并速度跟不上创建速度,最终就会出现Too Many Parts的情况。

  解决方案也很简单,SQL加上Order By,根据分区规则可以一定程度上解决,至少刚才说的那种情况,我的数据是今天的和昨天的,要是用Order By的话很有可能只创建2个分区,最后一次性写入,避免这种Too Many Parts的问题。

  Memory Limit其实也和ClickHouse的执行逻辑有关,Insert Into Select的逻辑是先Select,但在处理Select语句的时候默认把所有的数据全部载入内存,然后去做Insert操作,就是当你的数据量非常大的时候Select一个外部表,超出了你设置的内存大小之后,ClickHouse就有了Memory Limit,不仅是Insert Into,执行逻辑其实引发了非常多的Memory Limit的问题,Joining也会出现Memory Limit,其实和执行逻辑有关。

  以上的两个问题其实都暴露出了一个点,就是我们要解决上面两个问题怎么办?

  需要通过系统体系化的迁移方案去做,不是说我这里有一个外部表,要把外部表数据迁移到内部表,无脑地通过Insert Into Select就可以了,必须按照内部表的分区和各种情况去做一个Chain的方案,按照月分区或者按照天分批次,不是无脑地直接Insert Into,或者是上面的那句话详细展开来说。

  我们滴普有一个产品叫做DBMS4CK,因为具备一键安装ClickHouse集群和运维的能力,同时具备SQL编辑器,也有一个Notebook功能,此外还有两个用得非常多的功能:针对所有SQL进行统计分析,然后根据滴普进行优化建议,比如增加一些投影的工作,还有一个很强大的能力就是针对ClickHouse设计,因此把很多ClickHouse的独有功能做成傻瓜式想到,Insert Into Select的计划,可以解决刚才提到的两个问题,就是根据创建的Insert Into的逻辑和表格情况、表格数据量、分区数据量,怎么分区的创建一个Select Into的执行计划。

  Heated MergeTree就是一个自研引擎,类似于Distributed引擎,其实也是在业界被大家吐槽比较多的点,因为所谓的Distributed就是一个代理,背后一定是跟了2个物理表,就是在2个ClickHouse集群单独使用,只是某个机器上创建Distributed表以后去做代理,本质上其实是把查询分发到2台ClickHouse机器来做查询,所以我们也参照了Distributed的时限,就是解决我们认为的冷热问题,逻辑就是ClickHouse创建一个内部表和外部表,同时构建Heated Merge Tree引擎,后面去查的话就会自动处理查询请求,逻辑是通过构建查询计划的AST,通过识别AST里面碰到的情况,改变查询计划,比如发现现在查询的HeatedMap出现没有出现在SSD,在这种情况下就会把查询结果改变成去查外部表,同时也有一个HeatMap,就是每查一次HeatMap就会去看这次查询数据是在热存储还是冷存储。

  通过Heat升温的功能,大家可以理解为数据一开始是冷的,每查一次数据就会渐渐变高,温度变高到一定程度就会认为这是热数据,然后在这种情况下HeatMap就会自动把这个数据从冷数据迁移到热数据,一段时间不查温度就会渐渐降低,降低到一定程度就会从热存储重新恢复进冷存储,所以通过这种方式能够解决刚才提到的问题,通过升温和降温措施创建一个更加灵活、更加面向业务的冷热分离存储引擎。

  以上就是我们对ClickHouse冷热分离存储的实践和建议,后面再讲三个案例:

  我们客户有一个BI架构,就是刚才提到的明显的从冷存储到热存储的案例。以前BI系统可能是需要对MySQL和Hive,通过引入ClickHouse去做查询,其它的数据通过数据迁移到ClickHouse,然后在里面做数据的清洗和建模,最后形成我们所谓的缓冲表,缓冲表就是面向BI系统,BI系统的交互式分析都在ClickHouse完成。

  这里就用到ClickHouse非常重要的能力,因为查询速度非常快,并且也避免了数据延迟,因为ClickHouse只是数据缓冲,并不存储任何数据,可以理解为所有数据都是副本,真正的数据都在具体的业务系统,所以不需要去做一些数据的重构,只需要查询的时候把数据导入进来,用完其实就可以删除。

  我们某个IoT客户的数据分析,其实是用ClickHouse取代Flink,我们都知道如果用Flink本身是不带数据存储的,要是用kappa架构就必须在kafka缓存,缓存7天也好,缓存30天也好,但极大地增加了kafka的存储量。我们这个客户的数据量大概是每天400GB左右,要求是数据要保存30天,大概是有10TB左右的数据,如果都放在kafka其实是非常庞大的,所以在这种情况下我们使用ClickHouse取代Flink。因为ClickHouse本身具备数据存储能力,kafka只需要承担数据缓冲削峰的作用,避免IoT设备之间对ClickHouse影响造成崩溃。

  前面如果跟的不是IoT设备的话,kafka可以直接去掉,直接把数据写入ClickHouse,因为IoT设备需要kafka去做协议转换。其实这也是典型的数据写入以后不会修改,也是一个非常好的使用ClickHouse的能力,体现了热数据到冷数据的架构,ClickHouse的数据变了以后会写入HDFS。

  最后一个案例就是零售客户的实时分析,我们也会加入ClickHouse替代,就是Flink后面用户画像和实时报表都是基于ClickHouse构建,通过这种架构的优点就是降低Flink的集群压力,用户画像经常写SQL语句,如果用传统的方式Flink的资源使用效率比较高,所以资源占用率比较大,运维也很方便,每次新增一个用户画像都需要对Flink去做重新部署,通过引入ClickHouse降低了Flink集群的数量,原来的40台集群变成5-6台集群,只需承担数据迁徙的工作,其它的都由ClickHouse承担,其次也降低了运维的难度。Flink一方面会把数据写入ClickHouse,另一方面会把数据写入Hive。

  讲完三个案例之后,我们对滴普这么多年做ClickHouse做一些总结。

  使用ClickHouse首先一定要避免点查和全表扫描,因为这是非常消耗ClickHouse时间的,默认一级索引可能会加,但很多人会忽略二级索引。导入ClickHouse可以是传统表,但查询的一定要是宽表,越宽越好。分区粒度不能太小,否则ClickHouse会出问题。善于物化视图、投影,ClickHouse之所以功能强是因为提供了很多功能,如果用好的话对性能提升是非常大的。我们只对分布式表做查询,不要去做写入,直接用统计表去写,用好ClickHouse的代码,一定要避免小批量写入,就是一批最好的数据量一次写了1000条,不要一条一条地写,否则就会出现Too Many Parts的情况。

  最后介绍一下滴普FastData产品架构,这是滴普服务客户的过程中沉淀出的一套数据智能的一体化架构,主要解决的是企业传统数据库复杂的问题,ClickHouse其实是作为缓冲层来做,大部分的数据在底层经过其它大数据处理以后,对外需要做交互查询,数据都会接入SenseHouse计算引擎,通过这种方式就是为企业降低成本、解决企业业务工程师和数据工程师的Gap。

特别提醒:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
0
相关文章