DoubleCloud 即将停运。利用限时免费迁移服务迁移到 ClickHouse。立即联系我们 ->->

博客 / 工程

使用 ClickHouse 构建单页应用程序

author avatar
Dale McDiarmid
2024 年 10 月 10 日

介绍

在构建具有丰富 UI 的实时应用程序时,我们通常需要对数据库进行大量并发请求,以填充页面的一部分。为了不在 Web 浏览器中公开数据库凭据,常见的做法是在数据库和客户端之间放置一个 Web 服务器。

这种额外的复杂性对于某些应用程序来说是必要的,但在构建单页应用程序、概念验证和演示时可能太过复杂,因为用户需要快速迭代。相反,开发人员可以考虑采用“仅限客户端”架构,其中浏览器直接查询数据库。在本文中,我们将重点介绍采用“仅限客户端”架构的一些关键数据库注意事项。

单页应用程序在单个网页上动态更新内容,无需通过在客户端处理所有渲染和逻辑来完全重新加载页面,这种方法特别适用于这种方法,旨在提供无缝的用户体验。

我们将展示如何使用 ClickHouse 实现这一点,同时确保数据库保持安全且不会因请求而不堪重负。这要求我们利用几个简单但功能强大的功能,并重复使用一些配置配方,这些配方只需少量 JavaScript 就可以在几分钟内将分析安全地添加到现有应用程序中。

我们成功地将这种相同的“仅限客户端”方法用于我们许多公开的演示,包括 ClickPyCryptoHouseadsb.exposed,它们也是单页应用程序。但是,我们也意识到一些用户需要额外的安全级别,并希望通过添加 API 层来最大程度地减少其攻击面。为此,ClickHouse Cloud 提供了查询端点。

背景

传统上,Web 应用程序遵循传统的客户端-服务器架构。在此模型中,前端通过 API 与后端服务器通信,服务器处理数据库交互。

server-only.png

此设置要求开发人员构建和维护复杂的后端基础设施,从而增加了开发过程的复杂性并减慢了迭代周期。这种架构主要是因为安全问题,即从前端提供直接的数据库访问以及将数据库公开到公共互联网,因此需要安全的 API 通信。但是,这种架构具有固有的复杂性,并且带来了额外的可扩展性挑战 - 后端服务器需要仔细管理和手动调整才能有效地处理增长。总之,这使得开发和维护 Web 应用程序变得资源密集型且效率低下。

client-only.png

近年来,我们看到越来越多的采用允许从浏览器中的客户端代码直接访问数据库。这种构建 Web 应用程序的方法由 Firebase 推广,尤其是通过其 Firebase 实时数据库,该数据库引入了基于浏览器的访问概念,以及可以通过令牌(包括匿名身份验证令牌)管理的安全规则。

Firebase 在前端驱动型开发社区中的流行建立了这种实践,其他服务(如 Supabase)已经为基于 PostgreSQL 的数据库采用和改编了这种实践。这简化了开发,并通过减少对复杂后端基础设施的需求来加快迭代速度,并通过将其委派给数据库来简化可扩展性。

但是,这些数据库服务通常针对事务性工作负载进行了优化 - 非常适合处理您的应用程序状态,但不太适合在大型数据集上提供分析和丰富的可视化效果。幸运的是,ClickHouse 可以通过一些配置部署在该架构中。

以下建议适用于希望使用 ClickHouse 采用仅限客户端架构的用户。对于希望获得更简单体验的用户,我们推荐 ClickHouse Cloud 中的查询端点,它将许多复杂性抽象化,并允许通过可配置的 REST 端点使用 ClickHouse 的快速分析查询功能。

将 ClickHouse 用于单页应用程序

ClickHouse 具有几个关键功能,使其能够在仅限客户端架构中使用

  • HTTP 接口和 REST API - 使使用 JavaScript 从 ClickHouse 查询 SQL 变得轻而易举。默认情况下,ClickHouse 在端口 8123 或 8443(如果使用 SSL)上侦听,后者在 ClickHouse Cloud 中公开。此接口包括对 HTTP 压缩和会话的支持。
  • 输出格式 - 支持超过 70 种输出数据格式,包括 20 种 JSON 子格式,便于使用 JavaScript 解析。
  • 查询参数 - 允许对查询进行模板化,并保持对 SQL 注入的鲁棒性。
  • 基于角色的访问控制 - 允许管理员限制对特定表和行的访问权限。
  • 查询复杂度限制 - 限制用户为只读,以及限制可用的查询复杂度和资源。
  • 配额 - 限制来自任何特定客户端的查询数量,从而防止恶意或恶意客户端压垮数据库。

