跳到主要内容
跳到主要内容

S3 表引擎

该引擎提供与 Amazon S3 生态系统的集成能力。此引擎类似于 HDFS 引擎,但提供了 S3 专用功能。

示例

CREATE TABLE s3_engine_table (name String, value UInt32)
    ENGINE=S3('https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/test-data.csv.gz', 'CSV', 'gzip')
    SETTINGS input_format_with_names_use_header = 0;

INSERT INTO s3_engine_table VALUES ('one', 1), ('two', 2), ('three', 3);

SELECT * FROM s3_engine_table LIMIT 2;
┌─name─┬─value─┐
│ one  │     1 │
│ two  │     2 │
└──────┴───────┘

创建表

CREATE TABLE s3_engine_table (name String, value UInt32)
    ENGINE = S3(path [, NOSIGN | aws_access_key_id, aws_secret_access_key,] format, [compression], [partition_strategy], [partition_columns_in_data_file])
    [PARTITION BY expr]
    [SETTINGS ...]

引擎参数

  • path — 带有文件路径的存储桶 URL。在只读模式下支持以下通配符:***?{abc,def}{N..M},其中 NM 为数字,'abc''def' 为字符串。更多信息参见下文
  • NOSIGN - 如果在凭证位置提供此关键字,所有请求都不会被签名。
  • format — 文件的格式
  • aws_access_key_id, aws_secret_access_key - AWS 账号用户的长期凭证。可以使用它们对请求进行认证。该参数为可选。如果未指定凭证,则会从配置文件中读取。更多信息参见 Using S3 for Data Storage
  • compression — 压缩类型。支持的值:nonegzip/gzbrotli/brxz/LZMAzstd/zst。该参数为可选。默认情况下,将根据文件扩展名自动检测压缩类型。
  • partition_strategy – 选项:WILDCARDHIVEWILDCARD 要求路径中包含 {_partition_id},该占位符会被分区键替换。HIVE 不允许使用通配符,假定路径为表的根路径,并生成 Hive 风格的分区目录,使用 Snowflake ID 作为文件名,并以文件格式作为扩展名。默认值为 WILDCARD
  • partition_columns_in_data_file - 仅在使用 HIVE 分区策略时使用。用于指示 ClickHouse 数据文件中是否包含分区列。默认值为 false
  • storage_class_name - 选项:STANDARDINTELLIGENT_TIERING,用于指定 AWS S3 Intelligent Tiering 存储类别。

数据缓存

S3 表引擎支持在本地磁盘上进行数据缓存。 有关文件系统缓存配置选项和使用方法,请参见本。 缓存键基于存储对象的路径和 ETag,因此 ClickHouse 不会读取已过期的缓存版本。

要启用缓存,请使用设置 filesystem_cache_name = '<name>'enable_filesystem_cache = 1

SELECT *
FROM s3('http://minio:10000/clickhouse//test_3.csv', 'minioadmin', 'minioadminpassword', 'CSV')
SETTINGS filesystem_cache_name = 'cache_for_s3', enable_filesystem_cache = 1;

在配置文件中定义缓存有两种方式。

  1. 在 ClickHouse 配置文件中添加如下配置段:
<clickhouse>
    <filesystem_caches>
        <cache_for_s3>
            <path>缓存目录的路径</path>
            <max_size>10Gi</max_size>
        </cache_for_s3>
    </filesystem_caches>
</clickhouse>
  1. 复用 ClickHouse 中 storage_configuration 部分的缓存配置(以及相应的缓存存储),如本节所述

PARTITION BY

PARTITION BY — 可选。大多数情况下您不需要分区键,即便需要,一般也不需要比“按月”更细粒度的分区键。分区并不会加快查询速度(与 ORDER BY 表达式相反)。绝不要使用粒度过细的分区。不要按客户端标识符或名称对数据进行分区(而应将客户端标识符或名称设置为 ORDER BY 表达式中的第一列)。

对于按月分区,使用 toYYYYMM(date_column) 表达式,其中 date_column 是类型为 Date 的日期列。此时的分区名称采用 "YYYYMM" 格式。

分区策略

WILDCARD(默认):将文件路径中的 {_partition_id} 通配符替换为实际的分区键。不支持读取操作。

