ClickHouse vs StarRocks 选型对比
作者:懂的都懂
面向列存的 DBMS 新的选择
Hadoop 从诞生已经十三年了,Hadoop 的供应商争先恐后的为 Hadoop 贡献各种开源插件,发明各种的解决方案技术栈,一方面确实帮助很多用户解决了问题,但另一方面因为繁杂的技术栈与高昂的维护成本,Hadoop 也渐渐地失去了原本属于他的市场。对于用户来说,一套高性能,简单化,可扩展的数据库产品能够帮助他们解决业务痛点问题。越来越多的人将目光锁定在列存的分布式数据库上。
ClickHouse 简介
ClickHouse 是由俄罗斯的第一大搜索引擎 Yandex 公司开源的列存数据库。令人惊喜的是,ClickHouse 相较于很多商业 MPP 数据库,比如 Vertica,InfiniDB 有着极大的性能提升。除了 Yandex 以外,越来越多的公司开始尝试使用 ClickHouse 等列存数据库。对于一般的分析业务,结构性较强且数据变更不频繁,可以考虑将需要进行关联的表打平成宽表,放入 ClickHouse 中。
相比传统的大数据解决方案,ClickHouse 有以下的优点:
-
配置丰富,只依赖与 Zookeeper
-
线性可扩展性,可以通过添加服务器扩展集群
-
容错性高,不同分片间采用异步多主复制
-
单表性能极佳,采用向量计算,支持采样和近似计算等优化手段
-
功能强大支持多种表引擎
StarRocks 简介
StarRocks 是一款极速全场景 MPP 企业级数据库产品,具备水平在线扩缩容,金融级高可用,兼容 MySQL 协议和 MySQL 生态,提供全面向量化引擎与多种数据源联邦查询等重要特性。StarRocks 致力于在全场景 OLAP 业务上为用户提供统一的解决方案,适用于对性能,实时性,并发能力和灵活性有较高要求的各类应用场景。
相比于传统的大数据解决方案,StarRocks 有以下优点:
-
不依赖于大数据生态,同时外表的联邦查询可以兼容大数据生态
-
提供多种不同的模型,支持不同维度的数据建模
-
支持在线弹性扩缩容,可以自动负载均衡
-
支持高并发分析查询
-
实时性好,支持数据秒级写入
-
兼容 MySQL 5.7 协议和 MySQL 生态
StarRocks 与 ClickHouse 的功能对比
StarRocks 与 ClickHouse 有很多相似之处,比如说两者都可以提供极致的性能,也都不依赖于 Hadoop 生态,底层存储分片都提供了主主的复制高可用机制。但功能、性能与使用场景上也有差异。ClickHouse 在更适用与大宽表的场景,TP 的数据通过 CDC 工具的,可以考虑在 Flink 中将需要关联的表打平,以大宽表的形式写入 ClickHouse。StarRocks 对于 join 的能力更强,可以建立星型或者雪花模型应对维度数据的变更。
大宽表 vs 星型模型
ClickHouse:通过拼宽表避免聚合操作
- 在 ETL 的过程中处理好宽表的字段,分析师无需关心底层的逻辑就可以实现数据的分析
- 宽表能够包含更多的业务数据,看起来更直观一些
- 宽表相当于单表查询,避免了多表之间的数据关联,性能更好
- 宽表中的数据可能会因为 join 的过程中存在一对多的情况造成错误数据冗余
- 宽表的结构维护麻烦,遇到维度数据变更的情况需要重跑宽表
- 宽表需要根据业务预先定义,宽表可能无法满足临时新增的查询业务
StarRocks:通过星型模型适应维度变更
- 当小表与大表关联时,可以使用 boardcast join,小表会以广播的形式加载到不同节点的内存中
- 当大表与大表关联式,可以使用 shuffle join,两张表值相同的数据会 shuffle 到相同的机器上
- 为了避免 shuffle 带来的网络与 I/O 的开销,也可以在创建表示就将需要关联的数据存储在同一个 colocation group 中,使用 colocation join
CREATE TABLE tbl (k1 int, v1 int sum) DISTRIBUTED BY HASH(k1) BUCKETS 8 PROPERTIES( "colocate_with" = "group1" );
目前大部分的 MPP 架构计算引擎,都采用基于规则的优化器(RBO)。为了更好的选择 join 的类型,StarRocks 提供了基于代价的优化器(CBO)。用户在开发业务 SQL 的时候,不需要考虑驱动表与被驱动表的顺序,也不需要考虑应该使用哪一种 join 的类型,CBO 会基于采集到的表的 metric,自动的进行查询重写,优化 join 的顺序与类型。高并发支撑
ClickHouse 对高并发的支撑
为了更深维度的挖掘数据的价值,就需要引入更多的分析师从不同的维度进行数据勘察。更多的使用者同时也带来了更高的 QPS 要求。对于互联网,金融等行业,几万员工,几十万员工很常见,高峰时期并发量在几千也并不少见。随着互联网化和场景化的趋势,业务逐渐向以用户为中心转型,分析的重点也从原有的宏观分析变成了用户维度的细粒度分析。传统的 MPP 数据库由于所有的节点都要参与运算,所以一个集群的并发能力与一个节点的并发能力相差无几。如果一定要提高并发量,可以考虑增加副本数的方式,但同时也增加了 RPC 的交互,对性能和物理成本的影响巨大。在 ClickHouse 中,我们一般不建议做高并发的业务查询,对于三副本的集群,通常会将 QPS 控制在 100 以下。ClickHouse 对高并发的业务并不友好,即使一个查询,也会用服务器一半的 CPU 去查询。一般来说,没有什么有效的手段可以直接提高 ClickHouse 的并发量,只能考虑通过将结果集写入 MySQL 中增加查询的并发度。StarRocks 对高并发的支撑
相较于 ClickHouse,StarRocks 可以支撑数千用户同时进行分析查询,在部分场景下,高并发能力能够达到万级。StarRocks 在数据存储层,采用先分区再分桶的策略,增加了数据的指向性,利用前缀索引可以快读对数据进行过滤和查找,减少磁盘的 I/O 操作,提升查询性能。
数据的高频变更
ClickHouse 中的数据更新
StarRocks 中的数据更新
特点 | 适用场景 | |
明细模型 | 用于保存和分析原始明细数据,以追加写为主要写入方式,数据写入后几乎无更新。 | 日志,操作记录,设备状态采样,时序类数据等 |
聚合模型 | 用于保存和分析汇总类(如:max、min、sum等)数据,不需要查询明细数据。数据导入后实时完成聚合,数据写入后几乎无更新。 | 按时间、地域、机构汇总数据等 |
Primary Key模型 | 支持基于主键的更新,delete-and-insert,大批量导入时保证高性能查询。用于保存和分析需要更新的数据。 | 状态会发生变动的订单,设备状态等 |
Unique 模型 | 支持基于主键的更新,Merge On Read,更新频率比主键模型更高。用于保存和分析需要更新的数据。 | 状态会发生变动的订单,设备状态等 |
集群的维护
ClickHouse 中的节点扩容与重分布
- 如果业务允许,可以给集群中的表设置 TTL,长时间保留的数据会逐渐被清理到,新增的数据会自动选择新节点,最后会达到负载均衡。
- 在集群中建立临时表,将原表中的数据复制到临时表,再删除原表。当数据量较大时,或者表的数量过多时,维护成本较高。同时无法应对实时数据变更。
- 通过配置权重的方式,将新写入的数据引导到新的节点。权重维护成本较高。
StarRocks 中的在线弹性扩缩容
ClickHouse 与 StarRocks 的性能对比
单表 SSB 性能测试
测试环境
机器 | 配置 (阿里云主机 3 台) |
CPU | 64 核 Intel(R) Xeon(R) Platinum 8269CY CPU @ 2.5GHz Cache Size: 36608 KB |
内存 | 128G |
网络贷款 | 100G |
磁盘 | SSD 高效云盘 |
CK 版本 | 21.9.5.16-2.x86_64 (18-Oct-2021) |
StarRocks 版本 | v1.19.2 |
测试数据
表名 | 行数 | 说明 |
lineorder | 6 亿 | SSB 商品订单表 |
customer | 300 万 | SSB 客户表 |
part | 140 万 | SSB 零部件表 |
supplier | 20 万 | SSB 供应商表 |
dates | 2556 | 日期表 |
lineorder_flat | 6 亿 | SSB 打平后的宽表 |
测试结果
ClickHouse | StarRocks | |
Q1.1 | 1.022 | 0.37 |
Q1.2 | 0.105 | 0.05 |
Q2.1 | 4.107 | 3.51 |
Q2.2 | 3.421 | 3.06 |
Q2.3 | 3.175 | 2.28 |
Q3.1 | 5.196 | 3.86 |
Q3.2 | 2.159 | 2.88 |
Q3.3 | 1.61 | 1.95 |
Q3.4 | 0.036 | 0.05 |
Q4.1 | 6.304 | 4.75 |
Q4.2 | 1.761 | 1.43 |
Q4.3 | 0.969 | 0.98 |
Q5.1 | 1.107 | 0.45 |
Q5.2 | 2.499 | 1.86 |
Q5.3 | 5.009 | 2.44 |
多表 TPCH 性能测试
测试环境
机器 | 配置 (阿里云主机 3 台) |
CPU | 64 核 Intel(R) Xeon(R) Platinum 8269CY CPU @ 2.5GHz Cache Size: 36608 KB |
内存 | 128G |
网络贷款 | 100G |
磁盘 | SSD 高效云盘 |
StarRocks 版本 | v1.19.2 |
测试数据
表名 | 行数 |
customer | 15000000 |
lineitem | 600037902 |
nation | 25 |
orders | 150000000 |
part | 20000000 |
partsupp | 80000000 |
region | 5 |
supplier | 1000000 |
测试结果
| StarRocks |
Q1 | 0.691s |
Q2 | 0.635s 0.290s |
Q3 | 1.445s |
Q4 | 0.611s |
Q5 | 1.361s |
Q6 | 0.172s |
Q7 | 2.777s |
Q8 | 1.81s |
Q9 | 3.470s |
Q10 | 1.472s |
Q11 | 0.241s |
Q12 | 0.613s |
Q13 | 2.102s |
Q14 | 0.298s |
Q16 | 0.468s |
Q17 | 7.441s |
Q18 | 2.479s |
Q19 | 0.281s |
Q20 | 2.422s |
Q21 | 2.402s |
Q22 | 1.110s |
导入性能测试
数据集
导入方式
CREATE TABLE github_events_all AS github_events_local \ ENGINE = Distributed( \ perftest_3shards_1replicas, \ github, \ github_events_local, \ rand());
CREATE TABLE github_events_hdfs ( file_time DateTime, event_type Enum('CommitCommentEvent' = 1, 'CreateEvent' = 2, 'DeleteEvent' = 3, 'ForkEvent' = 4, 'GollumEvent' = 5, 'IssueCommentEvent' = 6, 'IssuesEvent' = 7, 'MemberEvent' = 8, 'PublicEvent' = 9, 'PullRequestEvent' = 10, 'PullRequestReviewCommentEvent' = 11, 'PushEvent' = 12, 'ReleaseEvent' = 13, 'SponsorshipEvent' = 14, 'WatchEvent' = 15, 'GistEvent' = 16, 'FollowEvent' = 17, 'DownloadEvent' = 18, 'PullRequestReviewEvent' = 19, 'ForkApplyEvent' = 20, 'Event' = 21, 'TeamAddEvent' = 22), actor_login LowCardinality(String), repo_name LowCardinality(String), created_at DateTime, updated_at DateTime, action Enum('none' = 0, 'created' = 1, 'added' = 2, 'edited' = 3, 'deleted' = 4, 'opened' = 5, 'closed' = 6, 'reopened' = 7, 'assigned' = 8, 'unassigned' = 9, 'labeled' = 10, 'unlabeled' = 11, 'review_requested' = 12, 'review_request_removed' = 13, 'synchronize' = 14, 'started' = 15, 'published' = 16, 'update' = 17, 'create' = 18, 'fork' = 19, 'merged' = 20), comment_id UInt64, body String, path String, position Int32, line Int32, ref LowCardinality(String), ref_type Enum('none' = 0, 'branch' = 1, 'tag' = 2, 'repository' = 3, 'unknown' = 4), creator_user_login LowCardinality(String), number UInt32, title String, labels Array(LowCardinality(String)), state Enum('none' = 0, 'open' = 1, 'closed' = 2), locked UInt8, assignee LowCardinality(String), assignees Array(LowCardinality(String)), comments UInt32, author_association Enum('NONE' = 0, 'CONTRIBUTOR' = 1, 'OWNER' = 2, 'COLLABORATOR' = 3, 'MEMBER' = 4, 'MANNEQUIN' = 5), closed_at DateTime, merged_at DateTime, merge_commit_sha String, requested_reviewers Array(LowCardinality(String)), requested_teams Array(LowCardinality(String)), head_ref LowCardinality(String), head_sha String, base_ref LowCardinality(String), base_sha String, merged UInt8, mergeable UInt8, rebaseable UInt8, mergeable_state Enum('unknown' = 0, 'dirty' = 1, 'clean' = 2, 'unstable' = 3, 'draft' = 4), merged_by LowCardinality(String), review_comments UInt32, maintainer_can_modify UInt8, commits UInt32, additions UInt32, deletions UInt32, changed_files UInt32, diff_hunk String, original_position UInt32, commit_id String, original_commit_id String, push_size UInt32, push_distinct_size UInt32, member_login LowCardinality(String), release_tag_name String, release_name String, review_state Enum('none' = 0, 'approved' = 1, 'changes_requested' = 2, 'commented' = 3, 'dismissed' = 4, 'pending' = 5) ) ENGINE = HDFS('hdfs://XXXXXXXXXX:9000/user/stephen/data/github-02/*', 'TSV')
LOAD LABEL github.xxzddszxxzz ( DATA INFILE("hdfs://XXXXXXXXXX:9000/user/stephen/data/github/*") INTO TABLE `github_events` (event_type,repo_name,created_at,file_time,actor_login,updated_at,action,comment_id,body,path,position,line,ref,ref_type,creator_user_login,number,title,labels,state,locked,assignee,assignees,comments,author_association,closed_at,merged_at,merge_commit_sha,requested_reviewers,requested_teams,head_ref,head_sha,base_ref,base_sha,merged,mergeable,rebaseable,mergeable_state,merged_by,review_comments,maintainer_can_modify,commits,additions,deletions,changed_files,diff_hunk,original_position,commit_id,original_commit_id,push_size,push_distinct_size,member_login,release_tag_name,release_name,review_state) ) WITH BROKER oss_broker1 ("username"="user", "password"="password") PROPERTIES ( "max_filter_ratio" = "0.1" );
导入结果
| | 并发数 | 总耗时(s) | 单机平均速率(MB/s) | ck-test01 Server or be CPU峰值/平均值 | ck-test02 Server or be CPU峰值/平均值 | ck-test03 Server or be CPU峰值/平均值 |
clickhouse | 单客户端 | 1 | | | | | |
2 | 13154.497 | 37.20 | 223%/36% | 358%/199% | 197%/34% | ||
4 | 4623.641 | 105.85 | 303%/127% | 1140%/714% | 330%/96% | ||
8 | 3570.095 | 137.07 | 383%/128% | 1595%/1070% | 346%/122% | ||
16 | 3277.488 | 149.32 | 361%/165% | 1599% /1471% | 440% /169% | ||
3客户端 | 1 | 8211/9061/6652 | 73.54 | 352% /144% | 415% /155% | 365% /160% | |
2 | 4501/5075/3452 | 108.74 | 405% /249% | 443% /252% | 430% /265% | ||
4 | 2538/3046/1579 | 192.80 | 980% /492% | 1186 % /523% | 1054 % /477% | ||
8 | 2863/3379/1850 | 170.91 | 1449% /466% | 1229% /464% | 1475% /582% | ||
16 | 2986/3817/1772 | 163.87 | 1517%/466% | 1491% /423% | 1496% /655% | ||
StarRocks | 1 | 6420 | 76.22 | 305%/176% | 324%/163% | 305%/161% | |
2 | 3632 | 134.73 | 453%/320% | 444%/306% | 455%/303 | ||
4 | 3900 | 125.47 | 728%/397% | 363%/659% | 709%/366% | ||
8 | 3300 | 148.28 | 934%/523% | 959%/521% | 947%/520% | ||
16 | 3050 | 160.44 | 824%/408% | 889%%/394% | 850%%/388% |
结论

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
GRPC: 快速创建静态文件下载 Web 服务
介绍 本文介绍如何通过 rk-boot 快速搭建静态文件下载 Web 服务。 什么是 静态文件下载 Web UI? 通过配置文件,快速搭建可下载文件的 Web 服务。 请访问如下地址获取完整教程: https://rkdocs.netlify.app/cn 安装 go get github.com/rookie-ninja/rk-boot 快速开始 rk-boot 提供了一个方便的方法,让用户快速实现网页【浏览和下载】静态文件的功能。 目前,rk-boot 支持如下文件源。如果用户希望支持更多的文件源,可以通过实现 http.FileSystem 接口来实现。 本地文件系统 pkger 1.创建 boot.yaml --- grpc: - name: greeter # Required port: 8080 # Required enabled: true # Required static: enabled: true # Optional, default: false path: "/rk/v1/static" # Optional, default: /rk/v1/stati...
- 下一篇
面试题:如何解决Spring 的循环依赖问题
Spring 的循环依赖问题 什么是循环依赖 什么是循环依赖呢?可以把它拆分成循环和依赖两个部分来看,循环是指计算机领域中的循环,执行流程形成闭合回路;依赖就是完成这个动作的前提准备条件,和我们平常说的依赖大体上含义一致。放到 Spring 中来看就一个或多个 Bean 实例之间存在直接或间接的依赖关系,构成循环调用,循环依赖可以分为直接循环依赖和间接循环依赖,直接循环依赖的简单依赖场景:Bean A 依赖于 Bean B,然后 Bean B 又反过来依赖于 Bean A(Bean A -> Bean B -> Bean A),间接循环依赖的一个依赖场景:Bean A 依赖于 Bean B,Bean B 依赖于 Bean C,Bean C 依赖于 Bean A,中间多了一层,但是最终还是形成循环(Bean A -> Bean B -> Bean C -> Bean A)。 循环依赖的类型 第一种是自依赖,自己依赖自己从而形成循环依赖,一般情况下不会发生这种循环依赖,因为它很容易被我们发现。 第二种是直接依赖,发生在两个对象之间,比如:Bean A 依赖于 ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8编译安装MySQL8.0.19
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS关闭SELinux安全模块
- CentOS6,CentOS7官方镜像安装Oracle11G
- Linux系统CentOS6、CentOS7手动修改IP地址
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7设置SWAP分区,小内存服务器的救世主