例如,在下面,我们查询我们的 ClickPy 实例,以查找过去 30 天下载次数最多的 Python 包。请注意 play 用户和 FORMAT JSONEachRow(默认值 TabSeparated)参数的使用,该参数请求将数据作为漂亮的 JSON 返回。

echo 'SELECT project, sum(count) as c FROM pypi.pypi_downloads GROUP BY project ORDER BY c DESC LIMIT 3 FORMAT JSONEachRow' | curl -u play: 'https://clickpy-clickhouse.clickhouse.com' --data-binary @-

{"project":"boto3","c":"27234697969"}
{"project":"urllib3","c":"17015345004"}
{"project":"botocore","c":"15812406924"}

虽然这些功能提供了必要的构建块,以确保可以从浏览器查询 ClickHouse,但它们必须经过精心配置和应用 - 特别是在将实例公开到公共互联网时。

在描述这些最佳实践时,我们只关注读取请求。这些通常是在开发仅限客户端的公共应用程序时允许的唯一适当请求。我们还假设所有客户端(实际用户)都使用相同的用户名发出查询。但是,这些原则可以轻松地扩展到多个 ClickHouse 用户。

仅限 HTTPS

对于面向公众的应用程序,用于大多数查询的用户凭据将在浏览器中可用,并且任何检查网络请求的开发人员都可见。虽然这些凭据不应被视为敏感信息,但应用程序可能允许用户修改用于请求的用户名和密码。这些凭据应受到保护,因此只应通过安全连接传输。有关如何为开源 ClickHouse 配置 TLS 并公开 HTTPS 接口的信息,请参阅 此处

例如,假设一个类似于 ClickHouse 演示环境的应用程序,其中用户可以在创建帐户时修改用户名和密码。也许该用户可能具有提升的权限或更高的配额。

ClickHouse Cloud 仅在端口 8443 上公开安全的 HTTP 接口。

允许跨域请求

为了确保浏览器可以从不同域托管的应用程序运行针对 ClickHouse 的查询,跨域请求 应该在 ClickHouse 中启用。在 ClickHouse Cloud 中,默认情况下已启用此功能。

curl -X OPTIONS -I https://clickpy-clickhouse.clickhouse.com -H "Origin: localhost:3000"

HTTP/1.1 204 No Content
Date: Fri, 27 Sep 2024 13:30:36 GMT
Connection: Close
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: origin, x-requested-with, x-clickhouse-format, x-clickhouse-user, x-clickhouse-key, Authorization
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Max-Age: 86400

OSS 用户可以通过修改 config.xml 中的相应设置来启用此功能。根据正常的最佳实践配置此设置,考虑是将访问限制在您的应用程序域还是允许用户在更广泛的上下文中使用您的 ClickHouse 数据。

<!-- It is off by default. Next headers are obligate for CORS.-->
<http_options_response>
   <header>
       <name>Access-Control-Allow-Origin</name>
       <value>*</value>
   </header>
   <header>
       <name>Access-Control-Allow-Headers</name>
       <value>origin, x-requested-with</value>
   </header>
   <header>
       <name>Access-Control-Allow-Methods</name>
       <value>POST, GET, OPTIONS</value>
   </header>
   <header>
       <name>Access-Control-Max-Age</name>
       <value>86400</value>
   </header>
</http_options_response>

通常为 JSON 格式

由于 Javascript 中有原生支持,JSON 是 Web 开发的首选数据交换格式。ClickHouse 支持超过 20 种 JSON 格式,每种格式都有其细微的差异。通常情况下,JSON 格式 提供最结构化和完整的响应,其中包含有关列及其类型、数据和查询统计信息的信息。我们可以使用 fetch API 对其进行测试。

