数据复制
在 ClickHouse Cloud 中,复制是为您管理的。请创建表时不要添加参数。例如,在下面的文本中,您将替换
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/table_name',
'{replica}',
ver
)
为
ENGINE = ReplicatedMergeTree
复制仅支持 MergeTree 系列中的表
- ReplicatedMergeTree
- ReplicatedSummingMergeTree
- ReplicatedReplacingMergeTree
- ReplicatedAggregatingMergeTree
- ReplicatedCollapsingMergeTree
- ReplicatedVersionedCollapsingMergeTree
- ReplicatedGraphiteMergeTree
复制工作在单个表的级别,而不是整个服务器。一个服务器可以同时存储复制表和非复制表。
复制不依赖于分片。每个分片都有自己独立的复制。
INSERT
和 ALTER
查询的压缩数据会被复制(有关更多信息,请参阅 ALTER 的文档)。
CREATE
、DROP
、ATTACH
、DETACH
和 RENAME
查询在单个服务器上执行,并且不被复制
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_queries 和 fallback_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 行,则它是原子性的。
数据块会被去重。对于同一数据块的多次写入(大小相同的数据块,其中包含相同顺序的相同行),该块仅写入一次。这样做的原因是,在发生网络故障时,客户端应用程序不知道数据是否已写入数据库,因此可以简单地重复 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
是可选的,因为所有表都是复制的。
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 = ReplicatedReplacingMergeTree('/clickhouse/tables/{layer}-{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 部分。
示例
<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
然后重启服务器。启动时,服务器会删除这些标志并开始恢复。
完全数据丢失后的恢复
如果所有数据和元数据都从其中一台服务器上消失了,请按照以下步骤进行恢复
- 在服务器上安装 ClickHouse。在包含分片标识符和副本的配置文件中正确定义替换,如果您使用它们。
- 如果您有必须在服务器上手动复制的非复制表,请从副本复制其数据(在目录
/var/lib/clickhouse/data/db_name/table_name/
中)。 - 从副本复制位于
/var/lib/clickhouse/metadata/
中的表定义。如果分片或副本标识符在表定义中显式定义,请对其进行更正,使其与此副本对应。(或者,启动服务器并执行应位于/var/lib/clickhouse/metadata/
中的 .sql 文件中的所有ATTACH TABLE
查询。) - 要启动恢复,请创建 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
表中收集了大量数据,并且现在想要启用复制,则可能需要这样做。
ATTACH TABLE ... AS REPLICATED 语句允许将分离的 MergeTree
表附加为 ReplicatedMergeTree
。
如果表的data目录(对于 Atomic
数据库,为 /store/xxx/xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy/
)中设置了 convert_to_replicated
标志,则可以在服务器重启时自动转换 MergeTree
表。创建空的 convert_to_replicated
文件,表将在下次服务器重启时加载为复制表。
此查询可用于获取表的数据路径。如果表有多个数据路径,则必须使用第一个。
SELECT data_paths FROM system.tables WHERE table = 'table_name' AND database = 'database_name';
请注意,ReplicatedMergeTree 表将使用 default_replica_path
和 default_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
使用 ATTACH TABLE ... AS NOT REPLICATED 语句将分离的 ReplicatedMergeTree
表作为单个服务器上的 MergeTree
附加。
另一种方法涉及服务器重启。使用不同的名称创建 MergeTree 表。将所有数据从 ReplicatedMergeTree
表数据目录移动到新表的数据目录。然后删除 ReplicatedMergeTree
表并重启服务器。
如果您想在不启动服务器的情况下摆脱 ReplicatedMergeTree
表
- 删除元数据目录 (
/var/lib/clickhouse/metadata/
) 中对应的.sql
文件。 - 删除 ClickHouse Keeper (
/path_to_table/replica_name
) 中的对应路径。
在此之后,您可以启动服务器,创建 MergeTree
表,将数据移动到其目录,然后重启服务器。
当 ClickHouse Keeper 集群中的元数据丢失或损坏时的恢复
如果 ClickHouse Keeper 中的数据丢失或损坏,您可以按照上述方法将其移动到非复制表来保存数据。
另请参阅