跳至主要内容

数据复制

注意

在 ClickHouse Cloud 中,复制由您管理。请创建您的表格,无需添加参数。例如,在下面的文本中,您将替换

ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/table_name',
'{replica}',
ver
)

ENGINE = ReplicatedMergeTree

复制仅支持 MergeTree 家族中的表格

  • ReplicatedMergeTree
  • ReplicatedSummingMergeTree
  • ReplicatedReplacingMergeTree
  • ReplicatedAggregatingMergeTree
  • ReplicatedCollapsingMergeTree
  • ReplicatedVersionedCollapsingMergeTree
  • ReplicatedGraphiteMergeTree

复制在单个表级别工作,而不是整个服务器。服务器可以同时存储复制表和非复制表。

复制不依赖于分片。每个分片都有其自己的独立复制。

INSERTALTER 查询的压缩数据会被复制(有关更多信息,请参见 ALTER 文档)。

CREATEDROPATTACHDETACHRENAME 查询在单个服务器上执行,不会被复制。

  • CREATE TABLE 查询在运行查询的服务器上创建一个新的可复制表。如果此表已存在于其他服务器上,它将添加一个新的副本。
  • DROP TABLE 查询删除位于运行查询的服务器上的副本。
  • RENAME 查询在其中一个副本上重命名表。换句话说,复制表在不同的副本上可以有不同的名称。

ClickHouse 使用 ClickHouse Keeper 来存储副本元数据信息。可以使用 ZooKeeper 版本 3.4.5 或更高版本,但建议使用 ClickHouse Keeper。

要使用复制,请在 zookeeper 服务器配置部分设置参数。

注意

不要忽略安全设置。ClickHouse 支持 ZooKeeper 安全子系统的 digest ACL 方案

设置 ClickHouse Keeper 集群地址的示例

<zookeeper>
<node>
<host>example1</host>
<port>2181</port>
</node>
<node>
<host>example2</host>
<port>2181</port>
</node>
<node>
<host>example3</host>
<port>2181</port>
</node>
</zookeeper>

ClickHouse 还支持在辅助 ZooKeeper 集群中存储副本元数据信息。为此,请提供 ZooKeeper 集群名称和路径作为引擎参数。换句话说,它支持在不同的 ZooKeeper 集群中存储不同表的元数据。

设置辅助 ZooKeeper 集群地址的示例

<auxiliary_zookeepers>
<zookeeper2>
<node>
<host>example_2_1</host>
<port>2181</port>
</node>
<node>
<host>example_2_2</host>
<port>2181</port>
</node>
<node>
<host>example_2_3</host>
<port>2181</port>
</node>
</zookeeper2>
<zookeeper3>
<node>
<host>example_3_1</host>
<port>2181</port>
</node>
</zookeeper3>
</auxiliary_zookeepers>

要在辅助 ZooKeeper 集群而不是默认的 ZooKeeper 集群中存储表元数据,我们可以使用 SQL 来创建具有 ReplicatedMergeTree 引擎的表,如下所示

CREATE TABLE table_name ( ... ) ENGINE = ReplicatedMergeTree('zookeeper_name_configured_in_auxiliary_zookeepers:path', 'replica_name') ...

您可以指定任何现有的 ZooKeeper 集群,系统将使用其上的一个目录用于其自身的数据(目录在创建可复制表时指定)。

如果配置文件中未设置 ZooKeeper,则无法创建复制表,并且任何现有的复制表将处于只读状态。

ZooKeeper 不在 SELECT 查询中使用,因为复制不影响 SELECT 的性能,查询的运行速度与非复制表的运行速度一样快。在查询分布式复制表时,ClickHouse 行为由设置 max_replica_delay_for_distributed_queriesfallback_to_stale_replicas_for_distributed_queries 控制。

对于每个 INSERT 查询,大约十个条目通过多个事务添加到 ZooKeeper 中。(更准确地说,这是针对每个插入的数据块;一个 INSERT 查询包含一个块或每个 max_insert_block_size = 1048576 行一个块。)这会导致 INSERT 相比于非复制表稍长一点的延迟。但是,如果您按照建议以每秒不超过一个 INSERT 的批次插入数据,则不会出现任何问题。用于协调一个 ZooKeeper 集群的整个 ClickHouse 集群每秒总共有数百个 INSERT。数据插入的吞吐量(每秒的行数)与非复制数据一样高。