const credentials = btoa('play:');
const response = await fetch('https://clickpy-clickhouse.clickhouse.com', {
	method: 'POST',
	body: 'SELECT project, sum(count) as c FROM pypi.pypi_downloads GROUP BY project ORDER BY c DESC LIMIT 3 FORMAT JSON',
	headers: {
  	'Authorization': `Basic ${credentials}`,
  	'Content-Type': 'application/x-www-form-urlencoded'
	}
  });

const data = await response.json();
console.log(JSON.stringify(data, null, 2));
{
  "meta": [
    {
      "name": "project",
      "type": "String"
    },
    {
      "name": "c",
      "type": "Int64"
    }
  ],
  "data": [
    {
      "project": "boto3",
      "c": "27234697969"
    },
    {
      "project": "urllib3",
      "c": "17015345004"
    },
    {
      "project": "botocore",
      "c": "15812406924"
    }
  ],
  "rows": 3,
  "rows_before_limit_at_least": 695858,
  "statistics": {
    "elapsed": 0.057031395,
    "rows_read": 1046002,
    "bytes_read": 32165070
  }
}

此格式有几种变体,例如 JSONObjectEachRowJSONColumnsWithMetadata,用户发现这些变体更容易解析以满足他们的使用场景。这些格式都会在外部 JSON 对象中返回响应,因此需要解析整个有效负载并将其加载到内存中。对于较小的响应,这很少是一个问题。对于较大的格式,用户可能希望考虑使用 EachRow 系列中的格式,该格式更容易使用 Streams API 解析,如这个简单的示例所示。这些格式,如 JSONEachRowJSONCompactEachRowJSONEachRowWithProgress,严格来说不是格式良好的 JSON - 更多地代表 NDJSON - 可以读取更多内容。

其他格式是 TSV 和 CSV 的变体,允许轻松下载数据。对于需要在大型数据量上实现高性能的用户(例如,在 Perspective 等 WebAssembly 库中进行渲染),ClickHouse 还支持 Arrow 和 ArrowStream 格式。有关示例,请参见此处

查询统计信息、会话和错误处理

ClickHouse HTTP 接口允许将查询统计信息作为响应标头发送,以描述查询的进度。这些标头可能难以读取,通常建议使用 JSONEachRowWithProgress 格式获取运行查询的进度统计信息。

用户还可以读取 X-ClickHouse-Summary 标头,其中汇总了读取的行数、字节数和执行时间。

echo 'SELECT project, sum(count) as c FROM pypi.pypi_downloads GROUP BY project ORDER BY c DESC LIMIT 3 FORMAT JSONEachRow' | curl -i -u play: 'https://clickpy-clickhouse.clickhouse.com' --data-binary @-

HTTP/1.1 200 OK
Date: Fri, 27 Sep 2024 15:02:22 GMT
Connection: Keep-Alive
Content-Type: application/x-ndjson; charset=UTF-8
X-ClickHouse-Server-Display-Name: clickhouse-cloud
Transfer-Encoding: chunked
X-ClickHouse-Query-Id: f05b0e25-8b9d-4d28-ad79-fe31e34acfbf
X-ClickHouse-Format: JSONEachRow
X-ClickHouse-Timezone: UTC
Keep-Alive: timeout=10
X-ClickHouse-Summary: {"read_rows":"1046002","read_bytes":"32165070","written_rows":"0","written_bytes":"0","total_rows_to_read":"1046002","result_rows":"0","result_bytes":"0","elapsed_ns":"45896728"}

{"project":"boto3","c":"27234697969"}
{"project":"urllib3","c":"17015345004"}
{"project":"botocore","c":"15812406924"}

请注意,除非请求中包含查询参数 wait_end_of_query=1,否则汇总统计信息可能不代表整个查询执行情况。如果没有此设置,响应将在查询完成之前以标头值返回,并进行流式传输。包括此设置会导致响应仅在查询完成后以准确的统计信息返回。请注意,这会导致响应在服务器上进行缓冲(可能会消耗大量内存)并延迟响应的提供,因此在读取大量行的情况下不合适。

echo 'SELECT project, sum(count) as c FROM pypi.pypi_downloads GROUP BY project ORDER BY c DESC LIMIT 3 FORMAT JSONEachRow' | curl -i -u play: 'https://clickpy-clickhouse.clickhouse.com' --data-binary @-

