目前 `ALTER` 语句仅支持 `*MergeTree` 表, `Merge` 和 `Distributed`。 此查询有一些变种版本。
列操作
更新表结构。
```sql
ALTER TABLE [db].name [ON CLUSTER cluster] ADD|DROP|MODIFY COLUMN ...
```
在查询中, 指定一个或多个逗号分隔动作的列表。
每一个动作都是在一个列上的操作。
如下的动作支持:
```sql
ADD COLUMN name [type] [default_expr] [AFTER name_after]
```
添加一个新的列到特定名称、类型和 `default_expr`的表中 (查询章节 "默认表达式")。 如果你指定了 `AFTER name_after` (另一个列的名称), 列被添加到指定字段之后。 否则, 列被添加到表的末尾。 注意目前没有方法可以添加列到表的开头。 一系列动作时候, 'name_after' 是一个列的名称, 此列被添加到之前的动作上。
添加一个新列即可改变列的结构, 在执行动作之前。 在ALTER之后, 此数据并没有出现在磁盘上。 当从表中读取数据时, 如果数据丢失, 它使用默认值填充 (如果有1,执行默认表达式, 或者使用0或空字符串)。 对于一个字段, 当从表中读取数据时, 如果数据为空, 它使用默认值填充 (如果有1,执行默认表达式, 或者使用0或空字符串)。 在合并数据块后, 字段出现在磁盘上,(请查看 MergeTree)。
此方法允许我们完成 ALTER 查询, 不需要增加旧数据的数据量。
```sql
DROP COLUMN name
```
删除带有名称'name'的列。
删除文件系统中的数据。 此语句删除整个文件, 查询立即完成。
```sql
MODIFY COLUMN name [type] [default_expr]
```
更新 'name' 字段的类型为 'type' 同时默认的表达式为 'default_expr'。 当更新类型时值被转换,如果'toType'函数被使用时。
如果仅有默认的表达式被改变, 此查询并不做任何复杂的操作, 并且查询立即完成。
更新字段类型是一个稍微复杂的动作 – 它更新带有数据的文件内容。 对于大表, 它可能需要比较长的时间。
有下面几个处理阶段:
- 准备带有更新数据的临时(新)文件。
- 重命名旧文件。
- 重命名临时(新)文件到旧名称。
- 删除旧文件。
仅有第一阶段花费时间。 如果在这个阶段出现故障, 数据将不会被更新。
如果在连续阶段的某个环节出现故障,数据得进行手工恢复。例如,如果旧的文件从文件系统删除,但是新文件中的数据还没有写入到磁盘中,数据会丢失。
目前不支持对数组和嵌套数据结构中的列类型进行更新。
`ALTER` 查询可以创建和删除嵌套数据结构中单独的元素 (列), 但是并不是整个数据结构。 为了添加一个嵌套数据结构,你需要添加带有名称如`name.nested_name` 的列和类型`Array(T)`。 一个嵌套数据结构等价于带有逗号之前有相同前缀的多个数组列。
目前不支持使用主键或样本键删除列 (列在 `ENGINE` 表达式中)。 对于包含在主键中的字段,更新类型仅可能发生在不导致数据更新的情况下 (例如, 添加相应的值到 Enum 类型中或者更新一个`DateTime`类型到 `UInt32`)。
如果 `ALTER` 查询不足以进行表更新操作,你能够创建一个新表,使用 `INSERT SELECT`语句拷贝数据到这个表,然后使用`RENAME`查询切换表和删除旧表。
`ALTER` 查询为表阻塞所有的读和写操作。换句话说,如果一个长的 `SELECT` 在 `ALTER` 查询时运行, `ALTER` 查询将等待它结束。与此同时,当`ALTER`正在运行时, 对于相同的表所有新的查询将等待。
对于本身不保存数据的表(例如 `Merge` 和 `Distributed`), `ALTER` 更改表结构,同时不更改下级表的结构。例如,当在 `Distributed` 表中运行ALTER, 你也需要在所有远程服务器的表上运行 `ALTER`。
对于更改列的 `ALTER` 查询是复制的。 此工具方法保存在ZooKeeper中, 然后每个复制节点执行他们。 所有的 `ALTER` 查询按照相同的顺序运行。 在其他复制节点,此查询等待合适的动作执行完成。 然而,在一个复制表中,更改列的查询能够被中断,同时所有的动作将异步执行。
## 操作表分区和数据分片
它仅在MergeTree的引擎上发挥作用. 具体操作如下所示:
DETACH PARTITION – 移动分区到 'detached' 目录.
DROP PARTITION – 删除一个分区.
ATTACH PART|PARTITION – 从卸载的目录中添加一个新的数据分片或分区到表中
FREEZE PARTITION – 创建一个分区的备份.
FETCH PARTITION – 从另一个服务器上下载一个分区.
查询的每个类型都单独覆盖.
表中的一个分区是一个单月份的数据. 通过表引擎指定的日期键的值来进行决定。 每个月的数据单独保存为了简化数据的操作.
表中的分片是单独分区的一部分数据, 通过主键来筛选数据.
你能够使用 system.parts 表来查看表分区或分片的具体信息:
SELECT * FROM system.parts WHERE active
active – 仅计数实际活跃的分片. 不活跃的分片, 例如, 保留的源数据分片在合并之后变成了更大的部分 – 这些分片大概在合并后10分钟内被删除。
查看数据分片和数据分区的另外一个方法是到数据目录中查看. 数据目录: /var/lib/clickhouse/data/database/table/, 其中 /var/lib/clickhouse/ 是到clickhouse数据的一个路径, 'database' 是数据库名称, 'table' 是表名. 例如:
$ ls -l /var/lib/clickhouse/data/test/visits/total48drwxrwxrwx2clickhouse clickhouse20480May502:58 20140317_20140323_2_2_0drwxrwxrwx2clickhouse clickhouse20480May502:58 20140317_20140323_4_4_0drwxrwxrwx2clickhouse clickhouse4096May502:55 detached-rw-rw-rw-1clickhouse clickhouse2May502:58 increment.txt
其中, 20140317_20140323_2_2_0 和 20140317_20140323_4_4_0 是数据分片的目录.
让我们看下第一个分片的名称 : 20140317_20140323_2_2_0.
20140317 是数据块中数据的最小日期
20140323 是数据块中数据的最大日期
2 是数据块的最小号码
2 是数据块的最大号码
0 是数据块的层级 (合并树的深度).
每个数据分片都和一个分区相互关联,包含了一个月的数据. 201403 是一个分区的名称. A 一个分区是一个单月数据的分片集合.
在生产环境中正在运行的服务器上,你不能手动更新文件系统的数据分片集合, 因为修改后服务器会找不到数据分片. 对于非同步表, 当服务器停机时,你可以做这个操作, 但是不建议这么操作. 对于同步表, 数据分片集合在任何情况下都不能修改.
卸载目录包含了服务器未使用的数据分片 - 使用 ALTER ... DETACH 语句来从表中卸载. 损坏的数据分片也 移动到此目录, 而不是删除它们. 你能够在任意时间添加、删除或修改'卸载'目录中的数据 – 当执行了 ALTER TABLE ... ATTACH 后 服务器才知道具体的数据分区信息.
ALTER TABLE [db.] table DETACH PARTITION 'name'
移动分区中所有的数据 到'卸载'目录. 分区名称通过 YYYYMM 格式来指定.
在查询执行后,你能够对'卸载'目录中的数据进行任意操作 — 从文件系统中删除它, 或者保留它作为归档.
查询是复制的 – 数据可将被移动到 '卸载' 目录,然后留在目录中. 查询仅能够发送给一个领导者(leader)副本. 为了找到哪个是领导者, 可执行 SELECT语句查询 'system.replicas' 系统表. 另外, 为了更方便查询所有的副本, 当发生错误时,所有副本都将抛出异常.
ALTER TABLE [db.] table DROP PARTITION 'name'
这个与 DETACH 操作相同. 从表中删除数据. 数据分片被标记为'不活跃' , 大约10分钟能够完全删除完毕. 查询是复制的 – 数据将在所有副本节点上删除.
ALTER TABLE [db.] table ATTACH PARTITION | PART 'name'
添加数据到'卸载'目录的表
为整个数据分区或单独的数据分片添加数据是需求的. 对于一个数据分片, 指定数据分区的全名.
查询是复制的. 每个副本检查是否 有数据在 '卸载' 目录. 如果有数据, 它将检查完整性, 验证数据是否匹配, 然后验证是否每个步骤是否都是正确的. 如果不正确, 它将从查询请求副本或者另外的副本中下载数据, 这些副本已经添加数据了.
因此,在副本中,你能够放数据到 '卸载' 目录, 同时使用 ALTER ... ATTACH 查询来添加数据到所有副本的表中.
ALTER TABLE [db.] table FREEZE PARTITION 'name'
从一个或多个分区中创建一个本地备份. 名称是分区的全名 (例如, 201403), 或者 它的前缀 (例如, 2014): 然后为所有对应的分区创建备份.
查询执行如下: 对于一个时间点的数据快照, 它创建硬链接到表数据,目录为 /var/lib/clickhouse/shadow/N/...
/var/lib/clickhouse/ 是一个配置中的工作目录. N 备份的增量数字.
相同的目录结构在备份的目录中被创建出来 /var/lib/clickhouse/. 同时也可以为所有文件执行 'chmod' 命令, 防止对文件进行写入.
备份可立即被创建(但是它需要等待当前的查询结束后执行)。首先,备份不占用任何磁盘空间。当系统运行时,数据有更新操作,备份将耗费磁盘空间;如果备份的是历史数据,那么将不耗费磁盘空间。
在创建备份之后, /var/lib/clickhouse/shadow/ 中的数据被拷贝到远程服务器上,然后在本地服务器上删除。整个备份过程执行不需要停服务器。
ALTER ... FREEZE PARTITION 查询是不同步的. 一个本地备份仅创建在本地服务器.
作为替代, 你可以手工拷贝数据从 /var/lib/clickhouse/data/database/table 目录. 但是,如果你在服务器运行时拷贝数据, 当文件正在添加或更改时,拷贝目录可能产生竞争条件, 可能导致备份不一致. 如果服务器没有运行,那么你可以做此操作 – 然后结果数据和 ALTER TABLE t FREEZE PARTITION 执行后的数据基本相同.
ALTER TABLE ... FREEZE PARTITION 仅拷贝数据, 不拷贝表的元数据. 为了备份表的元数据, 拷贝文件 /var/lib/clickhouse/metadata/database/table.sql.
为了从备份中恢复:
使用 CREATE 语句来创建表. 语句将从一个.sql 文件中加载 (用 CREATE 来替代 ATTACH).
从目录 data/database/table/ 拷贝数据到 /var/lib/clickhouse/data/database/table/detached/ 目录.
运行 ALTER TABLE ... ATTACH PARTITION YYYYMM 查询, 其中 YYYYMM 是月份.
在这种方式下, 在备份中的数据将被添加到表中. 从备份中恢复数据不需要停服务器.