本文字数7244估计阅读时间19 分钟作者Alexey MilovidovClickHouse 于 2016 年 6 月 15 日 开源发布至今已有十年。自那时起它已发展成为最受欢迎的开源分析型数据库拥有超过 2000 名贡献者。开放式构建开源项目有 不同的层级。0 级最低层级是将代码向公众公开供阅读但仅此而已。这通常是档案类或博物馆类项目的情况例如 Doom 或 MS-DOS。1 级下一层级是软件通过公共仓库 (public repository) 中的提交 (commits) 进行更新但通常不接受外部贡献者。这也属于开源的范畴。SQLite 和 Ladybird 便是此类项目的代表。2 级接受外部贡献但缺乏透明和开放的开发流程。大多数活跃的开源项目都处于这一层级。3 级具备开放的 贡献指南、任务追踪器、代码审查系统、开发路线图、测试与 CI 系统、发布周期、用户支持以及 文档。我始终追求最高层级。ClickHouse 应当成为以下方面的最佳典范如何构建一个卓越的数据库——如果你想构建一个新的数据库ClickHouse 的源代码和开发实践将是最佳范例。我始终致力于编写高质量代码以供所有人学习借鉴——通过保持其模块化、正交性并配备完善的文档。当代码涉及复杂概念时我会在注释中从零开始进行详细解释从而让读者无需查阅教科书、维基百科或人工智能 (AI)。C 开发学习之地。许多开发者都在寻找代表软件工程前沿的代码库如今 ClickHouse 便是 C 领域最受欢迎的开源项目之一。在这里每个人都能学到既激动人心如 C23又看似“枯燥”如构建系统、持续集成与测试、代码审查实践以及 AI的知识。进行数据结构与性能优化实验的场所。你可以发起一个作为实验性的拉取请求 (Pull Request)即便不以合并为目标——它仍将以与生产版本相同的严格标准进行测试。无论你发现了新的内存分配器、新的压缩库、新的哈希表、数据格式还是新的排序算法都可以将其引入 ClickHouse我们将对其进行全面而深入的检验。项目的路线图甚至包含了一些关于实验性、奇特乃至异想天开事物的章节。在这里你的工作将让你引以为傲。ClickHouse 在更新日志中甚至在数据库内部的 system.contributors 表中都会感谢每一位贡献者有无数的例子表明当贡献者提交了一个功能最初的、不完整的实现时我们会共同努力帮助其完善。即使代码需要完全重写我们也会积极主动地承担责任并且始终将功劳归于最初的作者。因为我们重视你的使用场景以及促成其实现的最初意图。简而言之我们珍视每一位贡献者。开源之前原型与首次提交ClickHouse 的首次提交于 2009 年 5 月 29 日完成这是一项性能优化工作旨在替换 libc 函数localtime、mktime、gmtime它们运行极其缓慢且在性能分析器中频繁出现令我深感困扰。然而这一切都发生在 ClickHouse 项目正式诞生之前。ClickHouse 始于我的一个实验项目当时我正在为一个网络分析系统处理数据。该系统与 Google Analytics 类似负责接收来自网站的页面浏览日志。其实现方案包括 MySQL 数据库、C 数据处理并在 MySQL 无法满足需求时采用 C 自定义数据结构。MySQL 数据库主要用于存储为客户预聚合的报告而自定义数据结构则用于计算用户会话、用户历史等信息。彼时我的体会是——数据量不断增长现有方案捉襟见肘且新数据不断实时涌入。如果无法在五分钟内处理完五分钟的日志数据块系统就会出现延迟。在这种延迟不断累积的压力下我必须在当天工作时间内找到并部署任何能解决问题的创新方案。正因如此我当时不惜一切代价寻找任何可行的解决方案——无论是哪种数据库何种库都来者不拒。我们可以使用 TokuDB 吗同事们使用的 LMDB 能否拯救我们让我们试试 Judy Arrays。午餐时有人提到了 Hadoop我们是否该尝试一下在走廊里偶然听到 LZO 和 QuickLZ不妨一试。如果我们将 HyperLogLog 对象存储在 MySQL BLOBs 中又该如何对它们进行聚合求和呢某个周末我会去研读那本 数据压缩书 或是 事件循环服务器 的相关文档……在稳定数据管道的同时我也在思考可以为产品带来哪些新功能。如果我们记录用户在链接上的点击行为便能在每个页面上展示一个 热力图。如果能记录每次点击在 DOM 中的具体位置我们就能生成一个 点击图。为了 4 月 1 日我曾用 Flash 制作了一个带有红蓝互补色的 3D 点击图。然而更具吸引力的功能是允许用户自由构建报告而非局限于一套预聚合报告的范畴。为了完成这项任务我研究了列式数据库 (column-oriented databases)。我曾从各公司的邮件列表、dbms2.com 等网站以及广告部门的同事那里了解到它们。其核心理念是存储非聚合但结构化的日志并在客户等待页面加载时实时聚合这些日志。我测试了几款 MySQL 扩展Infobright、InfiniDB以及一些独立的分析型数据库Vertica、MonetDB 和 LucidDB。然而它们都未能成功处理每天 1000 亿条记录、每条包含 500 列的数据加载任务。于是我尝试实现一个自定义数据结构的简单原型每天、每个网站的每一列仅存储整数用哈希替代字符串都保存在一个单独的二进制文件中约十亿个文件需使用 XFS 文件系统。该文件采用轻量级压缩每天更新一次有数小时的延迟通过一个 API 进行查询该 API 支持指定分组列、聚合函数、过滤器和排序功能查询以 XML 格式指定。最困难的部分在于如何通过“反聚合” MySQL 中的历史数据来填充新系统并确保聚合结果与原系统保持一致最终由我的同事Evgenii Gatov解决了这一难题。这个简单的原型命名为 OLAPServer于 2008 年 12 月实现2009 年 1 月部署运行成功。我还创建了一个端点使用户能够分析全球互联网数据而不仅仅是单个网站的数据效果出奇地好。例如公司内部的统计部门原本使用内部版本的 MapReduce 处理互联网日志然而公司分析师们很快便转而使用我的服务因为它能够即时响应查询。报告生成器的第一个原型。前端和设计同样由我完成。然后我决定重构 MySQL 中原有的聚合报告方案其在 50 个分片上积累了约 50 TB 数据。许多自定义数据结构以 BLOB 形式存储为了聚合这些数据程序需要先从数据库中读取应用自定义代码处理后再写回数据库。更重要的是MySQL 中的数据未经压缩且数据读取速度缓慢。这主要是因为数据到达按时间的顺序与查询范围按网站 ID的顺序不一致。在研究 LevelDB 和 TokuDB 之后我决定实现一个自定义数据结构专门用于增量聚合和后台合并。该表中的每条记录都由一个自定义 C 结构体定义代表一个 CRDT并具备add、update、merge、serializeText/Binary和deserializeText/Binary等方法。在读取时这些部分聚合的数据会最终合并并返回给 API。这种数据结构可以应用于任何聚合报告场景例如按区域统计的独立用户和访问量或是每个页面的点击热图。这个简单的原型名为 Metrage同样取得了成功。至此我们拥有了两种自定义数据结构一种是面向列的数据结构用于存储非聚合数据每日更新且仅支持整数类型另一种是面向行的数据结构支持任意 CRDT并能实时更新。在很长一段时间内这两种自定义数据结构都能很好地解决我们遇到的问题。坦白说当时并没有人提出更高的要求。但我开始思考如果能将面向列的方法与合并树相结合既能提升聚合速度又能支持实时更新和数据局部性同时还能将其泛化以支持真正的查询语言和丰富的数据类型那将会怎样而这正是 ClickHouse 的缘起。ClickHouse 是如何构建的ClickHouse 是一个罕见的数据库系统范例它并非基于任何现有系统而是完全从零开始实现的。如今大多数数据库管理系统 (DBMS) 都是在诸如 Postgres、Datafusion 甚至 ClickHouse 等现有系统之上构建的。因此深入探讨如何在没有任何基础的情况下从零引导一个 DBMS以及其具体实现步骤或许会非常有意思。2009 年的首次提交 涉及对同一单体仓库mono-repository中其他数据结构的优化。之所以能看到这些提交是因为在开源过程中我精心拆分了代码仓库并完整保留了所有历史记录。我开始实现新数据库管理系统 (DBMS) 的首次提交当时 ClickHouse 这个名字尚未诞生在此它实现了内存中的列存储可以看到大家已经熟悉的 IColumn 和 Field 类。可以将其与今天的实现进行比较 :) 你可能会觉得这与 Apache Arrow专注于内存中的列表示很相似并好奇我们为何没有采用它。然而当时 Apache Arrow 尚未问世其他列式存储格式例如 RCFile、Trevni、ORC 和 Parquet 也都还不存在。随后本次提交 引入了聚合函数aggregate functions。至今这仍然是 ClickHouse 最重要的组成部分之一。接着表引擎table engines也随之引入。有趣的是表引擎曾被命名为“primary key”但仅仅持续了几天。这使得在磁盘上读写列成为可能。第一个表引擎 类似于至今仍存在的 TinyLog 引擎。随后压缩功能 被加入。最初使用的是 QuickLZ但当我阅读了 Yann Collet 的博客后便迅速将其替换为 LZ4。然后是 block streams (数据块流)这些数据处理管道的组件负责以流式方式生成、消费或转换数据列块。如今它们已被 Processors (处理器) 取代。这一改进为 结果格式化 和实现表查询奠定了基础。同一提交还新增了StorageSystemNumbers最初用于测试 查询管道 (query pipelines)如今仍是我们备受喜爱的system.numbers表。ClickHouse 中 第一个查询管道 的功能是将数字以 TSV 格式打印出来。这里https://play.clickhouse.com/play?userplaytabQuery%20B 你可以查看 ClickHouse 中引入表引擎的顺序。ClickHouse 代码库中第一个 关系运算符 (relational operator) 是 LIMIT。接着我尝试 添加一个 SQL 解析器 (SQL parser)。首次尝试使用了 boost::spirit但最终失败了。一段时间后我开发了一个 递归下降解析器 (recursive descent parser)。值得一提的是一些最初被拒绝或后来又重新引入的想法。最初我曾尝试添加一个用于存储可变长度编码数字的列。但由于性能缓慢该列被移除。直到很久之后我们才引入了不依赖于列的自定义压缩编解码器。早期我还添加了一种可以包含任意字段值的 Variant 列类型。它同样性能不佳所以我将其移除——直到 Variant 的一个更好版本于 2025 年才被添加。我曾设计过固定大小fixed-size array和可变大小variable-size array的数组数据类型但因缺乏实际需求而将其移除。直到今天我们才考虑重新引入它。我认为移除不必要的代码比添加新代码更为重要而目前移除冗余代码已成为我最热衷的工作之一。在 ClickHouse 中你可以找到大量标题为“remove trash”的提交记录类似的情况并不少见。在此你可以看到 ClickHouse 中测试的第一个实际表结构——它正是你如今在 ClickBench 上仍能见到的hits表。在尝试读写这个表的过程中我们发现 C iostreams 性能低下。因此在这个提交中可以看到WriteBuffer和ReadBuffer的引入这些组件至今仍在沿用。SQL 中最早的函数——算术运算符—— 在此处 出现。这使得第一个 SELECT 查询解释器 得以实现。尽管当时 SELECT 查询解释器只能通过测试程序访问但它仍能让我们快速实现新的 聚合 函数、常规函数、关系运算符、数据格式及其他组件。ClickHouse 服务器 于 2012 年 3 月 9 日 问世而 clickhouse-client 则 于 3 月 25 日 推出。再加上Log、TinyLog、Merge、Distributed和Memory等表引擎ClickHouse 已足以在生产环境中部署。首次部署是为了存储传入的日志块以供进一步处理并对原始日志进行全局查询这正是Merge和Distributed表引擎的作用。可以说ClickHouse 最初的生产用途是一个支持 SQL 查询的持久化日志队列 。随后我引入了 MergeTree 表引擎它支持在后台进行数据的增量排序。这样一来即使数据按时间顺序写入对单个网站的范围查询也能快速响应。这使得我们能够将其部署到生产环境以替代早期的 OLAPServer 和 Metrage 等原型。MergeTree 的第一个版本包含了一些针对我们生产环境的特点例如在夜间更积极地合并数据块。2012 年我有幸聘请了团队的第二名成员他就是Michael Kolupaev。时至今日我依然很高兴能与他共事。我们的生产环境部署在多个区域数据中心。基础设施团队每月会故意关闭一个数据中心一小时这项操作被称为“演练”目的是让缺乏准备的服务经历停机以此促使所有团队构建高可用的多数据中心服务。因此生产环境中的所有数据和服务都必须在多个数据中心进行复制。最初我为此采用了简单的双写机制并在数据中心恢复后进行数据回填。但我们追求的是100%的一致性以及自动修复能力为此分布式共识机制必不可少。我的一些同事是Java工程师因此他们引入了ZooKeeper作为协调系统别担心我已经原谅他们了。Michael 利用 ZooKeeper 作为元数据层实现了 ReplicatedMergeTree。这使得 ClickHouse 得以在2014年成功部署用于处理生产环境中的用户查询。ClickHouse 是如何开源的2014年ClickHouse 已投入生产环境每日存储数百亿条记录并响应客户的实时查询。我还将其开放给公司内的数据科学家他们利用 ClickHouse 来计算互联网趋势。我发布了一份关于 ClickHouse 用法的简洁文档https://presentations.clickhouse.com/original_website/reference_en.html?_gl。广告、电商、基础设施和业务分析等其他部门也尝试使用 ClickHouse并将其部分用例从其他系统迁移过来例如内部的 Map-Reduce他们当时确实在使用 Perl 脚本处理文本日志、MySQL 和 Postgres。到2014年底ClickHouse 已在公司内部被广泛使用但当时仅限于这家公司有一个例外CERN也曾通过合作将其部署用于 LHCb 实验。当我观看技术会议上的演讲和阅读博客时我注意到其他公司的工程师经常开发类似于 OLAPServer 或 Metrage 的系统因为现有数据库无法妥善处理他们的用例——这对我来说是一个非常熟悉的故事我当时想——如果我能向大家介绍 ClickHouse 会怎么样呢我在2015年发表了一篇关于 ClickHouse 的文章translation这进一步证明了人们对它的兴趣。我的想法是——如果我能让 ClickHouse 普惠大众它就能填补这一市场空白。如果我不这样做——最终会有其他人来做那将是非常可怕的。我准备了一份清单列出了潜在的优势和风险以促使公司管理层批准其开源发布。不知怎的我的努力足够有说服力提案获得了批准。于是我制定了发布计划设计师制作了第一个 Logo我搭建了第一个网站https://presentations.clickhouse.com/original_website/?_gl准备了博客文章并与基础设施团队协作创建了一个 Debian 仓库。最终ClickHouse 于2016年6月15日向全世界开放。我希望这个故事也能激励每一位工程师尝试开源自己的代码。最坏的情况是它可能一无所获但也有可能像 ClickHouse 一样影响数代人不必担心自己的代码会让你感到难为情——我刚刚展示了我十五年前的代码现在看来也有点好笑。如今ClickHouse 已成为全球各大公司最青睐的分析数据库。ClickHouse 是面向 AI 时代打造的高性能实时分析数据库能够以极致性能处理海量数据分析任务。凭借高并发、低延迟和云原生架构ClickHouse 广泛应用于可观测性、数据仓库、实时分析及 AI 数据基础设施等场景。我们致力于帮助企业在公有云平台上构建安全、弹性且高性价比的实时分析与 AI 数据平台加速释放数据价值推动智能化创新与数字化转型。目前Trip.com、DiDi、Meta、Sony、Netflix、Deutsche Bank、Sierra、Cloudflare 等全球领先企业均在使用 ClickHouse 支撑其关键业务和数据分析平台。