HTTP/1.1 200 OK
Date: Fri, 27 Sep 2024 15:02:22 GMT
Connection: Keep-Alive
Content-Type: application/x-ndjson; charset=UTF-8
X-ClickHouse-Server-Display-Name: clickhouse-cloud
Transfer-Encoding: chunked
X-ClickHouse-Query-Id: f05b0e25-8b9d-4d28-ad79-fe31e34acfbf
X-ClickHouse-Format: JSONEachRow
X-ClickHouse-Timezone: UTC
Keep-Alive: timeout=10
X-ClickHouse-Summary: {"read_rows":"1046002","read_bytes":"32165070","written_rows":"0","written_bytes":"0","total_rows_to_read":"1046002","result_rows":"0","result_bytes":"0","elapsed_ns":"45896728"}

{"project":"boto3","c":"27234697969"}
{"project":"urllib3","c":"17015345004"}
{"project":"botocore","c":"15812406924"}

我们还建议用户熟悉错误处理。在收到语法正确的查询后,ClickHouse 将在可能的情况下发送结果(除非 wait_end_of_query=1),并使用响应代码 200。如果稍后在查询执行期间(可能在几个小时后)出现错误,则可能会使用包含错误的有效负载终止流 - 请考虑这个示例。这由用户自行处理。

这可以通过使用 部分缓解 wait_end_of_query=1 实现。因此,如果手动处理响应,我们始终建议处理响应并确保其正确无误。

直接使用客户端库!

对于除最简单的使用场景以外的所有情况,我们建议用户直接使用官方 Web 客户端。该客户端支持大多数常见的请求格式,并确保正确处理会话、压缩、错误和 长时间运行查询的策略。流式选择在 已优化的简单接口中公开,并额外提供了类型化接口的优势。

import { createClient } from '@clickhouse/client-web';

void (async () => {
 const client = createClient( {
     url: 'https://clickpy-clickhouse.clickhouse.com',
     username: 'play'
   }
 );
 const rows = await client.query({
   query:
     'SELECT project, sum(count) as c FROM pypi.pypi_downloads GROUP BY project ORDER BY c DESC LIMIT 3',
   format: 'JSONEachRow',
 });
 const result = await rows.json();
 result.map(row => console.log(row));
 await client.close();
})();

使用查询参数

查询参数 允许对查询进行模板化,避免需要将 SQL 语句作为字符串进行操作(例如,当接口中的筛选器值发生变化时)。用户应始终优先使用查询参数而不是字符串操作,ClickHouse 确保前者对 SQL 注入攻击具有鲁棒性。Web 客户端提供了利用此功能的简洁接口。

import { createClient } from '@clickhouse/client-web';

void (async () => {
 const client = createClient({
   url: 'https://clickpy-clickhouse.clickhouse.com',
   username: 'play',
 });
 const rows = await client.query({
   query:
     'SELECT sum(count) as c FROM pypi.pypi_downloads WHERE project = {project:String}',
   format: 'JSONEachRow',
   query_params: {
     project: 'clickhouse-connect',
   }
 });
 const result = await rows.json();
 result.map(row => console.log(row));
 await client.close();
})();

最小权限原则

在授予用户访问表的权限时,始终采用最小权限原则 - ClickHouse 遵循此原则,新创建的用户没有权限(除非分配了默认角色)。

提示:如果创建将用于仅客户端请求的用户,通常不需要创建密码,因为凭据对应用程序用户可见。要创建没有密码的用户,请执行以下操作

-- oss
CREATE USER play IDENTIFIED WITH no_password;
-- clickhouse cloud
CREATE USER play IDENTIFIED WITH double_sha1_hash BY 'BE1BDEC0AA74B4DCB079943E70528096CCA985F8';

通常,我们建议创建一个角色(如果需要更多用户,则当前我们假设所有客户端都使用同一个用户),并授予该角色以下权限。

首先,建立 Web 用户可以访问的表和列 - 授予相应的 SELECT 权限。例如,以下操作允许任何具有 play_role 角色的用户读取 pypi 数据库中的所有表(和列)。具有此角色的用户仅限于读取 github 数据库中 events 表的 event_typeactor_login 列(这必须显式执行,即不允许使用 *)。