HIVE 实现了用于读写的 Hive 风格分区。读取通过递归的 glob 模式完成,等价于 SELECT * FROM s3('table_root/**.parquet')。 写入会按如下格式生成文件:<prefix>/<key1=val1/key2=val2...>/<snowflakeid>.<toLower(file_format)>

注意:当使用 HIVE 分区策略时,use_hive_partitioning 设置不会生效。

HIVE 分区策略示例:

arthur :) CREATE TABLE t_03363_parquet (year UInt16, country String, counter UInt8)
ENGINE = S3(s3_conn, filename = 't_03363_parquet', format = Parquet, partition_strategy='hive')
PARTITION BY (year, country);

arthur :) INSERT INTO t_03363_parquet VALUES
    (2022, 'USA', 1),
    (2022, 'Canada', 2),
    (2023, 'USA', 3),
    (2023, 'Mexico', 4),
    (2024, 'France', 5),
    (2024, 'Germany', 6),
    (2024, 'Germany', 7),
    (1999, 'Brazil', 8),
    (2100, 'Japan', 9),
    (2024, 'CN', 10),
    (2025, '', 11);

arthur :) select _path, * from t_03363_parquet;

    ┌─_path──────────────────────────────────────────────────────────────────────┬─year─┬─country─┬─counter─┐
 1. │ test/t_03363_parquet/year=2100/country=Japan/7329604473272971264.parquet   │ 2100 │ Japan   │       9 │
 2. │ test/t_03363_parquet/year=2024/country=France/7329604473323302912.parquet  │ 2024 │ France  │       5 │
 3. │ test/t_03363_parquet/year=2022/country=Canada/7329604473314914304.parquet  │ 2022 │ Canada  │       2 │
 4. │ test/t_03363_parquet/year=1999/country=Brazil/7329604473289748480.parquet  │ 1999 │ Brazil  │       8 │
 5. │ test/t_03363_parquet/year=2023/country=Mexico/7329604473293942784.parquet  │ 2023 │ Mexico  │       4 │
 6. │ test/t_03363_parquet/year=2023/country=USA/7329604473319108608.parquet     │ 2023 │ USA     │       3 │
 7. │ test/t_03363_parquet/year=2025/country=/7329604473327497216.parquet        │ 2025 │         │      11 │
 8. │ test/t_03363_parquet/year=2024/country=CN/7329604473310720000.parquet      │ 2024 │ CN      │      10 │
 9. │ test/t_03363_parquet/year=2022/country=USA/7329604473298137088.parquet     │ 2022 │ USA     │       1 │
10. │ test/t_03363_parquet/year=2024/country=Germany/7329604473306525696.parquet │ 2024 │ Germany │       6 │
11. │ test/t_03363_parquet/year=2024/country=Germany/7329604473306525696.parquet │ 2024 │ Germany │       7 │
    └────────────────────────────────────────────────────────────────────────────┴──────┴─────────┴─────────┘

查询分区数据

本示例使用了集成 ClickHouse 和 MinIO 的 docker compose 配置示例。你只需替换 endpoint 和认证参数即可在 S3 上复现相同的查询。

请注意,在 ENGINE 配置中,S3 endpoint 将参数占位符 {_partition_id} 用作 S3 对象(文件名)的一部分,而 SELECT 查询则是针对这些生成的对象名称进行查询(例如 test_3.csv)。

注意

如示例所示,目前尚不支持直接从分区的 S3 表中进行查询, 但可以通过使用 S3 表函数分别查询各个分区来实现。

在 S3 中写入分区数据的主要用途,是为了将这些数据迁移到另一套 ClickHouse 系统(例如,从本地部署系统迁移到 ClickHouse Cloud)。由于 ClickHouse 数据集通常非常庞大,且网络可靠性有时并不理想, 因此将数据集拆分为多个子集进行传输是合理的做法,这也正是进行分区写入的原因。

创建表

CREATE TABLE p
(
    `column1` UInt32,
    `column2` UInt32,
    `column3` UInt32
)
ENGINE = S3(
-- highlight-next-line
           'http://minio:10000/clickhouse//test_{_partition_id}.csv',
           'minioadmin',
           'minioadminpassword',
           'CSV')
