以下是由 Igor Baliuk 撰写的客座博文。Igor 正在莫斯科高等经济学院攻读计算机科学学士学位,预计于 2023 年毕业。他是一名 Go 语言软件工程师,对服务网格和遥测特别感兴趣。
介绍
有时我们希望在线运行 SQL 查询以验证它们,与其他人共享它们,或者仅仅因为我们太懒得在本地安装数据库。在线 SQL 实验场可以帮助我们做到这一点。
实验场允许在浏览器中运行 SQL 查询,而无需用户端存在数据库实例。在本文中,我们将讨论一个专门为 ClickHouse 创建的新开源 SQL 实验场——ClickHouse Fiddle。我们将介绍该平台的动机和内部架构。
动机
如果您从未使用过在线实验场,可能会产生一个合理的问题:此类平台的用例是什么?
假设您需要执行一些查询并查看执行结果。例如,您想知道您最喜欢的数据库如何看待表达式 0.1 + 0.2 == 0.3
的真伪性。如果您已经有正在运行的数据库实例,则可以连接到它并在其中执行简单的查询。搞定。但是,如果您没有正在运行的实例怎么办?或者您现有的实例版本不是您想要的……
好吧,那么您需要下载所需版本的二进制文件,使用正确的选项运行它,最后执行 SELECT (0.1 + 0.2) == 0.3
……在当今的云世界中,我们甚至可以按下控制面板中的一个按钮,并在几分钟(甚至几秒钟)内获得数据库的托管实例。
但我们都同意,对于此类无上下文查询的执行,打开网页、在其中键入查询、等待几秒钟并查看执行结果要简单得多且更快。这就是实验场提供的!一种从浏览器快速、轻松地执行 SQL 查询的方法。
现在我们知道了,0.1 + 0.2 != 0.3
。浮点数…
一般来说,在线 SQL 实验场可帮助解决以下情况
- 数据库维护人员想要检查某个错误是否在特定数据库版本中可重现。
- 工程师阅读了有关新的强大 SQL 结构的信息,并且有兴趣了解他们使用的(或计划使用的)数据库版本是否支持它。
- 您的朋友询问您如何在 SQL 中执行某些操作,并且您想共享带有执行结果的 SQL 代码片段。
- 等等……
好吧,现在我们看到了实验场的优势。但是,如果我们已经拥有几个实验场,甚至包括 ClickHouse 的实验场,为什么还需要另一个实验场呢?简而言之,就是现有平台的局限性。
大多数 SQL 实验场旨在模拟 OLTP 数据库。创建事务以执行一堆查询。执行后,回滚事务以返回到初始数据库状态。提供在线实验场机制的另一种方法是仅允许对现有数据集进行读取查询(ClickHouse Play 就是这样实现的)。使用这种方法,有时很难(甚至不可能)获得所需的 SQL 查询逻辑。
此外,这两种方法都意味着每个受支持的数据库版本都存在一个始终在线的数据库实例。
可能性
简而言之,ClickHouse Fiddle 允许在 ClickHouse 的任意版本中运行多个 SQL 查询,并提供唯一的链接来共享执行结果。
还允许编写 DML 和 DDL 查询!这意味着您可以创建表,插入一些行并在其上执行查询。一次执行的数据在另一次运行中不可访问,通过容器化实现隔离(在设计部分将详细介绍)。 对最大执行时长和输入/输出大小有一些合理的限制,但这些限制并不太严格。用户可以使用一些基本的执行来理解一个或另一个 SQL 查询集的逻辑。
请记住,实验场并非旨在衡量数据库的性能。如果您想了解查询的速度有多快,建议您在生产就绪的实例中运行基准测试。
目前,在热数据库版本上执行简单查询通常需要几秒钟(p90 为 ~2 秒)。如果最近已将某个版本用于其他查询,则我们将其称为热版本。您可以在此处查看运行持续时间的第 90 百分位数示例
ClickHouse Fiddle 设计
让我们讨论一下实验场的实现方式。Fiddle 的设计可以概括在一张图中: 用户的入口点是一个简单的 Web 应用程序,可在 fiddle.clickhouse.com 访问。用户使用 HTTP API 与平台交互。所有请求都发送到系统的核心组件——实验场核心。
对于每个用户请求,都会创建一个包含所需 ClickHouse 版本的 Docker 容器。实验场的核心将传入负载分配到可用的机器上,并在每台机器上运行 Docker 容器。
为了优化查询执行的延迟,实验场会向运行程序发送存活性探测,并收集有关已拉取映像的信息。在负载均衡时,优先考虑具有已拉取映像的运行程序。 让我们更详细地了解一下用户请求平台执行查询时会发生什么
- 实验场核心使用负载均衡算法选择一台可用的机器。
- 如果选定的机器上没有包含所需 ClickHouse 版本的 Docker 映像,则从注册表中拉取它。
- 创建并运行 Docker 容器。
- 在容器中运行提供的 SQL 查询。
- 等待查询执行并杀死容器。
- 将执行结果保存在查询存储中(以便可以通过链接共享它们)。
- 将获取到的数据发送给用户作为响应。
临时容器通过 cgroups 机制帮助隔离不同的运行。如果将这种方法与始终开启的数据库实例进行比较,虽然会存在拉取镜像和创建容器的延迟,但它需要更少的资源来运行整个平台(对于不频繁的使用,1 个 runner 就足够了)。
但是,存在很多编排系统:Kubernetes、云服务等。为什么还要编写另一个容器管理器?现有的编排服务提供了大量可以对容器进行的操作。而为此付出的代价是它们的复杂性和速度缓慢(在游乐场环境中)。编写的协调器由于其简单性,比编排系统更快,并且需要更少的计算资源。
未来工作和反馈
Fiddle 有很多改进的机会
- 前端特性,例如 SQL 语法高亮
- 协调器分发算法
- 带有预加载数据集的数据库实例
- 通过提前运行容器来减少延迟
- 以及许多其他...
如果您想建议新的改进或分享任何其他反馈,请在 Github 仓库 中创建一个 issue。非常感谢!
并且 fiddle.clickhouse.com 正在等待您的查询 :)