请注意,我们还将该角色授予我们之前的 play 用户。

-- create the role
CREATE ROLE play_role;
-- limit read on the columns event_type, actor_login for the github.events table
GRANT SELECT(event_type, actor_login ) ON github.events TO play_role;
-- allow select on all tables in the pypi database
GRANT SELECT ON pypi.* TO play_role;
-- grant the role to the user
GRANT play_role TO play;

还可以应用基于行的策略,允许用户仅查看给定表的特定行。有关更多详细信息,请参见此处

只读和常见限制

以上操作创建一个具有 SELECT 权限的限制用户。我们可以通过创建一个设置配置文件并将该配置文件应用于角色来应用进一步的约束。

此配置文件应进一步限制用户进行只读操作,为用户的角色设置 read_only=1。这将限制为只读查询,同时阻止修改会话上下文或更改设置的查询。虽然这会对用户施加只读限制,但不会限制他们执行复杂查询的能力。为此,我们可以向配置文件中添加查询复杂度的限制

这些限制允许对执行的各个方面进行精细控制。此处的设置数量很多,但至少建议以下设置

例如,请考虑以下在 play.clickhouse.com 中用于 play 用户的设置配置文件,其中包含合理的值。

CREATE SETTINGS PROFILE `play` SETTINGS readonly = 1, max_execution_time = 60, max_rows_to_read = 10000000000, max_result_rows = 1000, max_bytes_to_read = 1000000000000, max_result_bytes = 10000000, max_network_bandwidth = 25000000, max_memory_usage = 20000000000, max_bytes_before_external_group_by = 10000000000, enable_http_compression = true
--assign settings profile to the role
ALTER USER play SETTINGS PROFILE play_role

重要的是,所有这些设置都是针对每个查询的。我们需要配额(见下文)来限制查询数量。

默认情况下,如果查询超过这些限制,将抛出错误。在应用程序有一组受限查询的情况下,这种行为是可以接受的,其主要目的是阻止滥用用户。但是,在某些情况下,您可能希望允许用户在达到限制时(例如,读取的行数)接收部分响应。例如,这就是 CryptoHouse 中的情况,用户可以在其中运行任意选择,我们返回当前结果。

要实现此目的,请将配置文件中受限设置的溢出模式设置为 break。例如,设置 result_overflow_mode = 'break' 将在达到 max_result_rows 设置的限制时中断执行。

请注意,返回的行数将大于 max_result_rows,并且将是 max_block_sizemax_threads 的倍数,因为执行是在块级别中断的。

通常,我们发现设置 read_overflow_mode=break 在这种情况下也很有用,如果读取了过多的行,它会导致执行中断,并返回当前结果。用户可以通过检查汇总统计信息中的读取行数与已知限制值进行比较,从而检测到这种中断(并显示警告)。

如有需要,公开设置

配置为只读(通过 readonly=1)的用户无法在查询时更改设置。虽然这通常是理想的,但有些使用场景可能会有意义。例如,max_execution_time 需要由 Grafana 为只读用户修改。此外,您可能希望用户能够根据预期的结果集大小选择是否使用响应压缩(这需要 enable_http_compression=1)。

我们建议应用对设置的约束,而不是使用 readonly=2(允许用户更改所有设置)。这些约束允许用户在指定约束范围内更改指定的设置。

这些约束允许通过 minmax 为数值设置指定可接受的范围。可以通过 changeable_in_readonly 约束定义接受值枚举的设置是否可变。这些约束允许在定义的最小/最大范围内调整指定的设置,即使 readonly 设置为 1。否则,当 readonly=1 时,无法更改设置。

仅当在 clickhouse.xml 配置文件中启用 settings_constraints_replace_previous 时,才可以使用 changeable_in_readonly

<access_control_improvements>
 <settings_constraints_replace_previous>true</settings_constraints_replace_previous>
</access_control_improvements>

例如,请考虑以下配置文件

CREATE SETTINGS PROFILE `play` SETTINGS readonly = 1,  max_execution_time = 2, enable_http_compression = false

这会阻止使用具有此设置配置文件的用户使用 HTTP 压缩,并将执行时间限制为 2 秒。因此,以下查询都会失败