对于非常大的集群,您可以为不同的分片使用不同的 ZooKeeper 集群。但是,根据我们对具有大约 300 台服务器的生产集群的经验,这已被证明没有必要。

复制是异步的和多主机的。INSERT 查询(以及 ALTER)可以发送到任何可用的服务器。数据插入到运行查询的服务器上,然后复制到其他服务器上。由于它是异步的,因此最近插入的数据以一定的延迟出现在其他副本上。如果部分副本不可用,则在它们可用时写入数据。如果副本可用,则延迟是将压缩数据块通过网络传输所需的时间。通过 background_schedule_pool_size 设置可以设置执行复制表后台任务的线程数。

ReplicatedMergeTree 引擎使用单独的线程池进行复制获取。池的大小受 background_fetches_pool_size 设置的限制,可以通过服务器重启进行调整。

默认情况下,INSERT 查询仅等待来自一个副本的写入数据的确认。如果数据仅成功写入了一个副本,并且具有此副本的服务器停止存在,则存储的数据将丢失。要启用从多个副本获取数据写入确认,请使用 insert_quorum 选项。

每个数据块都以原子方式写入。INSERT 查询被分成最多 max_insert_block_size = 1048576 行的块。换句话说,如果 INSERT 查询少于 1048576 行,则它是原子地完成的。

数据块会被去重。对于对同一个数据块的多次写入(相同大小的数据块,包含相同顺序的相同行),该块仅写入一次。这样做的原因是,如果发生网络故障,客户端应用程序不知道数据是否已写入 DB,因此可以简单地重复 INSERT 查询。将 INSERT 发送到哪个副本无关紧要,只要数据相同。INSERT 是幂等的。去重参数由 merge_tree 服务器设置控制。

在复制过程中,只有要插入的源数据通过网络传输。进一步的数据转换(合并)在所有副本上以相同的方式协调和执行。这最大程度地减少了网络使用量,这意味着复制在副本位于不同数据中心时效果很好。(请注意,在不同数据中心复制数据是复制的主要目标。)

您可以拥有相同数据的任意数量的副本。根据我们的经验,相对可靠且方便的解决方案可以在生产中使用双重复制,每个服务器都使用 RAID-5 或 RAID-6(在某些情况下使用 RAID-10)。

系统监控副本上的数据同步性,并且能够在发生故障后恢复。故障转移是自动的(对于数据的小差异)或半自动的(当数据差异太大时,这可能表明配置错误)。

创建复制表

注意

在 ClickHouse Cloud 中,复制由您管理。请创建您的表格,无需添加参数。例如,在下面的文本中,您将替换

ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/table_name', '{replica}', ver)

ENGINE = ReplicatedMergeTree

Replicated 前缀将添加到表引擎名称。例如:ReplicatedMergeTree

提示

在 ClickHouse Cloud 中,添加 Replicated 是可选的,因为所有表格都是复制的。

复制*MergeTree 参数

zoo_path

zoo_path — ClickHouse Keeper 中表的路径。

replica_name

replica_name — ClickHouse Keeper 中的副本名称。

other_parameters

other_parameters — 用于创建复制版本的引擎的参数,例如 ReplacingMergeTree 中的版本。

示例