PARTITION BY column3

插入数据

INSERT INTO p VALUES (1, 2, 3), (3, 2, 1), (78, 43, 45)

查询分区 3

提示

此查询使用 S3 表函数。

SELECT *
FROM s3('http://minio:10000/clickhouse//test_3.csv', 'minioadmin', 'minioadminpassword', 'CSV')
┌─c1─┬─c2─┬─c3─┐
│  1 │  2 │  3 │
└────┴────┴────┘

从分区 1 查询数据

SELECT *
FROM s3('http://minio:10000/clickhouse//test_1.csv', 'minioadmin', 'minioadminpassword', 'CSV')
┌─c1─┬─c2─┬─c3─┐
│  3 │  2 │  1 │
└────┴────┴────┘

从分区 45 中查询数据

SELECT *
FROM s3('http://minio:10000/clickhouse//test_45.csv', 'minioadmin', 'minioadminpassword', 'CSV')
┌─c1─┬─c2─┬─c3─┐
│ 78 │ 43 │ 45 │
└────┴────┴────┘

限制

你可能很自然地会尝试执行 Select * from p,但如上所述,此查询会失败;请改用前面的查询。

SELECT * FROM p
服务器返回异常(版本 23.4.1):
代码:48. DB::Exception: 来自 localhost:9000。DB::Exception: 尚未实现从分区 S3 存储读取数据的功能。(NOT_IMPLEMENTED)

插入数据

请注意,数据行只能插入到新文件中,不会执行合并周期或文件拆分操作。一旦文件写入完成,后续插入将会失败。为避免这种情况,可以使用 s3_truncate_on_inserts3_create_new_file_on_insert 设置。更多详情请参见此处

虚拟列

  • _path — 文件的路径。类型:LowCardinality(String)
  • _file — 文件名。类型:LowCardinality(String)
  • _size — 文件大小(字节)。类型:Nullable(UInt64)。如果大小未知,则值为 NULL
  • _time — 文件的最后修改时间。类型:Nullable(DateTime)。如果时间未知,则值为 NULL
  • _etag — 文件的 ETag。类型:LowCardinality(String)。如果 ETag 未知,则值为 NULL
  • _tags — 文件的标签。类型:Map(String, String)。如果没有标签,则值为空映射 {}