echo "SELECT sleep(3)" | curl -s -u play: 'https://clickpy-clickhouse.clickhouse.com
' --data-binary @-
Code: 159. DB::Exception: Timeout exceeded: elapsed 2.002346261 seconds, maximum: 2. (TIMEOUT_EXCEEDED) (version 24.6.1.4501 (official build))

echo "SELECT sleep(1) SETTINGS enable_http_compression=1" | curl -u play: 'https://clickpy-clickhouse.clickhouse.com?compress=true' --data-binary @- -H 'Accept-Encoding: gzip' --output -
Code: 164. DB::Exception: Cannot modify 'enable_http_compression' setting in readonly mode. (READONLY) (version 24.6.1.4501 (official build))

我们可以修改我们的设置配置文件,以允许使用约束来修改这些设置。

ALTER SETTINGS PROFILE `play` SETTINGS readonly = 1, max_execution_time = 10 CHANGEABLE_IN_READONLY min=1 max=60, enable_http_compression = false CHANGEABLE_IN_READONLY

现在可以配置先前的查询以执行

echo "SELECT sleep(3) SETTINGS max_execution_time=4" | curl -s -u play_v2: 'https://k5u1q15mc4.us-central1.gcp.clickhouse.cloud' --data-binary @-
0

echo "SELECT sleep(1) SETTINGS enable_http_compression=1" | curl -u play_v2: 'https://k5u1q15mc4.us-central1.gcp.clickhouse.cloud?compress=true' --data-binary @- -H 'Accept-Encoding: gzip' --output -
}''ߨ')CN''
')'2'

应用配额

我们之前的所有设置都只限制单个查询,而不会对整体查询吞吐量进行任何限制。恶意用户即使受到限制单个查询的约束,仍然可以运行数千个并发查询,从而消耗所有可用资源。

为了限制特定用户名下的查询数量,我们可以创建一个配额并将其应用于分配的角色。配额限制了每单位时间内的查询数量。重要的是,许多实际用户或客户端使用相同的用户名进行查询。我们希望配额应用于这些客户端中的每一个,而不仅仅是用户名 - 否则单个恶意客户端可能会耗尽所有人的限制!

为此,我们将配额与 IP 地址关联。这会导致限制按 IP 跟踪。例如,我们在下面创建了一个名为 play 的配额,允许每分钟最多 2 次查询,并将此配额分配给 play_role。

CREATE QUOTA play KEYED BY ip_address FOR INTERVAL 1 minute MAX queries = 2 TO play_role

在没有 play 用户的情况下进行查询,我们可以轻松地超过这些配额限制

echo "SELECT sum(number) FROM numbers(1000)" | curl -u play_v2: 'https://k5u1q15mc4.us-central1.gcp.clickhouse.cloud' --data-binary @-
Code: 201. DB::Exception: Quota for user `play_v2` for 60s has been exceeded: queries = 2/1. Interval will end at 2024-09-27 16:52:00. Name of quota template: `play_v2`. (QUOTA_EXCEEDED) (version 24.6.1.4501 (official build))

在 ClickHouse Cloud 中,配额是按副本计算的。在设置值时请确保考虑这一点。

不同角色的多个用户

在开发功能时,通常需要对应用程序的不同功能部分设置不同的限制。例如,用于执行查询的用户名可能受到每小时可以运行的查询数量的限制。相反,您可能有一个组件需要定期更新以显示统计数据。这些数据可以从物化视图中获取,并且需要每秒更新一次 - 比标准用户快得多。

clickpy-users.png

为了实现这一点,我们通常建议根据需要创建具有不同角色和配额的不同用户。CryptoHouse 中的用例就是这种情况,其中用于运行查询的用户名 crypto 每小时限制为 120 次查询。相反,用于获取查询执行进度中使用的用户名使用 monitor 用户,其配额限制要高得多,但反过来又限制在特定表上。

使用物化视图

我们建议开发人员确保频繁执行的查询针对物化视图进行。在最简单的形式中,ClickHouse 中的物化视图只是一个在向表插入数据时触发的查询。关键是物化视图本身不保存任何数据。它们只是对插入的行执行查询,并将结果发送到另一个“目标表”进行存储。

