ALTER TABLE 查询都会修改表设置或数据:
大多数
ALTER TABLE 查询仅适用于 *MergeTree、Merge 和 Distributed 表。ALTER 语句用于操作视图:
这些
ALTER 语句用于修改与基于角色的访问控制相关的实体:
在一条 ALTER 中组合多个操作
一条 ALTER TABLE 语句可以包含多个以逗号分隔的操作,因此原本需要分成多条语句依次提交的工作,可以合并为一条语句完成:
ALTER 完成时,具体由哪个设置控制这一等待取决于操作本身,而非语句:位于变更执行路径上的操作 (例如 MATERIALIZE INDEX) 由 mutations_sync 控制,而元数据操作则由 alter_sync 控制,因此即使将两类操作组合在同一语句中,它们也不会统一受某一个设置控制。有关每个设置分别涵盖哪些操作,请参阅 ALTER 查询的同步性;有关此类混合语句在 Replicated 数据库上受到的限制,请参阅 合并 MATERIALIZE INDEX 子句。
变更
用于修改表数据的ALTER 查询,是通过一种称为“变更”的机制实现的,最典型的就是 ALTER TABLE … DELETE 和 ALTER TABLE … UPDATE。它们是异步后台进程,类似于 MergeTree 表中的合并,用于生成新的“mutated”版本的 parts。
对于 *MergeTree 表,变更是通过重写整个数据分区片段来执行的。
它不具备原子性——变更后的 parts 一旦准备就绪,就会立即替换原来的 parts;而在变更执行期间开始的 SELECT 查询,既会看到已经变更过的 parts 中的数据,也会看到尚未变更的 parts 中的数据。
变更按照创建顺序完全有序,并按该顺序应用到每个 part。变更与 INSERT INTO 查询之间也存在部分顺序关系:在变更提交之前插入到表中的数据会被变更,而在此之后插入的数据则不会被变更。请注意,变更不会以任何方式阻塞插入。
变更查询在变更条目添加完成后会立即返回 (对于复制表,会添加到 ZooKeeper;对于非复制表,则会添加到文件系统) 。变更本身会使用系统 profile 设置异步执行。要跟踪变更的进度,可以使用 system.mutations 表。成功提交的变更即使在 ClickHouse 服务器重启后也会继续执行。变更一旦提交,就无法回滚;但如果由于某种原因卡住了,可以使用 KILL MUTATION 查询将其取消。
已完成变更的条目不会立即删除 (保留条目的数量由存储引擎参数 finished_mutations_to_keep 决定) 。较早的变更条目会被删除。
执行开销与完成
规划ALTER TABLE 时,应区分元数据变更与现有数据分区片段所需的处理工作。客户端是否等待操作完成取决于具体操作:alter_sync 控制元数据操作和 MODIFY COLUMN (包括会重写数据的类型转换) ;mutations_sync 控制后台变更,例如 UPDATE、DELETE 和 MATERIALIZE ...。请勿以固定时长作为运维参考:实际开销取决于元数据量、受影响的 parts 数量和字节数、转换开销以及当前后台负载。
ALTER 查询的同步性
默认情况下,针对非复制表的 ALTER 查询会等待其工作完成。当 alter_sync = 0 时,执行后台工作的 ALTER 可能会在该工作完成之前就返回。对于复制表,查询会将相应操作的指令添加到 ZooKeeper 中,而这些操作本身会尽快执行。查询可以等待,直到所有副本都完成这些操作。
对于通过变更执行路径创建变更的 ALTER 查询 (包括但不限于 UPDATE、DELETE、MATERIALIZE INDEX、MATERIALIZE PROJECTION、MATERIALIZE COLUMN、APPLY DELETED MASK、APPLY PATCHES、CLEAR STATISTIC 和 MATERIALIZE STATISTIC) ,其同步方式由 mutations_sync 设置决定。
对于其他 ALTER 查询 (包括 MODIFY COLUMN 重写) ,则由 alter_sync 控制等待行为。
你可以使用 replication_wait_for_inactive_replica_timeout 设置,指定等待非活动副本执行完所有 ALTER 查询的时长 (以秒为单位) 。
对于所有
ALTER 查询,如果 alter_sync = 2,且某些副本处于非活动状态的时间超过 replication_wait_for_inactive_replica_timeout 设置中指定的时长,则会抛出 UNFINISHED 异常。而使用 alter_sync = 3 时不会等待非活动副本,因此不会抛出异常。单张表上的并发 ALTER 分配
在复制表上,短时间内连续对同一张表提交多条独立的 ALTER 语句,可能会因 CANNOT_ASSIGN_ALTER (代码 517) 而失败。当副本尚未应用此前的某些 ALTER (元数据版本仍落后于共享元数据——服务器可能提示副本“still not applied some of previous alters”或“Probably too many alters executing concurrently”) 时,复制路径会引发此错误。即使较早的 ALTER 已被分配,这种情况仍可能持续存在。这是一种通用的并发元数据 ALTER / 变更情况,并不局限于仅产生变更的语句。常规的并发元数据修改 (ADD / DROP / MODIFY 等) 同样可能引发这一可重试错误代码 (例如,参见 tests/queries/0_stateless/03518_alter_logical_race.sh 覆盖的重试路径) 。
避免此竞争条件的方法:
- 在语法允许的情况下,将独立的元数据操作合并为单条包含多个子句的
ALTER(例如多个ADD INDEX子句) ,详见在一条ALTER中组合多个操作。 - 将
ALTER语句串行执行;遇到代码 517 时重试,直到副本应用了此前的ALTER。 - 对于会产生变更的
ALTER,请在提交下一条语句前,通过已文档化的可观测项 (如mutations_sync或system.mutations中的is_done) 等待此前的变更完成。
合并 MATERIALIZE INDEX 子句
一条 ALTER 语句中可以包含多个 MATERIALIZE INDEX 子句。代码库中已覆盖的场景是:在一条语句中,将多个 ADD INDEX 子句与针对同一批新索引的 MATERIALIZE INDEX 一并使用 (参见 tests/queries/0_stateless/02911_add_index_and_materialize_index.sql) 。这种组合的 ADD INDEX + MATERIALIZE INDEX 形式同时包含 AlterCommand 片段和 MutationCommand 片段,因此 DatabaseReplicated 会拒绝该语句,并返回 QUERY_IS_PROHIBITED (InterpreterAlterQuery::validateReplicatedDatabaseSegments) 。02911 示例适用于普通 (非 DatabaseReplicated) 数据库;对于 DatabaseReplicated,请将元数据变更和物化变更拆分为不同的语句。
在当前实现中,准备变更时,每个 MATERIALIZE INDEX 子句都会根据表元数据快照进行解析。因此,针对已有索引、仅包含物化操作的多子句形式也会遵循相同的准备路径 (仅包含变更操作,因此仍位于同一个片段内) 。这种确切形式尚未有专门的无状态测试覆盖;在具备此类测试覆盖前,应将其视为当前实现行为,而非单独保证的契约。
如果需要按顺序应用变更,仍可在每条语句中执行一个 MATERIALIZE INDEX,并通过 mutations_sync 等待。