有关虚拟列的更多信息,请参阅此处`.

实现细节

  • 读写操作可以并行进行

  • 不支持:

    • ALTERSELECT...SAMPLE 操作。
    • 索引。
    • 零拷贝 复制在技术上可行,但目前不受支持。
    零拷贝复制尚未准备好用于生产环境

    在 ClickHouse 22.8 及更高版本中,零拷贝复制默认被禁用。该特性不建议在生产环境中使用。

路径中的通配符

path 参数可以使用类似 bash 的通配符来指定多个文件。要参与处理,文件必须实际存在,并且与整个路径模式完全匹配。文件列表在执行 SELECT 时确定(而不是在 CREATE 时)。

  • * — 匹配任意数量的任意字符(不包括 /),可以为空字符串。
  • ** — 匹配任意数量的任意字符(包括 /),可以为空字符串。
  • ? — 匹配任意单个字符。
  • {some_string,another_string,yet_another_one} — 匹配字符串 'some_string'、'another_string'、'yet_another_one' 中的任意一个。
  • {N..M} — 匹配从 N 到 M 范围内(含两端)的任意数字。N 和 M 可以有前导零,例如 000..078

带有 {} 的写法类似于 remote 表函数。

注意

如果文件列表中包含带前导零的数字范围,请对每一位数字分别使用带花括号的写法,或者使用 ?

通配符示例 1

创建一个表,使用名为 file-000.csvfile-001.csv、...、file-999.csv 的文件:

CREATE TABLE big_table (name String, value UInt32)
    ENGINE = S3('https://clickhouse-public-datasets.s3.amazonaws.com/my-bucket/my_folder/file-{000..999}.csv', 'CSV');

带有通配符的示例 2

假设我们有几个 CSV 格式的文件,其在 S3 上的 URI 如下:

有几种方法可以创建一个由这六个文件构成的表:

  1. 指定文件后缀的范围:
CREATE TABLE table_with_range (name String, value UInt32)
    ENGINE = S3('https://clickhouse-public-datasets.s3.amazonaws.com/my-bucket/{some,another}_folder/some_file_{1..3}', 'CSV');
  1. 将所有带有 some_file_ 前缀的文件取出(两个文件夹中都不应存在除此之外、带有该前缀的额外文件):
CREATE TABLE table_with_question_mark (name String, value UInt32)
    ENGINE = S3('https://clickhouse-public-datasets.s3.amazonaws.com/my-bucket/{some,another}_folder/some_file_?', 'CSV');
  1. 将这两个文件夹中的所有文件拿出来(所有文件都应符合查询中描述的格式和架构):
CREATE TABLE table_with_asterisk (name String, value UInt32)
    ENGINE = S3('https://clickhouse-public-datasets.s3.amazonaws.com/my-bucket/{some,another}_folder/*', 'CSV');

存储设置

与 S3 相关的设置

以下设置可以在查询执行前进行配置,或写入配置文件。

  • s3_max_single_part_upload_size — 使用单部分上传到 S3 时,单个对象允许的最大大小。默认值为 32Mb
  • s3_min_upload_part_size — 使用 S3 多部分上传 时,每个分片的最小大小。默认值为 16Mb
  • s3_max_redirects — 允许的 S3 重定向跳转的最大次数。默认值为 10
  • s3_single_read_retries — 单次读操作的最大重试次数。默认值为 4
  • s3_max_put_rps — 在开始限流前,每秒允许的最大 PUT 请求数。默认值为 0(无限制)。
  • s3_max_put_burst — 在触及每秒请求数限制前,可以同时发起的最大请求数。默认值为 0 时,等同于 s3_max_put_rps
  • s3_max_get_rps — 在开始限流前,每秒允许的最大 GET 请求数。默认值为 0(无限制)。
  • s3_max_get_burst — 在触及每秒请求数限制前,可以同时发起的最大请求数。默认值为 0 时,等同于 s3_max_get_rps
  • s3_upload_part_size_multiply_factor - 每当在一次对 S3 的写入中上传了 s3_multiply_parts_count_threshold 个分片时,将 s3_min_upload_part_size 按该因子放大。默认值为 2
  • s3_upload_part_size_multiply_parts_count_threshold - 每当向 S3 上传了该数量的分片时,会将 s3_min_upload_part_size 乘以 s3_upload_part_size_multiply_factor。默认值为 500
  • s3_max_inflight_parts_for_one_file - 限制对同一对象可以并发执行的 PUT 请求数。该数量应被限制。值为 0 表示不限制。默认值为 20。每个进行中的分片(in-flight part)都会占用一个缓冲区,对于前 s3_upload_part_size_multiply_factor 个分片,缓冲区大小为 s3_min_upload_part_size;当文件足够大时,缓冲区会更大,详见 upload_part_size_multiply_factor。在默认设置下,对于小于 8G 的文件,单个上传文件占用的内存不超过 320Mb。对于更大的文件,占用会更高。

安全注意事项:如果恶意用户可以指定任意 S3 URL,应将 s3_max_redirects 设为 0 以避免 SSRF 攻击;或者在服务器配置中指定 remote_host_filter

基于 endpoint 的设置

可以在配置文件中为指定的 endpoint 设置以下配置(通过 URL 的精确前缀进行匹配):

  • endpoint — 指定 endpoint 的前缀。必需项。
  • access_key_idsecret_access_key — 指定用于该 endpoint 的凭证。可选。
  • use_environment_credentials — 如果设置为 true,S3 客户端将尝试从环境变量和给定 endpoint 的 Amazon EC2 元数据中获取凭证。可选,默认值为 false
  • region — 指定 S3 区域名。可选。
  • use_insecure_imds_request — 如果设置为 true,S3 客户端在从 Amazon EC2 元数据获取凭证时将使用不安全的 IMDS 请求。可选,默认值为 false
  • expiration_window_seconds — 用于检查基于过期时间的凭证是否已失效的宽限时间(秒)。可选,默认值为 120
  • no_sign_request - 忽略所有凭证,使请求不进行签名。用于访问公共存储桶时非常有用。
  • header — 将指定的 HTTP 头添加到对给定 endpoint 的请求中。可选,可以指定多次。
  • access_header - 当不存在来自其他来源的凭证时,将指定的 HTTP 头添加到对给定 endpoint 的请求中。
  • server_side_encryption_customer_key_base64 — 如果指定,将为访问使用 SSE-C 加密的 S3 对象设置所需的 HTTP 头。可选。
  • server_side_encryption_kms_key_id - 如果指定,将为访问使用 SSE-KMS 加密 的 S3 对象设置所需的 HTTP 头。如果指定为空字符串,则使用 AWS 托管的 S3 密钥。可选。
  • server_side_encryption_kms_encryption_context - 如果与 server_side_encryption_kms_key_id 一起指定,将设置给定的 SSE-KMS 加密上下文 HTTP 头。可选。
  • server_side_encryption_kms_bucket_key_enabled - 如果与 server_side_encryption_kms_key_id 一起指定,将设置启用 SSE-KMS S3 bucket key 的 HTTP 头。可选,可以为 truefalse,默认不设置(遵循 bucket 级别设置)。
  • max_single_read_retries — 单次读取时的最大重试次数。默认值为 4。可选。
  • max_put_rpsmax_put_burstmax_get_rpsmax_get_burst - 限流设置(参见上文说明),用于特定 endpoint,而不是按查询进行设置。可选。

示例:

<s3>
    <endpoint-name>
        <endpoint>https://clickhouse-public-datasets.s3.amazonaws.com/my-test-bucket-768/</endpoint>
        <!-- <access_key_id>ACCESS_KEY_ID</access_key_id> -->
        <!-- <secret_access_key>SECRET_ACCESS_KEY</secret_access_key> -->
        <!-- <region>us-west-1</region> -->
        <!-- <use_environment_credentials>false</use_environment_credentials> -->
        <!-- <use_insecure_imds_request>false</use_insecure_imds_request> -->
        <!-- <expiration_window_seconds>120</expiration_window_seconds> -->
        <!-- <no_sign_request>false</no_sign_request> -->
        <!-- <header>Authorization: Bearer SOME-TOKEN</header> -->
        <!-- <server_side_encryption_customer_key_base64>BASE64-ENCODED-KEY</server_side_encryption_customer_key_base64> -->
        <!-- <server_side_encryption_kms_key_id>KMS_KEY_ID</server_side_encryption_kms_key_id> -->
        <!-- <server_side_encryption_kms_encryption_context>KMS_ENCRYPTION_CONTEXT</server_side_encryption_kms_encryption_context> -->
        <!-- <server_side_encryption_kms_bucket_key_enabled>true</server_side_encryption_kms_bucket_key_enabled> -->
        <!-- <max_single_read_retries>4</max_single_read_retries> -->
    </endpoint-name>
</s3>

使用归档文件

假设我们在 S3 上有若干归档文件,其 URI 如下:

可以使用 :: 从这些归档文件中提取数据。通配模式(glob)既可以用于 URL 的部分,也可以用于 :: 之后的部分(用于指定归档内部文件的名称)。

SELECT *
FROM s3(
   'https://s3-us-west-1.amazonaws.com/umbrella-static/top-1m-2018-01-1{0..2}.csv.zip :: *.csv'
);
注意

ClickHouse 支持三种归档格式: ZIP TAR 7Z 虽然可以从任意受支持的存储位置访问 ZIP 和 TAR 归档,但 7Z 归档只能从安装了 ClickHouse 的本地文件系统读取。

访问公共 bucket

ClickHouse 会尝试从多种不同类型的来源自动获取凭证。 有时,在访问某些公共 bucket 时,这可能会导致问题,从而导致客户端返回 403 错误码。 可以通过使用 NOSIGN 关键字来避免这一问题,该关键字会强制客户端忽略所有凭证,不对请求进行签名。

CREATE TABLE big_table (name String, value UInt32)
    ENGINE = S3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/aapl_stock.csv', NOSIGN, 'CSVWithNames');

性能优化

如需了解如何优化 s3 函数的性能,请参阅我们的详细指南

另请参阅