重要的是,运行的查询可以将行聚合到更小的结果集中,从而允许在目标表上更快地运行查询。这种方法有效地将工作从查询时间转移到插入时间,并避免查询遇到复杂性限制,同时最大限度地减少运行查询所需的资源并确保 UI 尽可能地响应。

我们在流行的 ClickPy 演示 中采用了这种技术,该技术允许用户对 Python 包执行分析。示例视图和有关实现的详细信息可以在这里找到。

利用压缩

ClickHouse 的 HTTP 端点支持请求和响应压缩。对于上面描述的基于文本的格式,我们建议使用 HTTP 响应压缩。由于读取请求通常很小,因此用户通常仅在开发仅客户端应用程序时才需要响应压缩。我们建议仅在网络成为瓶颈时,在流式传输大型响应时才启用压缩。压缩可能会通过服务器上产生的 CPU 负载来减慢响应时间。一如既往,测试和测量。

如果为用户设置了 enable_http_compression=1(或在请求中设置),则应在标头 Accept-Encoding: compression_method 中指定所需的压缩方法,其中包含支持的多个选项

const client = createClient( {
    url: 'https://clickpy-clickhouse.clickhouse.com',
    username: 'play',
     compression: {
       response: true
   }
  }
);

如果需要压缩,我们建议使用 ClickHouse JS 网页客户端,它支持使用 gzip 进行响应压缩,如上所示,并自动设置所需的标头和设置。

预定义的 HTTP 接口

开源用户可以使用预定义的 HTTP 接口将 SQL 与客户端抽象出来。此功能允许 ClickHouse 公开一个端点,向其提供参数。这些参数反过来会被注入到预定义的 SQL 查询中,并将响应返回给用户。对于简单的业务应用程序,这可以简化仅与有限 REST API 通信的客户端代码。上述相同原则可以应用于调用用户,以强制执行访问限制和配额。

ClickHouse Cloud 的查询端点

预定义的 HTTP 接口有其局限性,特别是实现更改或添加端点需要修改 clickhouse.xml 配置。

某些用户可能也不愿意公开他们的 ClickHouse HTTP 接口。查询端点有助于解决这些问题,方法是限制可以执行的查询,从而减少攻击面。

ClickHouse Cloud 通过查询端点使用基于令牌的身份验证机制,公开了一个类似但更高级的功能。除了模板化查询之外,用户还可以将角色分配给端点(通过这些端点可以应用设置约束和配额)并为每个端点配置 CORs。这些端点可以在任何时候轻松地从 ClickHouse Cloud 的统一控制台中添加或修改。

API 端点不仅简化了接口,还增加了关注点分离。除了使更新应用程序查询变得更简单之外(无需修改或重新部署代码),这还使团队能够轻松地公开分析,而无需编写 SQL 或直接与不同团队拥有的 ClickHouse 数据库交互。我们上面概述的所有最佳实践都抽象到了这些端点之后,开发人员只需要与简单的 REST API 端点进行交互即可。

有关此示例,我们建议您查看这篇博文

结论

仅客户端架构对于小型应用程序、演示和单页应用程序来说可能是理想的,它简化了开发并通过将扩展问题卸载到数据库来实现更快的迭代。但是,这确实要求目标数据库同时支持 HTTP 接口和将数据库安全地公开给客户端所需的功能。虽然 Firebase 和 Supabase 已经普及了这种架构用于 OLTP 工作负载,但 ClickHouse 具备必要的特性,使其能够被用于实时分析。我们提供了有关配置 ClickHouse 以用于这种架构的全面建议。对于希望获得更简单体验的用户,ClickHouse 中的查询端点抽象了许多复杂性,并允许通过简单的 REST 端点使用 ClickHouse 的快速分析查询功能。

立即开始使用 ClickHouse Cloud 并获得 300 美元的积分。在您 30 天的试用期结束后,您可以继续使用按需付费计划,或者联系我们了解有关我们基于数量的折扣的更多信息。访问我们的定价页面以了解更多详情。

分享此帖子

订阅我们的时事通讯

随时了解功能发布、产品路线图、支持和云产品!
正在加载表格...
关注我们
Twitter imageSlack imageGitHub image
Telegram imageMeetup imageRss image