将 S3 与 ClickHouse 集成
您可以将数据从 S3 插入到 ClickHouse 中,也可以将 S3 用作导出目标,从而实现与“数据湖”架构的交互。此外,S3 可以提供“冷”存储层,并协助分离存储和计算。在以下章节中,我们使用纽约市出租车数据集来演示在 S3 和 ClickHouse 之间移动数据的过程,以及识别关键配置参数并提供性能优化提示。
S3 表函数
s3
表函数允许您从 S3 兼容存储读取和写入文件。此语法的概要是
s3(path, [aws_access_key_id, aws_secret_access_key,] [format, [structure, [compression]]])
其中
- path — 存储桶 URL,带有文件路径。这支持只读模式下的以下通配符:
*
、?
、{abc,def}
和{N..M}
,其中N
、M
是数字,'abc'
、'def'
是字符串。有关更多信息,请参阅关于在路径中使用通配符的文档。 - format — 文件的 格式。
- structure — 表的结构。格式
'column1_name column1_type, column2_name column2_type, ...'
。 - compression — 参数是可选的。支持的值:
none
、gzip/gz
、brotli/br
、xz/LZMA
、zstd/zst
。默认情况下,它将通过文件扩展名自动检测压缩。
在路径表达式中使用通配符允许引用多个文件,并为并行处理打开了大门。
准备工作
为了与我们的基于 S3 的数据集交互,我们准备一个标准的 MergeTree
表作为我们的目标。以下语句在默认数据库中创建一个名为 trips
的表
CREATE TABLE trips
(
`trip_id` UInt32,
`vendor_id` Enum8('1' = 1, '2' = 2, '3' = 3, '4' = 4, 'CMT' = 5, 'VTS' = 6, 'DDS' = 7, 'B02512' = 10, 'B02598' = 11, 'B02617' = 12, 'B02682' = 13, 'B02764' = 14, '' = 15),
`pickup_date` Date,
`pickup_datetime` DateTime,
`dropoff_date` Date,
`dropoff_datetime` DateTime,
`store_and_fwd_flag` UInt8,
`rate_code_id` UInt8,
`pickup_longitude` Float64,
`pickup_latitude` Float64,
`dropoff_longitude` Float64,
`dropoff_latitude` Float64,
`passenger_count` UInt8,
`trip_distance` Float64,
`fare_amount` Float32,
`extra` Float32,
`mta_tax` Float32,
`tip_amount` Float32,
`tolls_amount` Float32,
`ehail_fee` Float32,
`improvement_surcharge` Float32,
`total_amount` Float32,
`payment_type` Enum8('UNK' = 0, 'CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4),
`trip_type` UInt8,
`pickup` FixedString(25),
`dropoff` FixedString(25),
`cab_type` Enum8('yellow' = 1, 'green' = 2, 'uber' = 3),
`pickup_nyct2010_gid` Int8,
`pickup_ctlabel` Float32,
`pickup_borocode` Int8,
`pickup_ct2010` String,
`pickup_boroct2010` String,
`pickup_cdeligibil` String,
`pickup_ntacode` FixedString(4),
`pickup_ntaname` String,
`pickup_puma` UInt16,
`dropoff_nyct2010_gid` UInt8,
`dropoff_ctlabel` Float32,
`dropoff_borocode` UInt8,
`dropoff_ct2010` String,
`dropoff_boroct2010` String,
`dropoff_cdeligibil` String,
`dropoff_ntacode` FixedString(4),
`dropoff_ntaname` String,
`dropoff_puma` UInt16
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(pickup_date)
ORDER BY pickup_datetime
SETTINGS index_granularity = 8192
请注意在 pickup_date
字段上使用了 分区。通常,分区键用于数据管理,但稍后我们将使用此键来并行化写入 S3。
我们的出租车数据集中的每个条目都包含一次出租车行程。此匿名数据包含 2000 万条记录,压缩在 S3 存储桶 https://datasets-documentation.s3.eu-west-3.amazonaws.com/ 的 nyc-taxi 文件夹下。数据为 TSV 格式,每个文件约 100 万行。
从 S3 读取数据
我们可以将 S3 数据作为源进行查询,而无需在 ClickHouse 中持久化。在以下查询中,我们对 10 行进行抽样。请注意,此处没有凭据,因为该存储桶是公开可访问的
SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_*.gz', 'TabSeparatedWithNames')
LIMIT 10;
请注意,我们不需要列出列,因为 TabSeparatedWithNames
格式将列名编码在第一行中。其他格式(例如 CSV
或 TSV
)将为此查询返回自动生成的列,例如 c1
、c2
、c3
等。
查询还支持虚拟列,例如 _path
和 _file
,它们分别提供有关存储桶路径和文件名的信息。例如
SELECT _path, _file, trip_id
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_0.gz', 'TabSeparatedWithNames')
LIMIT 5;
┌─_path──────────────────────────────────────┬─_file──────┬────trip_id─┐
│ datasets-documentation/nyc-taxi/trips_0.gz │ trips_0.gz │ 1199999902 │
│ datasets-documentation/nyc-taxi/trips_0.gz │ trips_0.gz │ 1199999919 │
│ datasets-documentation/nyc-taxi/trips_0.gz │ trips_0.gz │ 1199999944 │
│ datasets-documentation/nyc-taxi/trips_0.gz │ trips_0.gz │ 1199999969 │
│ datasets-documentation/nyc-taxi/trips_0.gz │ trips_0.gz │ 1199999990 │
└────────────────────────────────────────────┴────────────┴────────────┘
确认此示例数据集中的行数。请注意,此处使用了通配符进行文件扩展,因此我们考虑所有二十个文件。此查询将花费大约 10 秒,具体取决于 ClickHouse 实例上的核心数
SELECT count() AS count
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_*.gz', 'TabSeparatedWithNames');
┌────count─┐
│ 20000000 │
└──────────┘
虽然直接从 S3 读取数据对于数据抽样和执行即席探索性查询很有用,但这不是您希望经常做的事情。当需要认真处理时,请将数据导入到 ClickHouse 中的 MergeTree
表中。
使用 clickhouse-local
clickhouse-local
程序使您无需部署和配置 ClickHouse 服务器即可对本地文件执行快速处理。可以使用此实用程序执行任何使用 s3
表函数的查询。例如
clickhouse-local --query "SELECT * FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_*.gz', 'TabSeparatedWithNames') LIMIT 10"
从 S3 插入数据
为了充分利用 ClickHouse 的功能,接下来我们将数据读取并插入到我们的实例中。我们将 s3
函数与简单的 INSERT
语句结合使用来实现这一点。请注意,我们不需要列出我们的列,因为我们的目标表提供了所需的结构。这要求列按照表 DDL 语句中指定的顺序出现:列根据它们在 SELECT
子句中的位置进行映射。插入所有 1000 万行可能需要几分钟,具体取决于 ClickHouse 实例。下面我们插入 100 万行以确保及时响应。根据需要调整 LIMIT
子句或列选择以导入子集
INSERT INTO trips
SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_*.gz', 'TabSeparatedWithNames')
LIMIT 1000000;
使用 ClickHouse Local 进行远程插入
如果网络安全策略阻止您的 ClickHouse 集群建立出站连接,您可以使用 clickhouse-local
潜在地插入 S3 数据。在下面的示例中,我们从 S3 存储桶读取数据,并使用 remote
函数插入到 ClickHouse 中
clickhouse-local --query "INSERT INTO TABLE FUNCTION remote('localhost:9000', 'default.trips', 'username', 'password') (*) SELECT * FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_*.gz', 'TabSeparatedWithNames') LIMIT 10"
要通过安全的 SSL 连接执行此操作,请使用 remoteSecure
函数。
导出数据
您可以使用 s3
表函数写入 S3 中的文件。这将需要适当的权限。我们在请求中传递所需的凭据,但请查看管理凭据页面以获取更多选项。
在下面的简单示例中,我们使用表函数作为目标而不是源。在这里,我们将 10,000 行从 trips
表流式传输到存储桶,指定 lz4
压缩和 CSV
输出类型
INSERT INTO FUNCTION
s3(
'https://datasets-documentation.s3.eu-west-3.amazonaws.com/csv/trips.csv.lz4',
's3_key',
's3_secret',
'CSV'
)
SELECT *
FROM trips
LIMIT 10000;
请注意此处文件的格式是如何从扩展名推断出来的。我们也不需要在 s3
函数中指定列 - 这可以从 SELECT
中推断出来。
拆分大型文件
您可能不希望将数据导出为单个文件。大多数工具(包括 ClickHouse)在读取和写入多个文件时,由于并行处理的可能性,将实现更高的吞吐量性能。我们可以多次执行我们的 INSERT
命令,以数据子集为目标。ClickHouse 提供了一种使用 PARTITION
键自动拆分文件的方法。
在下面的示例中,我们使用 rand()
函数的模数创建十个文件。请注意,结果分区 ID 如何在文件名中被引用。这将产生十个带有数字后缀的文件,例如 trips_0.csv.lz4
、trips_1.csv.lz4
等…
INSERT INTO FUNCTION
s3(
'https://datasets-documentation.s3.eu-west-3.amazonaws.com/csv/trips_{_partition_id}.csv.lz4',
's3_key',
's3_secret',
'CSV'
)
PARTITION BY rand() % 10
SELECT *
FROM trips
LIMIT 100000;
或者,我们可以引用数据中的字段。对于此数据集,payment_type
提供了一个自然的分区键,基数为 5。
INSERT INTO FUNCTION
s3(
'https://datasets-documentation.s3.eu-west-3.amazonaws.com/csv/trips_{_partition_id}.csv.lz4',
's3_key',
's3_secret',
'CSV'
)
PARTITION BY payment_type
SELECT *
FROM trips
LIMIT 100000;
利用集群
上述函数都仅限于在单个节点上执行。读取速度将随着 CPU 核心线性扩展,直到其他资源(通常是网络)饱和,从而允许用户垂直扩展。但是,这种方法有其局限性。虽然用户可以通过在执行 INSERT INTO SELECT
查询时插入到分布式表中来减轻一些资源压力,但这仍然使单个节点读取、解析和处理数据。为了解决此挑战并允许我们水平扩展读取,我们有 s3Cluster 函数。
接收查询的节点(称为发起者)创建与集群中每个节点的连接。确定需要读取哪些文件的 glob 模式被解析为一组文件。发起者将文件分发到集群中的节点,这些节点充当工作节点。这些工作节点反过来在完成读取时请求要处理的文件。此过程确保我们可以水平扩展读取。
s3Cluster
函数采用与单节点变体相同的格式,但需要目标集群来表示工作节点
s3Cluster(cluster_name, source, [access_key_id, secret_access_key,] format, structure)
cluster_name
— 用于构建远程和本地服务器的地址和连接参数集的集群名称。source
— 指向文件或一批文件的 URL。支持只读模式下的以下通配符:*
、?
、{'abc','def'}
和{N..M}
,其中 N, M — 数字,abc, def — 字符串。有关更多信息,请参阅 路径中的通配符。access_key_id
和secret_access_key
— 指定与给定端点一起使用的凭据的密钥。可选。format
— 文件的 格式。structure
— 表的结构。格式 'column1_name column1_type, column2_name column2_type, ...'。
与任何 s3
函数一样,如果存储桶不安全或您通过环境(例如 IAM 角色)定义安全性,则凭据是可选的。但是,与 s3 函数不同,结构必须在请求中指定(截至 22.3.1),即不推断模式。
在大多数情况下,此函数将用作 INSERT INTO SELECT
的一部分。在这种情况下,您通常会插入分布式表。我们在下面说明一个简单的示例,其中 trips_all 是一个分布式表。虽然此表使用 events 集群,但用于读取和写入的节点的一致性不是必需的
INSERT INTO default.trips_all
SELECT *
FROM s3Cluster(
'events',
'https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_*.gz',
'TabSeparatedWithNames'
)
插入将针对发起者节点进行。这意味着虽然读取将在每个节点上发生,但结果行将被路由到发起者以进行分发。在高吞吐量场景中,这可能证明是一个瓶颈。为了解决这个问题,请为 s3cluster
函数设置参数 parallel_distributed_insert_select。
S3 表引擎
虽然 s3
函数允许对存储在 S3 中的数据执行即席查询,但它们的语法很冗长。S3
表引擎使您不必一遍又一遍地指定存储桶 URL 和凭据。为了解决这个问题,ClickHouse 提供了 S3 表引擎。
CREATE TABLE s3_engine_table (name String, value UInt32)
ENGINE = S3(path, [aws_access_key_id, aws_secret_access_key,] format, [compression])
[SETTINGS ...]
path
— 存储桶 URL,带有文件路径。支持只读模式下的以下通配符:*
、?
、{abc,def}
和{N..M}
,其中 N, M — 数字,'abc', 'def' — 字符串。有关更多信息,请参见此处。format
— 文件的格式。aws_access_key_id
,aws_secret_access_key
- AWS 账户用户的长期凭据。您可以使用这些凭据来验证您的请求。该参数是可选的。如果未指定凭据,则使用配置文件值。有关更多信息,请参阅管理凭据。compression
— 压缩类型。支持的值:none, gzip/gz, brotli/br, xz/LZMA, zstd/zst。该参数是可选的。默认情况下,它将通过文件扩展名自动检测压缩。
读取数据
在以下示例中,我们使用位于 https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/
存储桶中的前十个 TSV 文件创建一个名为 trips_raw
的表。这些文件中的每一个都包含 100 万行
CREATE TABLE trips_raw
(
`trip_id` UInt32,
`vendor_id` Enum8('1' = 1, '2' = 2, '3' = 3, '4' = 4, 'CMT' = 5, 'VTS' = 6, 'DDS' = 7, 'B02512' = 10, 'B02598' = 11, 'B02617' = 12, 'B02682' = 13, 'B02764' = 14, '' = 15),
`pickup_date` Date,
`pickup_datetime` DateTime,
`dropoff_date` Date,
`dropoff_datetime` DateTime,
`store_and_fwd_flag` UInt8,
`rate_code_id` UInt8,
`pickup_longitude` Float64,
`pickup_latitude` Float64,
`dropoff_longitude` Float64,
`dropoff_latitude` Float64,
`passenger_count` UInt8,
`trip_distance` Float64,
`fare_amount` Float32,
`extra` Float32,
`mta_tax` Float32,
`tip_amount` Float32,
`tolls_amount` Float32,
`ehail_fee` Float32,
`improvement_surcharge` Float32,
`total_amount` Float32,
`payment_type_` Enum8('UNK' = 0, 'CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4),
`trip_type` UInt8,
`pickup` FixedString(25),
`dropoff` FixedString(25),
`cab_type` Enum8('yellow' = 1, 'green' = 2, 'uber' = 3),
`pickup_nyct2010_gid` Int8,
`pickup_ctlabel` Float32,
`pickup_borocode` Int8,
`pickup_ct2010` String,
`pickup_boroct2010` FixedString(7),
`pickup_cdeligibil` String,
`pickup_ntacode` FixedString(4),
`pickup_ntaname` String,
`pickup_puma` UInt16,
`dropoff_nyct2010_gid` UInt8,
`dropoff_ctlabel` Float32,
`dropoff_borocode` UInt8,
`dropoff_ct2010` String,
`dropoff_boroct2010` FixedString(7),
`dropoff_cdeligibil` String,
`dropoff_ntacode` FixedString(4),
`dropoff_ntaname` String,
`dropoff_puma` UInt16
) ENGINE = S3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_{0..9}.gz', 'TabSeparatedWithNames', 'gzip');
请注意使用 {0..9}
模式来限制为前十个文件。创建后,我们可以像查询任何其他表一样查询此表
SELECT DISTINCT(pickup_ntaname)
FROM trips_raw
LIMIT 10;
┌─pickup_ntaname───────────────────────────────────┐
│ Lenox Hill-Roosevelt Island │
│ Airport │
│ SoHo-TriBeCa-Civic Center-Little Italy │
│ West Village │
│ Chinatown │
│ Hudson Yards-Chelsea-Flatiron-Union Square │
│ Turtle Bay-East Midtown │
│ Upper West Side │
│ Murray Hill-Kips Bay │
│ DUMBO-Vinegar Hill-Downtown Brooklyn-Boerum Hill │
└──────────────────────────────────────────────────┘
插入数据
S3
表引擎支持并行读取。仅当表定义不包含 glob 模式时才支持写入。因此,上面的表将阻止写入。
为了演示写入,创建一个指向可写 S3 存储桶的表
CREATE TABLE trips_dest
(
`trip_id` UInt32,
`pickup_date` Date,
`pickup_datetime` DateTime,
`dropoff_datetime` DateTime,
`tip_amount` Float32,
`total_amount` Float32
) ENGINE = S3('<bucket path>/trips.bin', 'Native');
INSERT INTO trips_dest
SELECT
trip_id,
pickup_date,
pickup_datetime,
dropoff_datetime,
tip_amount,
total_amount
FROM trips
LIMIT 10;
SELECT * FROM trips_dest LIMIT 5;
┌────trip_id─┬─pickup_date─┬─────pickup_datetime─┬────dropoff_datetime─┬─tip_amount─┬─total_amount─┐
│ 1200018648 │ 2015-07-01 │ 2015-07-01 00:00:16 │ 2015-07-01 00:02:57 │ 0 │ 7.3 │
│ 1201452450 │ 2015-07-01 │ 2015-07-01 00:00:20 │ 2015-07-01 00:11:07 │ 1.96 │ 11.76 │
│ 1202368372 │ 2015-07-01 │ 2015-07-01 00:00:40 │ 2015-07-01 00:05:46 │ 0 │ 7.3 │
│ 1200831168 │ 2015-07-01 │ 2015-07-01 00:01:06 │ 2015-07-01 00:09:23 │ 2 │ 12.3 │
│ 1201362116 │ 2015-07-01 │ 2015-07-01 00:01:07 │ 2015-07-01 00:03:31 │ 0 │ 5.3 │
└────────────┴─────────────┴─────────────────────┴─────────────────────┴────────────┴──────────────┘
请注意,行只能插入到新文件中。没有合并周期或文件拆分操作。一旦写入文件,后续插入将失败。用户在此处有两个选择
- 指定设置
s3_create_new_file_on_insert=1
。这将导致在每次插入时创建新文件。数字后缀将附加到每个文件的末尾,该后缀对于每个插入操作都会单调递增。对于上面的示例,后续插入将导致创建 trips_1.bin 文件。 - 指定设置
s3_truncate_on_insert=1
。这将导致文件截断,即它将仅包含新插入的行(完成后)。
这两个设置的默认值都为 0 - 因此强制用户设置其中一个。如果同时设置了两者,则 s3_truncate_on_insert
将优先。
关于 S3
表引擎的一些注意事项
- 与传统的
MergeTree
系列表不同,删除S3
表不会删除底层数据。 - 此表类型的完整设置可以在此处找到。
- 使用此引擎时,请注意以下注意事项
- 不支持 ALTER 查询
- 不支持 SAMPLE 操作
- 没有索引的概念,即主索引或跳数索引。
管理凭据
在前面的示例中,我们在 s3
函数或 S3
表定义中传递了凭据。虽然这对于偶尔使用可能是可以接受的,但用户在生产中需要不太显式的身份验证机制。为了解决这个问题,ClickHouse 有几个选项
-
在 config.xml 或 conf.d 下的等效配置文件中指定连接详细信息。下面显示了一个示例文件的内容,假设使用 debian 软件包进行安装。
ubuntu@single-node-clickhouse:/etc/clickhouse-server/config.d$ cat s3.xml
<clickhouse>
<s3>
<endpoint-name>
<endpoint>https://dalem-files.s3.amazonaws.com/test/</endpoint>
<access_key_id>key</access_key_id>
<secret_access_key>secret</secret_access_key>
<!-- <use_environment_credentials>false</use_environment_credentials> -->
<!-- <header>Authorization: Bearer SOME-TOKEN</header> -->
</endpoint-name>
</s3>
</clickhouse>这些凭据将用于任何请求,其中上面的端点与请求的 URL 完全前缀匹配。另请注意,在此示例中,可以声明授权标头作为访问密钥和密钥的替代方案。支持的设置的完整列表可以在此处找到。
-
上面的示例重点介绍了配置参数
use_environment_credentials
的可用性。此配置参数也可以在s3
级别全局设置<clickhouse>
<s3>
<use_environment_credentials>true</use_environment_credentials>
</s3>
</clickhouse>此设置启用尝试从环境中检索 S3 凭据,从而允许通过 IAM 角色进行访问。具体来说,执行以下检索顺序
- 查找环境变量
AWS_ACCESS_KEY_ID
、AWS_SECRET_ACCESS_KEY
和AWS_SESSION_TOKEN
- 在 $HOME/.aws 中执行检查
- 通过 AWS Security Token Service 获取的临时凭据 - 即通过
AssumeRole
API - 检查 ECS 环境变量
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
或AWS_CONTAINER_CREDENTIALS_FULL_URI
和AWS_ECS_CONTAINER_AUTHORIZATION_TOKEN
中的凭据。 - 通过 Amazon EC2 实例元数据 获取凭据,前提是 AWS_EC2_METADATA_DISABLED 未设置为 true。
- 这些相同的设置也可以为特定端点设置,使用相同的前缀匹配规则。
- 查找环境变量
性能优化
有关如何优化使用 S3 函数进行读取和插入的信息,请参阅专用性能指南。
S3 存储调优
在内部,ClickHouse MergeTree 使用两种主要存储格式:Wide
和 Compact
。虽然当前的实现使用 ClickHouse 的默认行为(通过设置 min_bytes_for_wide_part
和 min_rows_for_wide_part
控制),但我们预计 S3 的行为在未来的版本中会有所不同,例如,更大的 min_bytes_for_wide_part
默认值鼓励更 Compact
的格式,从而减少文件数量。用户现在可能希望在使用独占 S3 存储时调整这些设置。
S3 支持的 MergeTree
s3
函数和相关的表引擎允许我们使用熟悉的 ClickHouse 语法查询 S3 中的数据。但是,在数据管理功能和性能方面,它们受到限制。不支持主索引,不支持缓存,并且文件插入需要由用户管理。
ClickHouse 认识到 S3 代表着一种有吸引力的存储解决方案,尤其是在对“较冷”数据的查询性能不太关键,并且用户寻求分离存储和计算的情况下。为了帮助实现这一点,提供了使用 S3 作为 MergeTree 引擎存储的支持。这将使用户能够利用 S3 的可扩展性和成本优势,以及 MergeTree 引擎的插入和查询性能。
存储层
ClickHouse 存储卷允许从 MergeTree 表引擎中抽象出物理磁盘。任何单个卷都可以由一组有序的磁盘组成。虽然主要允许多个块设备可能用于数据存储,但这种抽象也允许其他存储类型,包括 S3。ClickHouse 数据部分可以根据存储策略在卷和填充率之间移动,从而创建存储层的概念。
存储层解锁了热冷架构,其中最新的数据(通常也是查询最多的数据)只需要高性能存储(例如 NVMe SSD)上的少量空间。随着数据的老化,查询时间的 SLA 会增加,查询频率也会增加。数据的这种长尾可以存储在较慢、性能较低的存储(如 HDD 或对象存储(如 S3))上。
创建磁盘
要将 S3 存储桶用作磁盘,我们必须首先在 ClickHouse 配置文件中声明它。可以扩展 config.xml,或者最好在 conf.d 下提供一个新文件。下面显示了 S3 磁盘声明的示例
<clickhouse>
<storage_configuration>
...
<disks>
<s3>
<type>s3</type>
<endpoint>https://sample-bucket.s3.us-east-2.amazonaws.com/tables/</endpoint>
<access_key_id>your_access_key_id</access_key_id>
<secret_access_key>your_secret_access_key</secret_access_key>
<region></region>
<metadata_path>/var/lib/clickhouse/disks/s3/</metadata_path>
</s3>
<s3_cache>
<type>cache</type>
<disk>s3</disk>
<path>/var/lib/clickhouse/disks/s3_cache/</path>
<max_size>10Gi</max_size>
</s3_cache>
</disks>
...
</storage_configuration>
</clickhouse>
与此磁盘声明相关的完整设置列表可以在此处找到。请注意,可以使用管理凭据中描述的相同方法在此处管理凭据,即可以在上述设置块中将 use_environment_credentials 设置为 true 以使用 IAM 角色。
创建存储策略
配置完成后,此“磁盘”可以被策略中声明的存储卷使用。对于下面的示例,我们假设 s3 是我们唯一的存储。这忽略了更复杂的热冷架构,在这些架构中,数据可以根据 TTL 和填充率重新定位。
<clickhouse>
<storage_configuration>
<disks>
<s3>
...
</s3>
<s3_cache>
...
</s3_cache>
</disks>
<policies>
<s3_main>
<volumes>
<main>
<disk>s3</disk>
</main>
</volumes>
</s3_main>
</policies>
</storage_configuration>
</clickhouse>
创建表
假设您已将磁盘配置为使用具有写入访问权限的存储桶,您应该能够创建如下例所示的表。为了简洁起见,我们使用了 NYC 出租车列的子集,并将数据直接流式传输到 s3 支持的表
CREATE TABLE trips_s3
(
`trip_id` UInt32,
`pickup_date` Date,
`pickup_datetime` DateTime,
`dropoff_datetime` DateTime,
`pickup_longitude` Float64,
`pickup_latitude` Float64,
`dropoff_longitude` Float64,
`dropoff_latitude` Float64,
`passenger_count` UInt8,
`trip_distance` Float64,
`tip_amount` Float32,
`total_amount` Float32,
`payment_type` Enum8('UNK' = 0, 'CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4)
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(pickup_date)
ORDER BY pickup_datetime
SETTINGS index_granularity = 8192, storage_policy='s3_main'
INSERT INTO trips_s3 SELECT trip_id, pickup_date, pickup_datetime, dropoff_datetime, pickup_longitude, pickup_latitude, dropoff_longitude, dropoff_latitude, passenger_count, trip_distance, tip_amount, total_amount, payment_type FROM s3('https://ch-nyc-taxi.s3.eu-west-3.amazonaws.com/tsv/trips_{0..9}.tsv.gz', 'TabSeparatedWithNames') LIMIT 1000000;
根据硬件的不同,后面对 100 万行的插入可能需要几分钟才能执行。您可以通过 system.processes 表确认进度。随意调整行数,最高可达 1000 万行,并探索一些示例查询。
SELECT passenger_count, avg(tip_amount) as avg_tip, avg(total_amount) as avg_amount FROM trips_s3 GROUP BY passenger_count;
修改表
有时,用户可能需要修改特定表的存储策略。虽然这是可能的,但它有一些限制。新的目标策略必须包含先前策略的所有磁盘和卷,即,数据不会迁移以满足策略更改。在验证这些约束时,卷和磁盘将通过其名称进行标识,尝试违反约束将导致错误。但是,假设您使用之前的示例,则以下更改是有效的。
<policies>
<s3_main>
<volumes>
<main>
<disk>s3</disk>
</main>
</volumes>
</s3_main>
<s3_tiered>
<volumes>
<hot>
<disk>default</disk>
</hot>
<main>
<disk>s3</disk>
</main>
</volumes>
<move_factor>0.2</move_factor>
</s3_tiered>
</policies>
ALTER TABLE trips_s3 MODIFY SETTING storage_policy='s3_tiered'
在这里,我们在新的 s3_tiered 策略中重用了主卷,并引入了一个新的热卷。这使用默认磁盘,该磁盘仅由通过参数 <path>
配置的一个磁盘组成。请注意,我们的卷名和磁盘没有更改。对我们表的新插入将驻留在默认磁盘上,直到达到 move_factor * disk_size - 此时数据将被重新定位到 S3。
处理复制
可以使用 ReplicatedMergeTree
表引擎完成与 S3 磁盘的复制。有关详细信息,请参阅使用 S3 对象存储在两个 AWS 区域复制单个分片指南。
读取和写入
以下注释涵盖了 S3 与 ClickHouse 交互的实现。虽然通常仅供参考,但在性能优化时,它可能会对读者有所帮助
- 默认情况下,查询处理管道任何阶段使用的最大查询处理线程数等于核心数。某些阶段比其他阶段更易于并行化,因此此值提供了一个上限。由于数据是从磁盘流式传输的,因此可以同时执行多个查询阶段。因此,查询使用的确切线程数可能会超过此值。通过设置 max_threads 进行修改。
- 默认情况下,对 S3 的读取是异步的。此行为由设置
remote_filesystem_read_method
确定,默认情况下设置为值threadpool
。在处理请求时,ClickHouse 以条纹形式读取粒度。这些条纹中的每一个都可能包含许多列。一个线程将逐个读取其粒度的列。与其同步执行此操作,不如在等待数据之前为所有列进行预取。与同步等待每一列相比,这提供了显着的性能改进。在大多数情况下,用户无需更改此设置 - 请参阅性能优化。 - 写入是并行执行的,最多 100 个并发文件写入线程。
max_insert_delayed_streams_for_parallel_write
的默认值为 1000,它控制并行写入的 S3 blob 的数量。由于每个正在写入的文件都需要一个缓冲区 (~1MB),这有效地限制了 INSERT 的内存消耗。在服务器内存不足的情况下,降低此值可能是合适的。
将 S3 对象存储用作 ClickHouse 磁盘
如果您需要创建存储桶和 IAM 角色的分步说明,请展开创建 S3 存储桶和 IAM 角色并按照步骤操作
创建 S3 存储桶和 IAM 用户
本文演示了如何配置 AWS IAM 用户、创建 S3 存储桶以及配置 ClickHouse 以将该存储桶用作 S3 磁盘的基础知识。您应该与您的安全团队合作以确定要使用的权限,并将这些视为起点。
创建 AWS IAM 用户
在此过程中,我们将创建服务帐户用户,而不是登录用户。
-
登录到 AWS IAM 管理控制台。
-
在“用户”中,选择添加用户
- 输入用户名并将凭据类型设置为访问密钥 - 编程访问,然后选择下一步:权限
- 请勿将用户添加到任何组;选择下一步:标签
- 除非您需要添加任何标签,否则选择下一步:审核
-
选择创建用户
注意可以忽略声明用户没有权限的警告消息;权限将在下一节中授予给该用户的存储桶
- 用户现已创建;点击显示并复制访问密钥和密钥。
将密钥保存到其他地方;这是密钥将可用的唯一一次。
- 点击关闭,然后在用户屏幕中找到该用户。
- 复制 ARN(Amazon 资源名称)并保存,以便在配置存储桶的访问策略时使用。
创建 S3 存储桶
- 在 S3 存储桶部分,选择创建存储桶
- 输入存储桶名称,保留其他选项为默认值
存储桶名称在整个 AWS 中必须是唯一的,而不仅仅是在组织内,否则会发出错误。
- 保持
阻止所有公有访问
启用状态;不需要公有访问。
- 在页面底部选择创建存储桶
-
选择链接,复制 ARN,并保存,以便在配置存储桶的访问策略时使用。
-
存储桶创建完成后,在 S3 存储桶列表中找到新的 S3 存储桶并选择链接
- 选择创建文件夹
- 输入将作为 ClickHouse S3 磁盘的目标的文件夹名称,然后选择创建文件夹
- 该文件夹现在应该在存储桶列表中可见
- 选中新文件夹的复选框,然后点击复制 URL。保存复制的 URL,以便在下一节的 ClickHouse 存储配置中使用。
- 选择权限选项卡,然后点击存储桶策略部分中的编辑按钮
- 添加存储桶策略,示例如下
{
"Version": "2012-10-17",
"Id": "Policy123456",
"Statement": [
{
"Sid": "abc123",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::921234567898:user/mars-s3-user"
},
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::mars-doc-test",
"arn:aws:s3:::mars-doc-test/*"
]
}
]
}
|Parameter | Description | Example Value |
|----------|-------------|----------------|
|Version | Version of the policy interpreter, leave as-is | 2012-10-17 |
|Sid | User-defined policy id | abc123 |
|Effect | Whether user requests will be allowed or denied | Allow |
|Principal | The accounts or user that will be allowed | arn:aws:iam::921234567898:user/mars-s3-user |
|Action | What operations are allowed on the bucket| s3:*|
|Resource | Which resources in the bucket will operations be allowed in | "arn:aws:s3:::mars-doc-test", "arn:aws:s3:::mars-doc-test/*" |
您应该与您的安全团队合作,以确定要使用的权限,将这些权限视为起点。有关策略和设置的更多信息,请参阅 AWS 文档:https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-policy-language-overview.html
- 保存策略配置。
配置 ClickHouse 以使用 S3 存储桶作为磁盘
以下示例基于以默认 ClickHouse 目录作为服务安装的 Linux Deb 包。
- 在 ClickHouse
config.d
目录中创建一个新文件,以存储存储配置。
vim /etc/clickhouse-server/config.d/storage_config.xml
- 添加以下内容以进行存储配置;替换先前步骤中的存储桶路径、访问密钥和密钥
<clickhouse>
<storage_configuration>
<disks>
<s3_disk>
<type>s3</type>
<endpoint>https://mars-doc-test.s3.amazonaws.com/clickhouse3/</endpoint>
<access_key_id>ABC123</access_key_id>
<secret_access_key>Abc+123</secret_access_key>
<metadata_path>/var/lib/clickhouse/disks/s3_disk/</metadata_path>
</s3_disk>
<s3_cache>
<type>cache</type>
<disk>s3_disk</disk>
<path>/var/lib/clickhouse/disks/s3_cache/</path>
<max_size>10Gi</max_size>
</s3_cache>
</disks>
<policies>
<s3_main>
<volumes>
<main>
<disk>s3_disk</disk>
</main>
</volumes>
</s3_main>
</policies>
</storage_configuration>
</clickhouse>
<disks>
标签内的标签 s3_disk
和 s3_cache
是任意标签。这些可以设置为其他内容,但在 <policies>
标签下的 <disk>
标签中必须使用相同的标签来引用磁盘。<S3_main>
标签也是任意的,并且是策略的名称,该策略将用作在 ClickHouse 中创建资源时的标识符存储目标。
上面显示的配置适用于 ClickHouse 22.8 或更高版本,如果您使用的是旧版本,请参阅存储数据文档。
有关使用 S3 的更多信息:集成指南:S3 支持的 MergeTree
- 将文件的所有者更新为
clickhouse
用户和组
chown clickhouse:clickhouse /etc/clickhouse-server/config.d/storage_config.xml
- 重启 ClickHouse 实例以使更改生效。
service clickhouse-server restart
测试
- 使用 ClickHouse 客户端登录,如下所示
clickhouse-client --user default --password ClickHouse123!
- 创建一个表,指定新的 S3 存储策略
CREATE TABLE s3_table1
(
`id` UInt64,
`column1` String
)
ENGINE = MergeTree
ORDER BY id
SETTINGS storage_policy = 's3_main';
- 显示该表已使用正确的策略创建
SHOW CREATE TABLE s3_table1;
┌─statement────────────────────────────────────────────────────
│ CREATE TABLE default.s3_table1
(
`id` UInt64,
`column1` String
)
ENGINE = MergeTree
ORDER BY id
SETTINGS storage_policy = 's3_main', index_granularity = 8192
└──────────────────────────────────────────────────────────────
- 将测试行插入到表中
INSERT INTO s3_table1
(id, column1)
VALUES
(1, 'abc'),
(2, 'xyz');
INSERT INTO s3_table1 (id, column1) FORMAT Values
Query id: 0265dd92-3890-4d56-9d12-71d4038b85d5
Ok.
2 rows in set. Elapsed: 0.337 sec.
- 查看行
SELECT * FROM s3_table1;
┌─id─┬─column1─┐
│ 1 │ abc │
│ 2 │ xyz │
└────┴─────────┘
2 rows in set. Elapsed: 0.284 sec.
- 在 AWS 控制台中,导航到存储桶,然后选择新的存储桶和文件夹。您应该看到类似以下内容
使用 S3 对象存储跨两个 AWS 区域复制单个分片
对象存储在 ClickHouse Cloud 中默认使用,如果您在 ClickHouse Cloud 中运行,则无需遵循此过程。
规划部署
本教程基于在 AWS EC2 中部署两个 ClickHouse Server 节点和三个 ClickHouse Keeper 节点。ClickHouse 服务器的数据存储是 S3。使用两个 AWS 区域,每个区域都有一个 ClickHouse Server 和一个 S3 存储桶,以支持灾难恢复。
ClickHouse 表跨两个服务器(因此也跨两个区域)进行复制。
安装软件
ClickHouse 服务器节点
在 ClickHouse 服务器节点上执行部署步骤时,请参阅安装说明。
部署 ClickHouse
在两个主机上部署 ClickHouse,在示例配置中,这些主机被命名为 chnode1
、chnode2
。
将 chnode1
放置在一个 AWS 区域中,将 chnode2
放置在第二个区域中。
部署 ClickHouse Keeper
在三个主机上部署 ClickHouse Keeper,在示例配置中,这些主机被命名为 keepernode1
、keepernode2
和 keepernode3
。keepernode1
可以与 chnode1
部署在同一区域,keepernode2
与 chnode2
部署在同一区域,keepernode3
可以部署在任一区域,但与该区域中的 ClickHouse 节点位于不同的可用区。
在 ClickHouse Keeper 节点上执行部署步骤时,请参阅安装说明。
创建 S3 存储桶
创建两个 S3 存储桶,每个存储桶分别位于您放置 chnode1
和 chnode2
的区域中。
如果您需要创建存储桶和 IAM 角色的分步说明,请展开创建 S3 存储桶和 IAM 角色并按照步骤操作
创建 S3 存储桶和 IAM 用户
本文演示了如何配置 AWS IAM 用户、创建 S3 存储桶以及配置 ClickHouse 以将该存储桶用作 S3 磁盘的基础知识。您应该与您的安全团队合作以确定要使用的权限,并将这些视为起点。
创建 AWS IAM 用户
在此过程中,我们将创建服务帐户用户,而不是登录用户。
-
登录到 AWS IAM 管理控制台。
-
在“用户”中,选择添加用户
- 输入用户名并将凭据类型设置为访问密钥 - 编程访问,然后选择下一步:权限
- 请勿将用户添加到任何组;选择下一步:标签
- 除非您需要添加任何标签,否则选择下一步:审核
-
选择创建用户
注意可以忽略声明用户没有权限的警告消息;权限将在下一节中授予给该用户的存储桶
- 用户现已创建;点击显示并复制访问密钥和密钥。
将密钥保存到其他地方;这是密钥将可用的唯一一次。
- 点击关闭,然后在用户屏幕中找到该用户。
- 复制 ARN(Amazon 资源名称)并保存,以便在配置存储桶的访问策略时使用。
创建 S3 存储桶
- 在 S3 存储桶部分,选择创建存储桶
- 输入存储桶名称,保留其他选项为默认值
存储桶名称在整个 AWS 中必须是唯一的,而不仅仅是在组织内,否则会发出错误。
- 保持
阻止所有公有访问
启用状态;不需要公有访问。
- 在页面底部选择创建存储桶
-
选择链接,复制 ARN,并保存,以便在配置存储桶的访问策略时使用。
-
存储桶创建完成后,在 S3 存储桶列表中找到新的 S3 存储桶并选择链接
- 选择创建文件夹
- 输入将作为 ClickHouse S3 磁盘的目标的文件夹名称,然后选择创建文件夹
- 该文件夹现在应该在存储桶列表中可见
- 选中新文件夹的复选框,然后点击复制 URL。保存复制的 URL,以便在下一节的 ClickHouse 存储配置中使用。
- 选择权限选项卡,然后点击存储桶策略部分中的编辑按钮
- 添加存储桶策略,示例如下
{
"Version": "2012-10-17",
"Id": "Policy123456",
"Statement": [
{
"Sid": "abc123",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::921234567898:user/mars-s3-user"
},
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::mars-doc-test",
"arn:aws:s3:::mars-doc-test/*"
]
}
]
}
|Parameter | Description | Example Value |
|----------|-------------|----------------|
|Version | Version of the policy interpreter, leave as-is | 2012-10-17 |
|Sid | User-defined policy id | abc123 |
|Effect | Whether user requests will be allowed or denied | Allow |
|Principal | The accounts or user that will be allowed | arn:aws:iam::921234567898:user/mars-s3-user |
|Action | What operations are allowed on the bucket| s3:*|
|Resource | Which resources in the bucket will operations be allowed in | "arn:aws:s3:::mars-doc-test", "arn:aws:s3:::mars-doc-test/*" |
您应该与您的安全团队合作,以确定要使用的权限,将这些权限视为起点。有关策略和设置的更多信息,请参阅 AWS 文档:https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-policy-language-overview.html
- 保存策略配置。
配置文件将放置在 /etc/clickhouse-server/config.d/
中。这是一个存储桶的示例配置文件,另一个类似,但三个突出显示的行有所不同
<clickhouse>
<storage_configuration>
<disks>
<s3_disk>
<type>s3</type>
<endpoint>https://docs-clickhouse-s3.s3.us-east-2.amazonaws.com/clickhouses3/</endpoint>
<access_key_id>ABCDEFGHIJKLMNOPQRST</access_key_id>
<secret_access_key>Tjdm4kf5snfkj303nfljnev79wkjn2l3knr81007</secret_access_key>
<metadata_path>/var/lib/clickhouse/disks/s3_disk/</metadata_path>
</s3_disk>
<s3_cache>
<type>cache</type>
<disk>s3</disk>
<path>/var/lib/clickhouse/disks/s3_cache/</path>
<max_size>10Gi</max_size>
</s3_cache>
</disks>
<policies>
<s3_main>
<volumes>
<main>
<disk>s3_disk</disk>
</main>
</volumes>
</s3_main>
</policies>
</storage_configuration>
</clickhouse>
本指南中的许多步骤将要求您将配置文件放置在 /etc/clickhouse-server/config.d/
中。这是 Linux 系统上配置文件覆盖的默认位置。当您将这些文件放入该目录时,ClickHouse 将使用这些内容来覆盖默认配置。通过将这些文件放置在覆盖目录中,您将避免在升级期间丢失配置。
配置 ClickHouse Keeper
当独立运行 ClickHouse Keeper(与 ClickHouse 服务器分开)时,配置是一个单独的 XML 文件。在本教程中,该文件是 /etc/clickhouse-keeper/keeper_config.xml
。所有三个 Keeper 服务器都使用相同的配置,只有一个设置不同:<server_id>
。
server_id
指示要分配给使用配置文件的主机的 ID。在下面的示例中,server_id
为 3
,如果您进一步查看文件中的 <raft_configuration>
部分,您将看到服务器 3 的主机名为 keepernode3
。这就是 ClickHouse Keeper 进程在选择领导者和所有其他活动时知道要连接到哪些其他服务器的方式。
<clickhouse>
<logger>
<level>trace</level>
<log>/var/log/clickhouse-keeper/clickhouse-keeper.log</log>
<errorlog>/var/log/clickhouse-keeper/clickhouse-keeper.err.log</errorlog>
<size>1000M</size>
<count>3</count>
</logger>
<listen_host>0.0.0.0</listen_host>
<keeper_server>
<tcp_port>9181</tcp_port>
<server_id>3</server_id>
<log_storage_path>/var/lib/clickhouse/coordination/log</log_storage_path>
<snapshot_storage_path>/var/lib/clickhouse/coordination/snapshots</snapshot_storage_path>
<coordination_settings>
<operation_timeout_ms>10000</operation_timeout_ms>
<session_timeout_ms>30000</session_timeout_ms>
<raft_logs_level>warning</raft_logs_level>
</coordination_settings>
<raft_configuration>
<server>
<id>1</id>
<hostname>keepernode1</hostname>
<port>9234</port>
</server>
<server>
<id>2</id>
<hostname>keepernode2</hostname>
<port>9234</port>
</server>
<server>
<id>3</id>
<hostname>keepernode3</hostname>
<port>9234</port>
</server>
</raft_configuration>
</keeper_server>
</clickhouse>
复制 ClickHouse Keeper 的配置文件到位(记住设置 <server_id>
)
sudo -u clickhouse \
cp keeper.xml /etc/clickhouse-keeper/keeper.xml
配置 ClickHouse Server
定义集群
ClickHouse 集群在配置的 <remote_servers>
部分中定义。在本示例中,定义了一个集群 cluster_1S_2R
,它由一个分片和两个副本组成。副本位于主机 chnode1
和 chnode2
上。
<clickhouse>
<remote_servers replace="true">
<cluster_1S_2R>
<shard>
<replica>
<host>chnode1</host>
<port>9000</port>
</replica>
<replica>
<host>chnode2</host>
<port>9000</port>
</replica>
</shard>
</cluster_1S_2R>
</remote_servers>
</clickhouse>
当使用集群时,定义宏来填充带有集群、分片和副本设置的 DDL 查询非常方便。此示例允许您指定复制表引擎的使用,而无需提供 shard
和 replica
详细信息。当您创建表时,您可以通过查询 system.tables
来查看 shard
和 replica
宏是如何使用的。
<clickhouse>
<distributed_ddl>
<path>/clickhouse/task_queue/ddl</path>
</distributed_ddl>
<macros>
<cluster>cluster_1S_2R</cluster>
<shard>1</shard>
<replica>replica_1</replica>
</macros>
</clickhouse>
上面的宏适用于 chnode1
,在 chnode2
上将 replica
设置为 replica_2
。
禁用零拷贝复制
在 ClickHouse 22.7 及更低版本中,对于 S3 和 HDFS 磁盘,设置 allow_remote_fs_zero_copy_replication
默认设置为 true
。对于此灾难恢复场景,此设置应设置为 false
,并且在 22.8 及更高版本中,默认设置为 false
。
此设置应为 false,原因有两个:1) 此功能尚未准备好用于生产环境;2) 在灾难恢复场景中,数据和元数据都需要存储在多个区域中。将 allow_remote_fs_zero_copy_replication
设置为 false
。
<clickhouse>
<merge_tree>
<allow_remote_fs_zero_copy_replication>false</allow_remote_fs_zero_copy_replication>
</merge_tree>
</clickhouse>
ClickHouse Keeper 负责协调跨 ClickHouse 节点的数据复制。要将 ClickHouse Keeper 节点告知 ClickHouse,请向每个 ClickHouse 节点添加一个配置文件。
<clickhouse>
<zookeeper>
<node index="1">
<host>keepernode1</host>
<port>9181</port>
</node>
<node index="2">
<host>keepernode2</host>
<port>9181</port>
</node>
<node index="3">
<host>keepernode3</host>
<port>9181</port>
</node>
</zookeeper>
</clickhouse>
配置网络
当您在 AWS 中配置安全设置以使您的服务器可以相互通信以及您可以与它们通信时,请参阅网络端口列表。
所有三个服务器都必须侦听网络连接,以便它们可以在服务器之间以及与 S3 进行通信。默认情况下,ClickHouse 仅在环回地址上侦听,因此必须更改此设置。这在 /etc/clickhouse-server/config.d/
中配置。这是一个示例,它配置 ClickHouse 和 ClickHouse Keeper 在所有 IP v4 接口上侦听。有关更多信息,请参阅文档或默认配置文件 /etc/clickhouse/config.xml
。
<clickhouse>
<listen_host>0.0.0.0</listen_host>
</clickhouse>
启动服务器
运行 ClickHouse Keeper
在每个 Keeper 服务器上,运行适用于您的操作系统的命令,例如
sudo systemctl enable clickhouse-keeper
sudo systemctl start clickhouse-keeper
sudo systemctl status clickhouse-keeper
检查 ClickHouse Keeper 状态
使用 netcat
向 ClickHouse Keeper 发送命令。例如,mntr
返回 ClickHouse Keeper 集群的状态。如果您在每个 Keeper 节点上运行该命令,您将看到一个是领导者,另外两个是追随者
echo mntr | nc localhost 9181
zk_version v22.7.2.15-stable-f843089624e8dd3ff7927b8a125cf3a7a769c069
zk_avg_latency 0
zk_max_latency 11
zk_min_latency 0
zk_packets_received 1783
zk_packets_sent 1783
zk_num_alive_connections 2
zk_outstanding_requests 0
zk_server_state leader
zk_znode_count 135
zk_watch_count 8
zk_ephemerals_count 3
zk_approximate_data_size 42533
zk_key_arena_size 28672
zk_latest_snapshot_size 0
zk_open_file_descriptor_count 182
zk_max_file_descriptor_count 18446744073709551615
zk_followers 2
zk_synced_followers 2
运行 ClickHouse Server
在每个 ClickHouse 服务器上运行
sudo service clickhouse-server start
验证 ClickHouse Server
当您添加集群配置时,定义了一个跨两个 ClickHouse 节点复制的单个分片。在此验证步骤中,您将检查集群是否在 ClickHouse 启动时构建,并且您将使用该集群创建一个复制表。
-
验证集群是否存在
show clusters
┌─cluster───────┐
│ cluster_1S_2R │
└───────────────┘
1 row in set. Elapsed: 0.009 sec. ` -
使用
ReplicatedMergeTree
表引擎在集群中创建一个表create table trips on cluster 'cluster_1S_2R' (
`trip_id` UInt32,
`pickup_date` Date,
`pickup_datetime` DateTime,
`dropoff_datetime` DateTime,
`pickup_longitude` Float64,
`pickup_latitude` Float64,
`dropoff_longitude` Float64,
`dropoff_latitude` Float64,
`passenger_count` UInt8,
`trip_distance` Float64,
`tip_amount` Float32,
`total_amount` Float32,
`payment_type` Enum8('UNK' = 0, 'CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4))
ENGINE = ReplicatedMergeTree
PARTITION BY toYYYYMM(pickup_date)
ORDER BY pickup_datetime
SETTINGS index_granularity = 8192, storage_policy='s3_main'┌─host────┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
│ chnode1 │ 9000 │ 0 │ │ 1 │ 0 │
│ chnode2 │ 9000 │ 0 │ │ 0 │ 0 │
└─────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘ -
了解先前定义的宏的用法
宏
shard
和replica
是先前定义的,在下面突出显示的行中,您可以看到这些值在每个 ClickHouse 节点上是如何替换的。此外,还使用了值uuid
;uuid
未在宏中定义,因为它是由系统生成的。SELECT create_table_query
FROM system.tables
WHERE name = 'trips'
FORMAT VerticalQuery id: 4d326b66-0402-4c14-9c2f-212bedd282c0
Row 1:
──────
create_table_query: CREATE TABLE default.trips (`trip_id` UInt32, `pickup_date` Date, `pickup_datetime` DateTime, `dropoff_datetime` DateTime, `pickup_longitude` Float64, `pickup_latitude` Float64, `dropoff_longitude` Float64, `dropoff_latitude` Float64, `passenger_count` UInt8, `trip_distance` Float64, `tip_amount` Float32, `total_amount` Float32, `payment_type` Enum8('UNK' = 0, 'CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4))
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{uuid}/{shard}', '{replica}')
PARTITION BY toYYYYMM(pickup_date) ORDER BY pickup_datetime SETTINGS index_granularity = 8192, storage_policy = 's3_main'
1 row in set. Elapsed: 0.012 sec.注意您可以通过设置
default_replica_path
和default_replica_name
来自定义上面显示的 zookeeper 路径'clickhouse/tables/{uuid}/{shard}
。文档在这里。
测试
这些测试将验证数据是否正在跨两个服务器复制,以及数据是否存储在 S3 存储桶中而不是本地磁盘上。
-
从纽约市出租车数据集添加数据
INSERT INTO trips
SELECT trip_id,
pickup_date,
pickup_datetime,
dropoff_datetime,
pickup_longitude,
pickup_latitude,
dropoff_longitude,
dropoff_latitude,
passenger_count,
trip_distance,
tip_amount,
total_amount,
payment_type
FROM s3('https://ch-nyc-taxi.s3.eu-west-3.amazonaws.com/tsv/trips_{0..9}.tsv.gz', 'TabSeparatedWithNames') LIMIT 1000000; -
验证数据是否存储在 S3 中。
此查询显示磁盘上数据的大小,以及用于确定使用哪个磁盘的策略。
SELECT
engine,
data_paths,
metadata_path,
storage_policy,
formatReadableSize(total_bytes)
FROM system.tables
WHERE name = 'trips'
FORMAT VerticalQuery id: af7a3d1b-7730-49e0-9314-cc51c4cf053c
Row 1:
──────
engine: ReplicatedMergeTree
data_paths: ['/var/lib/clickhouse/disks/s3_disk/store/551/551a859d-ec2d-4512-9554-3a4e60782853/']
metadata_path: /var/lib/clickhouse/store/e18/e18d3538-4c43-43d9-b083-4d8e0f390cf7/trips.sql
storage_policy: s3_main
formatReadableSize(total_bytes): 36.42 MiB
1 row in set. Elapsed: 0.009 sec.检查本地磁盘上数据的大小。从上面可以看出,存储的数百万行数据在磁盘上的大小为 36.42 MiB。这应该在 S3 上,而不是本地磁盘上。上面的查询还告诉我们本地磁盘上数据和元数据的存储位置。检查本地数据
root@chnode1:~# du -sh /var/lib/clickhouse/disks/s3_disk/store/551
536K /var/lib/clickhouse/disks/s3_disk/store/551检查每个 S3 存储桶中的 S3 数据(未显示总计,但在插入后,两个存储桶都存储了大约 36 MiB)
S3Express
S3Express 是 Amazon S3 中新的高性能、单可用区存储类。
您可以参考此博客,阅读有关我们使用 ClickHouse 测试 S3Express 的经验。
S3Express 将数据存储在单个可用区内。这意味着如果可用区中断,数据将不可用。
S3 磁盘
使用 S3Express 存储桶支持的存储创建表涉及以下步骤
- 创建
Directory
类型的存储桶 - 安装适当的存储桶策略,以授予您的 S3 用户所有必需的权限(例如,
"Action": "s3express:*"
以简单地允许无限制访问) - 在配置存储策略时,请提供
region
参数
存储配置与普通 S3 相同,例如,可能如下所示
<storage_configuration>
<disks>
<s3_express>
<type>s3</type>
<endpoint>https://my-test-bucket--eun1-az1--x-s3.s3express-eun1-az1.eu-north-1.amazonaws.com/store/</endpoint>
<region>eu-north-1</region>
<access_key_id>...</access_key_id>
<secret_access_key>...</secret_access_key>
</s3_express>
</disks>
<policies>
<s3_express>
<volumes>
<main>
<disk>s3_express</disk>
</main>
</volumes>
</s3_express>
</policies>
</storage_configuration>
然后在新存储上创建一个表
CREATE TABLE t
(
a UInt64,
s String
)
ENGINE = MergeTree
ORDER BY a
SETTINGS storage_policy = 's3_express';
S3 存储
S3 存储也受支持,但仅适用于 Object URL
路径。示例
select * from s3('https://test-bucket--eun1-az1--x-s3.s3express-eun1-az1.eu-north-1.amazonaws.com/file.csv', ...)
它还需要在配置中指定存储桶区域
<s3>
<perf-bucket-url>
<endpoint>https://test-bucket--eun1-az1--x-s3.s3express-eun1-az1.eu-north-1.amazonaws.com</endpoint>
<region>eu-north-1</region>
</perf-bucket-url>
</s3>
备份
可以将备份存储在我们上面创建的磁盘上
BACKUP TABLE t TO Disk('s3_express', 't.zip')
┌─id───────────────────────────────────┬─status─────────┐
│ c61f65ac-0d76-4390-8317-504a30ba7595 │ BACKUP_CREATED │
└──────────────────────────────────────┴────────────────┘
RESTORE TABLE t AS t_restored FROM Disk('s3_express', 't.zip')
┌─id───────────────────────────────────┬─status───┐
│ 4870e829-8d76-4171-ae59-cffaf58dea04 │ RESTORED │
└──────────────────────────────────────┴──────────┘