亿级数据库毫秒级查询?看完这⼀篇,海量数据赋能你也⾏
策划 | 徐瑛
作者 | 王强
近年来,随着⼤数据分析技术的普及和物联⽹产业的兴起,越来越多的企业开始重视海量数据的收集和分析处理活动,希望从庞⼤的数据资料中挖掘出⾼价值的信息和洞见。⽽在数据规模快速膨胀的同时,企业对数据处理平台的软硬件基础设施也提出了更⾼的要求,并在这⼀领域催⽣了很多⾼⽔平的前沿技术变⾰。
在这样的趋势下,由俄罗斯Yandex 开发的⼀款名为Clickhouse 的数据库产品就在众多竞争者中脱颖⽽出,凭借⼗亿乃⾄百亿⾏的数据规模下依旧具备秒级返回能⼒的卓越性能,赢得了全球众多⼤⼚的青睐。Clickhouse 究竟有怎样的独门绝技,如何做到如此强悍的性能表现,实践中⼜是如何在主流云平台上部署和优化的?9 ⽉22 ⽇,来⾃京东智联云云产品研发部的架构师王向飞⽼师做客 InfoQ,介绍了 Clickhouse 数据库在京东智联云的落地应⽤与优化改进经历,为想要深⼊了解 Clickhouse 的⼩伙伴们送上了⼀堂⼲货满满的技术分享课程。
本⽂总结⾃王向飞⽼师的在线公开课:《Clickhouse 在京东智联云的⼤规模应⽤和架构改良》。
批量更新sql语句
1什么是 Clickhouse?
根据数据库处理的业务数据量,以及处理数据的不同⽅式,⼈们把数据库分为OLTP 和OLAP 两⼤类型。由俄罗斯Yandex 搜索引擎公司开发并开源的Clickhouse 数据库,其初始定位就是Yandex 内部的分析型数据库,符合OLAP 类型数据库的实现特点。Clickhouse 的性能超过了很多流⾏的商业数据库,已经得到了包括CloudFlare、Spotify、阿⾥云、腾讯云、京东智联云、今⽇头条、携程等诸多头部⼤⼚的采⽤。
OLTP vs OLAP
常见的 Oracle、MySQL 等数据库都属于 OLTP 类型,也就是 On-Line Transaction Processing,联机事务处理。OLTP 数据库处理请求和数据时对延迟要求很⾼,并且要保证数据的完整性和⼀致性。此类数据库是⾯向最终客户的,需要具备 7x24 不间断服务能⼒。
OLAP 的含义是联机分析数据处理,这种数据库需要存储海量、但很少更新修改的数据,主要⽤于多维度历史数据分析统计⽬的。出于这种需求,OLAP 数据库需要确保⾜够⾼的查询效率,⾄少 90% 的请求要在很短的时间内返回。
另⼀⽅⾯,OLAP 数据库并不是直接⾯对最终客户,⽽是更关注数据吞吐,要求海量数据尽快持久化,
为业务决策、战略定位和分析、个性化推荐等任务提供分析统计能⼒,所以这类数据库中的查询⼀般都是较低频的。
Clickhouse 的关键特性:列式存储
基于OLAP 数据库的特点,Clickhouse 采⽤了基于列的数据存储引擎。传统的⾏式数据库在存储信息时,是在数据库中按顺序逐个记录的。以⽤户注册信息为例,⾏式数据库会将每个⽤户的姓名、职业、年龄等数据依次记录下来。当业务需要查注册⽤户的职业或年龄分布时,数据库需要打开所有存储⽤户注册信息的⽂件,遍历全部数据⾏,依次挑出所有职业和年龄信息进⾏汇总。使⽤这种⽅式,查询遍历的数据往往远⼤于所需的数据⼤⼩,对IO 能⼒会造成严重浪费。
相⽐之下,列式存储会将数据分为多个属性列,例如⽤户注册信息分为职业、年龄等多个属性,并按这些属性列分为多个⽂件分别存储。这样当查询需要获取其中某些属性的数据时,只需查对应⽂件即可完成,⼤⼤节约了 IO 需求。
以⼀个包含 1 亿条数据的测试表为例,⼀个简单的 count 查询在 MySQL 上需要两分多钟,⽽在列式存储的 Clickhouse 数据库上仅⽤不⾜ 1 秒就返回了结果。那么,Clickhouse 具体都使⽤了哪些技术来实现如此⾼的效率提升呢?
2亿级查询,毫秒级返回:Clickhouse 背后的秘密
B+Tree vs MergeTree
在深⼊研究 Clickhouse 之前,我们⾸先以传统的 MySQL InnoDB 的存储格式来做对⽐。
从 InnoDB 的逻辑结构图可以看到,在 InnoDB 中所有数据会被放在表空间内。表空间可以看作是 InnoDB 的逻辑最⾼层,由多个段组成,段⼜分为数据段、索引段。数据⽣成时按顺序写⼊数据段,随着数据记录的增多,InnoDB 会将⼀些主键值放到索引段内以实现快速定位。
随着数据量不断增多,数据库形成了名为B+Tree 的树状结构。这个树有层级结构,会横向⽣长,其查询的复杂度取决于树的⾼度。B+Tree 的数据节点⼀般存储主键值,根据主键查时可以通过叶⼦节点⼤概定位数据页,之后直接读取数据页即可。
⽽ Clickhouse 的数据架构类似关系型数据库,其中包括了解析器,主要负责将 SQL 语句通过词法分析、语法分析等转换成计算机可读的抽象语法树。另外还有优化器,逻辑优化负责优化抽象语法树的逻辑,⽐如简化⼀些长难运算表达式,做⼀些语义优化等。物理优化则负责⽣成可以直接执⾏的物理执⾏计划,指导数据库管理系统如何获取数据表、如何进⾏数据 join、排序等等。
Clickhouse 的物理执⾏计划可认为是⼀个数据流图,也就是数据的有向⽆环图。在这个图⾥,数据从⼀个管道传到另⼀个管道,也就是从⼀个操作符传到另⼀个操作符。查询执⾏器是⽤来执⾏计划的引擎,它会从存储引擎中取出数据,并返回给客户端。
如上图,Clickhouse 在启动时加载配置信息,然后根据不同的解析协议监听不同的服务端⼝。客户端发送来SQL 请求后,⾸先它会对SQL 进⾏语法解析,然后⽣成抽象语法树,并进⾏⼀系列的逻辑优化、物理优化,⽣成执⾏计划。接下来由不同的执⾏器根据SQL 请求来将执⾏计划分发到本地或远端的存储引擎,从存储引擎中取出数据。数据经过⼀系列的计算加⼯后返回给客户端,客户端就可以输出缓冲区读取查询结果。
MergeTree 存储过程
相⽐InnoDB 使⽤的B+Tree,Clickhouse 使⽤的是MergeTree 存储引擎来存储数据。这⾥以⼀个Clickhouse 表为例:
本例中,我们根据出⽣⽇期做⼀个数据分区,主键选⽤⽤户的名字,并设置SETTINGS index_granularity=3。表建成后插⼊ 10 条记录,分为 2001 年 3 ⽉和 2001 年 2 ⽉两个数据区间。表建完、数据写完以后,Clickhouse 默认会在数据⽂件存放路径下建⼀个相应的表名:
这⾥可以看到,10 条数据分了两个⽂件夹来存储。⽂件夹命名时,其第⼀部分是分区键,也就是出⽣⽇期;1_1(2_2)代表每个数据分区内数据块最⼩块和最⼤块的编号。最后的数字 0 代表合并层级。
上图是 MergeTree 中对 Data part 进⾏元数据管理的结构体。其中,partition id 代表数据所处的分区 i
d;min block、max block 代表数据写⼊的版本信息——⽤户每次批量写的数据都会⽣成⼀个 Data part,同⼀批写⼊的数据会被标记为唯⼀的block number。MergeTree 存储引擎后台会定期通过异步任务合并数据,且只会合并位于同⼀个数据分区内的数据,还要求 min block 和 max block 数据区间必须是连续⾮重合的。
第四个 level 字段默认新插⼊的数据都是 0,之后会随着合并次数的增加在原来的基础上依次增⼤。下⾯的 mutation 字段在数据更正时使⽤。如果要进⾏数据的更正操作,Clickhouse 会默认给 mutation 字段进⾏标记和更新。
虽然测试数据只有⼀张表 10 条数据,但它会在磁盘⽬录上⽣成⼤量⽂件。具体来说,Clickhouse 默认每⼀个列⽣成⼀个⽂件,默认数据⽂件放在 bin ⽂件⾥。每⼀个数据分析⽬录下⽣成⼀个 count ⽂件,记录分区⾥有多少⾏数据。
本例中,建表时设置的 SETTINGS index_granularity 设为 3。插完数据以后观察主键索引,可以发现它会把主键以每 3条记录为⼀个区间,将主键信息存储在 primary.idx ⾥。
条记录为⼀个区间,将主键信息存储在 primary.idx ⾥。
结合前⽂例⼦来看数据全景。假设下⾯绿颜⾊的就是要写⼊的⼀批数据,存放⽤户的名字;假设每个
名字占⽤  4 个字节,可以看到绿颜⾊上边有⼀个 granule,写的是 8192。指定 granule 是 8192 之后,数据在写⼊时会放到⼀个具有缓冲区的 OutPort 流中,按照⼀个 granule ⼀个 granule 来写;写完第⼀个 granule,当发现这个缓冲区内数据⼤⼩超过64KB,这时就会把数据进⾏压缩落盘,放在下边的粉红⾊⽂件块⾥。落数据块时会先写⼀个⽂件头,⽂件头由三部分组成,如上图所⽰。
第⼆段8192 的数据,压缩完之后数据块可能⽐较长⼀些;可以发现,数据每次写⼊就会产⽣两个⽂件。⼀个是bin ⽂件,也就是压缩后的数据⽂件。另⼀个⽂件就是主键index ⽂件。但这样以来,在数据查询时不知道数据究竟在数据⽂件⾥的哪⼀块,不知道该怎么拆分 bin ⽂件,如果把整个 bin ⽂件都加载内存以后扫描,效率是会很差的。为了解决这个尴尬的问题 Clickhouse 引⼊了 mrk ⽂件。写数据⽂件的时候会把 bin ⽂件头信息写到 mrk ⽂件⾥。⽐如说第⼀块数据写完之后,会把起始位置、解压缩后的位置、解压缩前的位置放在mrk 这个⽂件块⾥, 作为⼀⾏记录。查询时直接根据主键 index 记录的偏移量到对应的 mrk 记录的某段数据的起始位置,之后读取数据即可。
MergeTree 也有⼀些异步任务处理,主要有三部分:⾸先是定期把⼀些有问题的、提交失败的、或写失败的数据⽂件清理掉;然后是定期把⼀些⽐较琐碎的插⼊语句⽣成的⼩⽂件块合并为⼤的⽂件块;还有偶尔有⼀些更正,例如数据的更新删除⽣成的临时⽂件,MergeTree 也会把对应的数据⽂件汇总成⼀个⽐较⼤的 Part。
3京东智联云如何基于 K8s 部署 Clickhouse
智联云选择 K8s 部署的原因
京东智联云基于现有的云平台部署 Clickhouse 时,是基于 K8s 团队提供的强⼤运维调度平台来实现的。之所以选择基于 K8s 来部署有⼏⽅⾯原因:
•⾸先,K8s 可以屏蔽底层的环境差异,使⽤户⽆需再具体关注主机⽹络、存储、API 接⼝等变化,只需将精⼒集中到数据库管理开发任务上。
•其次,构建这个 JCHDB 平台并不只是为京东智联云内部使⽤。这个平台构建完成后,不仅可以在公有云上给客户提供服务,并且在私有化部署或者跨云部署时,都可以完全不⽤对架构做任何修改直接部署。
智联云部署 Clickhouse 的流程
数据库系统是⼀个⽐较复杂的有状态业务系统,分⽚跟副本之间有状态关系,这时候就需要维护Pod 与Pod 之间的关系,K8s 为此提供了operator 功能。京东智联云⼆次开发了开源的Clickhouse operator,丰富了operator 的API 功能,并且为安全⽣产的需要给它打上⼀些额外的标签,控制 Pod 调度的亲和性,防⽌主从副本落到同⼀个物理机上。
这个operator 开发部署完毕,安装到K8s 以后,K8s 就有了管理调度Clickhouse 状态的能⼒。在它的
外部会借助helm 系统,将提前定义好的⼀些表单发送给 K8s,由 K8s 来根据表单⾥定义的这些参数来创建需要的实例。
除了创建最基础的Clickhouse,如果需要有复制关系引擎,它也会⼀同创建Zookeeper;同时为了丰富监控能⼒,便于 DBA 进⾏服务器的运维监控,它还会创建⼀个 Promethus,还有可视化的 Grafana。这样它就可以直接在 VPC ⾥通过 Grafana 来监控数据库的运维状态。
这个服务还会创建⼀个绑定headless service ip 地址的域名,⽤户可以通过这个数据库域名直接连到这⼀套Clickhouse 系统上。由上图右可见,这个 Pod 的底层存储使⽤了京东的云硬盘,它会在建 Pod 时申请⼀个 PVC 控制器,PVC 控制器会绑定京东云硬盘。这样就形成了计算与存储分离的架构,可以进⼀步提升计算能⼒。
京东智联云⽬前在⾼性能、⾼可⽤和可扩展层⾯上都有⾃⼰的特⾊:
•⾼性能⽅⾯,智联云采⽤最新⼀代的云主机,CPU 最⼤可以⽀持 64 核⼼,单个 Pod 最⼤可以扩展到 512G 内存。•⾼可⽤⽅⾯,智联云借助 K8s 调度管理平台,发现有 Pod 不可⽤时 K8s 会⾃动将这个 Pod 剔除。同时平台会新建⼀个 Pod,基于 StatefulSet 机制将被删除的 Pod 所绑定的云盘挂到新 Pod 上。这样如果有实例由于某些硬件原因出现问题,可以在分或秒级完成实例替换。
•可扩展⽅⾯,智联云基于云主机、云硬盘的⼀些基础组件,提供了⼀些灵活的扩展接⼝,可以直接在原地扩容。智联云⽀持热扩容,可以在不影响⽤户使⽤的前提下,在分钟级甚⾄秒级就可以完成 CPU 数量、内存容量或磁盘空间
联云⽀持热扩容,可以在不影响⽤户使⽤的前提下,在分钟级甚⾄秒级就可以完成 CPU 数量、内存容量或磁盘空间的扩容。
京东智联云还提供了完善的监控体系,可以帮助DBA 更好地观察的数据库的运⾏状况。平台不但提供了service 级别的数据库监控,同时还能把所有Pod 所使⽤的磁盘空间、CPU、内存都展⽰给⽤户。有了这些信息,⽤户可以更加直观地观察到每⼀个 Pod 的压⼒分布情况,进⽽⽅便灵活地调整数据压⼒,避免某个 Pod 出现数据瓶颈。
根据这些监控的⽬标,⽤户可以灵活地定义告警信息。智联云⽀持多维度数据告警,可以通过邮件、短信、等形式告警。
4总结
Clickhouse ⾃⾝具备强⼤的数据处理能⼒,还能很好地兼容SQL 语句。但在实际设计和使⽤过程中,不能仅仅把它当成⼀个传统关系型数据库的增强替代品,这样可能会限制Clickhouse 的潜⼒发挥。企
业需要对传统的数据仓库、设计理念,以及数据上下游的流转⽅式做出改进,发挥想象⼒和创造⼒,更好地利⽤Clickhouse 的列式存储、并⾏计算等数据能⼒,为数据业务创造更⼤价值。
京东智联云的 Clickhouse ⽬前正在产品公测,欢迎⼤家使⽤。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。