CREATE TABLE table_name
(
EventDate DateTime,
CounterID UInt32,
UserID UInt32,
ver UInt16
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/table_name', '{replica}', ver)
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID);
已弃用语法中的示例
CREATE TABLE table_name
(
EventDate DateTime,
CounterID UInt32,
UserID UInt32
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/table_name', '{replica}', EventDate, intHash32(UserID), (CounterID, EventDate, intHash32(UserID), EventTime), 8192);

如示例所示,这些参数可以包含花括号中的替换。替换的值取自配置文件的 部分。

示例

<macros>
<shard>02</shard>
<replica>example05-02-1</replica>
</macros>

ClickHouse Keeper 中表的路径对于每个复制表应该是唯一的。不同分片上的表格应该有不同的路径。在这种情况下,路径由以下部分组成

/clickhouse/tables/ 是公共前缀。我们建议使用完全相同的路径。

{shard} 将扩展为分片标识符。

table_name 是 ClickHouse Keeper 中表的节点名称。最好使它与表名称相同。它是显式定义的,因为与表名称不同,它在 RENAME 查询后不会更改。提示:您也可以在 table_name 前面添加数据库名称。例如 db_name.table_name

内置的两个替换 {database}{table} 可供使用,它们分别展开为表名和数据库名(除非这些宏在 macros 部分定义)。因此,ZooKeeper 路径可以指定为 '/clickhouse/tables/{shard}/{database}/{table}'。使用这些内置替换时,要注意表重命名。ClickHouse Keeper 中的路径无法更改,当表重命名时,宏会展开到不同的路径,表将引用 ClickHouse Keeper 中不存在的路径,并进入只读模式。

副本名称标识同一表的不同副本。您可以使用服务器名称来标识,如示例所示。名称只需要在每个分片内唯一。

您可以明确定义参数,而不是使用替换。这对于测试和配置小型集群可能很方便。但是,在这种情况下,您不能使用分布式 DDL 查询(ON CLUSTER)。

在处理大型集群时,建议使用替换,因为它们可以降低出错的概率。

您可以在服务器配置文件中为 Replicated 表引擎指定默认参数。例如

<default_replica_path>/clickhouse/tables/{shard}/{database}/{table}</default_replica_path>
<default_replica_name>{replica}</default_replica_name>

在这种情况下,您可以在创建表时省略参数

CREATE TABLE table_name (
x UInt32
) ENGINE = ReplicatedMergeTree
ORDER BY x;

它等效于

CREATE TABLE table_name (
x UInt32
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/{database}/table_name', '{replica}')
ORDER BY x;

在每个副本上运行 CREATE TABLE 查询。此查询创建新的复制表,或将新的副本添加到现有表中。

如果在表已包含其他副本上的一些数据后添加了新的副本,则在运行查询后,数据将从其他副本复制到新的副本。换句话说,新的副本与其他副本同步。

要删除副本,请运行 DROP TABLE。但是,只删除一个副本——位于您运行查询的服务器上的副本。

故障恢复

如果服务器启动时 ClickHouse Keeper 不可访问,则复制表会切换到只读模式。系统会定期尝试连接到 ClickHouse Keeper。

如果在 INSERT 期间 ClickHouse Keeper 不可访问,或者在与 ClickHouse Keeper 交互时发生错误,则会抛出异常。

连接到 ClickHouse Keeper 后,系统会检查本地文件系统中的数据集是否与预期数据集匹配(ClickHouse Keeper 存储此信息)。如果有轻微的不一致,系统会通过与副本同步数据来解决这些不一致。

如果系统检测到损坏的数据块(文件大小错误)或无法识别的块(写入文件系统但未记录在 ClickHouse Keeper 中的块),则会将其移动到 detached 子目录(不会删除它们)。任何缺失的块都会从副本中复制过来。

请注意,ClickHouse 不会执行任何破坏性操作,例如自动删除大量数据。

当服务器启动(或与 ClickHouse Keeper 建立新的会话)时,它只会检查所有文件的数量和大小。如果文件大小匹配,但字节在中间某处发生了变化,则不会立即检测到,而只会在尝试读取 SELECT 查询的数据时检测到。查询会抛出一个关于压缩块的校验和或大小不匹配的异常。在这种情况下,数据块会被添加到验证队列中,如果需要,会从副本中复制过来。

如果本地数据集与预期数据集差异过大,则会触发安全机制。服务器会在日志中记录此事件,并拒绝启动。这样做的原因是,这种情况可能表明配置错误,例如,如果一个分片上的副本意外配置为与另一个分片上的副本相同。但是,此机制的阈值设置得比较低,这种情况可能会在正常故障恢复期间发生。在这种情况下,数据会通过“按下按钮”的方式半自动恢复。

要启动恢复,请在 ClickHouse Keeper 中创建节点 /path_to_table/replica_name/flags/force_restore_data(任何内容都可以),或运行命令恢复所有复制表

sudo -u clickhouse touch /var/lib/clickhouse/flags/force_restore_data

然后重新启动服务器。在启动时,服务器会删除这些标志并开始恢复。

完全数据丢失后的恢复

如果所有数据和元数据都从其中一台服务器上消失了,请按照以下步骤进行恢复

  1. 在服务器上安装 ClickHouse。如果您使用替换,则在包含分片标识符和副本的配置文件中正确定义替换。
  2. 如果您有未复制的表,必须在服务器上手动复制这些表,请从副本(在目录 /var/lib/clickhouse/data/db_name/table_name/ 中)复制其数据。
  3. 从副本中复制位于 /var/lib/clickhouse/metadata/ 中的表定义。如果在表定义中明确定义了分片或副本标识符,请将其更正,使其与该副本相对应。(或者,启动服务器,并进行所有 ATTACH TABLE 查询,这些查询应该位于 /var/lib/clickhouse/metadata/ 中的 .sql 文件中。)
  4. 要启动恢复,请创建 ClickHouse Keeper 节点 /path_to_table/replica_name/flags/force_restore_data(任何内容都可以),或运行命令恢复所有复制表:sudo -u clickhouse touch /var/lib/clickhouse/flags/force_restore_data

然后启动服务器(如果服务器已经在运行,则重新启动)。数据将从副本下载。

另一种恢复方法是从 ClickHouse Keeper 中删除有关丢失副本的信息(/path_to_table/replica_name),然后如“创建复制表”中所述,再次创建该副本。

恢复过程中对网络带宽没有限制。如果您要同时恢复多个副本,请记住这一点。

从 MergeTree 转换为 ReplicatedMergeTree

我们使用术语 MergeTree 来指代 MergeTree 家族 中的所有表引擎,与 ReplicatedMergeTree 相同。

如果您有一个手动复制的 MergeTree 表,则可以将其转换为复制表。如果您已经在 MergeTree 表中收集了大量数据,并且现在想要启用复制,则可能需要执行此操作。

如果在表的 data 目录(对于 Atomic 数据库,为 /store/xxx/xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy/)中设置了 convert_to_replicated 标志,则可以在服务器重启时自动转换 MergeTree 表。创建空的 convert_to_replicated 文件,并在下次服务器重启时,表将被加载为复制表。

此查询可用于获取表的 data 路径。如果表有多个 data 路径,则需要使用第一个路径。

SELECT data_paths FROM system.tables WHERE table = 'table_name' AND database = 'database_name';

请注意,ReplicatedMergeTree 表将使用 default_replica_pathdefault_replica_name 设置的值创建。要在其他副本上创建已转换的表,您需要在 ReplicatedMergeTree 引擎的第一个参数中明确指定其路径。以下查询可用于获取其路径。

SELECT zookeeper_path FROM system.replicas WHERE table = 'table_name';

还有一种在不重启服务器的情况下手动执行此操作的方法。

如果数据在各个副本上的不同,请先同步数据,或者删除除一个副本之外的所有副本上的数据。

重命名现有的 MergeTree 表,然后使用旧名称创建一个 ReplicatedMergeTree 表。将数据从旧表移动到包含新表数据的目录(/var/lib/clickhouse/data/db_name/table_name/)中的 detached 子目录。然后在其中一个副本上运行 ALTER TABLE ATTACH PARTITION,将这些数据块添加到工作集中。

从 ReplicatedMergeTree 转换为 MergeTree

使用不同的名称创建一个 MergeTree 表。将包含 ReplicatedMergeTree 表数据的目录中的所有数据移动到新表的数据目录。然后删除 ReplicatedMergeTree 表并重新启动服务器。

如果您想删除 ReplicatedMergeTree 表而不启动服务器

  • 删除元数据目录(/var/lib/clickhouse/metadata/)中对应的 .sql 文件。
  • 删除 ClickHouse Keeper 中对应的路径(/path_to_table/replica_name)。

之后,您可以启动服务器,创建一个 MergeTree 表,将数据移动到其目录,然后重新启动服务器。

ClickHouse Keeper 集群中的元数据丢失或损坏时的恢复

如果 ClickHouse Keeper 中的数据丢失或损坏,则可以通过将其移动到未复制的表来保存数据,如上所述。

另请参见