Doris系列9-表结构变更
一. 表结构变更概述
用户可以通过 Schema Change 操作来修改已存在表的 Schema。目前 Doris 支持以下几种修改:
- 增加、删除列
- 修改列类型
- 调整列顺序
- 增加、修改 Bloom Filter
- 增加、删除 bitmap index
名词解释:
- Base Table:基表。每一个表被创建时,都对应一个基表。
- Rollup:基于基表或者其他 Rollup 创建出来的上卷表。
- Index:物化索引。Rollup 或 Base Table 都被称为物化索引。
- Transaction:事务。每一个导入任务都是一个事务,每个事务有一个唯一递增的
Transaction ID。
二. 原理介绍
执行 Schema Change 的基本过程,是通过原 Index 的数据,生成一份新 Schema 的 Index 的数据。其中主要需要进行两部分数据转换,一是已存在的历史数据的转换,二是在 Schema Change 执行过程中,新到达的导入数据的转换。
+----------+
| Load Job |
+----+-----+
|
| Load job generates both origin and new index data
|
| +------------------+ +---------------+
| | Origin Index | | Origin Index |
+------> New Incoming Data| | History Data |
| +------------------+ +------+--------+
| |
| | Convert history data
| |
| +------------------+ +------v--------+
| | New Index | | New Index |
+------> New Incoming Data| | History Data |
+------------------+ +---------------+
在开始转换历史数据之前,Doris 会获取一个最新的 Transaction ID。并等待这个 Transaction ID 之前的所有导入事务完成。这个 Transaction ID 成为分水岭。意思是,Doris 保证在分水岭之后的所有导入任务,都会同时为原 Index 和新 Index 生成数据。这样当历史数据转换完成后,可以保证新的 Index 中的数据是完整的。
三. 作业
3.1 创建作业
创建 Schema Change 的具体语法可以查看帮助 HELP ALTER TABLE 中 Schema Change 部分的说明。
Schema Change 的创建是一个异步过程,作业提交成功后,用户需要通过 SHOW ALTER TABLE COLUMN 命令来查看作业进度。
3.2 查看作业
SHOW ALTER TABLE COLUMN 可以查看当前正在执行或已经完成的 Schema Change 作业。当一次 Schema Change 作业涉及到多个 Index 时,该命令会显示多行,每行对应一个 Index。举例如下:
JobId: 20021
TableName: tbl1
CreateTime: 2019-08-05 23:03:13
FinishTime: 2019-08-05 23:03:42
IndexName: tbl1
IndexId: 20022
OriginIndexId: 20017
SchemaVersion: 2:792557838
TransactionId: 10023
State: FINISHED
Msg:
Progress: N/A
Timeout: 86400
-
JobId:每个 Schema Change 作业的唯一 ID。
-
TableName:Schema Change 对应的基表的表名。
-
CreateTime:作业创建时间。
-
FinishedTime:作业结束时间。如未结束,则显示 "N/A"。
-
IndexName: 本次修改所涉及的某一个 Index 的名称。
-
IndexId:新的 Index 的唯一 ID。
-
OriginIndexId:旧的 Index 的唯一 ID。
-
SchemaVersion:以 M:N 的格式展示。其中 M 表示本次 Schema Change 变更的版本,N 表示对应的 Hash 值。每次 Schema Change,版本都会递增。
-
TransactionId:转换历史数据的分水岭 transaction ID。
-
State:作业所在阶段。
1)PENDING:作业在队列中等待被调度。
2)WAITING_TXN:等待分水岭 transaction ID 之前的导入任务完成。
3)RUNNING:历史数据转换中。
4)FINISHED:作业成功。
5)CANCELLED:作业失败。 -
Msg:如果作业失败,这里会显示失败信息。
-
Progress:作业进度。只有在 RUNNING 状态才会显示进度。进度是以 M/N 的形式显示。其中 N 为 Schema Change 涉及的总副本数。M 为已完成历史数据转换的副本数。
-
Timeout:作业超时时间。单位秒。
3.3 取消作业
在作业状态不为 FINISHED 或 CANCELLED 的情况下,可以通过以下命令取消 Schema Change 作业:
CANCEL ALTER TABLE COLUMN FROM tbl_name;
四. 最佳实践
Schema Change 可以在一个作业中,对多个 Index 进行不同的修改。举例如下:
源 Schema:
+-----------+-------+------+------+------+---------+-------+
| IndexName | Field | Type | Null | Key | Default | Extra |
+-----------+-------+------+------+------+---------+-------+
| tbl1 | k1 | INT | No | true | N/A | |
| | k2 | INT | No | true | N/A | |
| | k3 | INT | No | true | N/A | |
| | | | | | | |
| rollup2 | k2 | INT | No | true | N/A | |
| | | | | | | |
| rollup1 | k1 | INT | No | true | N/A | |
| | k2 | INT | No | true | N/A | |
+-----------+-------+------+------+------+---------+-------+
可以通过以下命令给 rollup1 和 rollup2 都加入一列 k4,并且再给 rollup2 加入一列 k5:
ALTER TABLE tbl1
ADD COLUMN k4 INT default "1" to rollup1,
ADD COLUMN k4 INT default "1" to rollup2,
ADD COLUMN k5 INT default "1" to rollup2;
完成后,Schema 变为:
+-----------+-------+------+------+------+---------+-------+
| IndexName | Field | Type | Null | Key | Default | Extra |
+-----------+-------+------+------+------+---------+-------+
| tbl1 | k1 | INT | No | true | N/A | |
| | k2 | INT | No | true | N/A | |
| | k3 | INT | No | true | N/A | |
| | k4 | INT | No | true | 1 | |
| | k5 | INT | No | true | 1 | |
| | | | | | | |
| rollup2 | k2 | INT | No | true | N/A | |
| | k4 | INT | No | true | 1 | |
| | k5 | INT | No | true | 1 | |
| | | | | | | |
| rollup1 | k1 | INT | No | true | N/A | |
| | k2 | INT | No | true | N/A | |
| | k4 | INT | No | true | 1 | |
+-----------+-------+------+------+------+---------+-------+
可以看到,Base 表 tbl1 也自动加入了 k4, k5 列。即给任意 rollup 增加的列,都会自动加入到 Base 表中。
同时,不允许向 Rollup 中加入 Base 表已经存在的列。如果用户需要这样做,可以重新建立一个包含新增列的 Rollup,之后再删除原 Rollup。
五. 注意事项
-
一张表在同一时间只能有一个 Schema Change 作业在运行。
-
Schema Change 操作不阻塞导入和查询操作。
-
分区列和分桶列不能修改。
-
如果 Schema 中有 REPLACE 方式聚合的 value 列,则不允许删除 Key 列。
如果删除 Key 列,Doris 无法决定 REPLACE 列的取值。
Unique 数据模型表的所有非 Key 列都是 REPLACE 聚合方式。 -
在新增聚合类型为 SUM 或者 REPLACE 的 value 列时,该列的默认值对历史数据没有含义。
因为历史数据已经失去明细信息,所以默认值的取值并不能实际反映聚合后的取值。 -
当修改列类型时,除 Type 以外的字段都需要按原列上的信息补全。
如修改列 k1 INT SUM NULL DEFAULT "1" 类型为 BIGINT,则需执行命令如下:
ALTER TABLE tbl1 MODIFY COLUMN `k1` BIGINT SUM NULL DEFAULT "1";
注意,除新的列类型外,如聚合方式,Nullable 属性,以及默认值都要按照原信息补全。
- 不支持修改列名称、聚合类型、Nullable 属性、默认值以及列注释。
六. 常见问题
6.1 Schema Change 的执行速度
目前 Schema Change 执行速度按照最差效率估计约为 10MB/s。保守起见,用户可以根据这个速率来设置作业的超时时间。
6.2 提交作业报错 Table xxx is not stable. ...
Schema Change 只有在表数据完整且非均衡状态下才可以开始。如果表的某些数据分片副本不完整,或者某些副本正在进行均衡操作,则提交会被拒绝。
数据分片副本是否完整,可以通过以下命令查看:
ADMIN SHOW REPLICA STATUS FROM tbl WHERE STATUS != "OK";
如果有返回结果,则说明有副本有问题。通常系统会自动修复这些问题,用户也可以通过以下命令优先修复这个表:
ADMIN REPAIR TABLE tbl1;
用户可以通过以下命令查看是否有正在运行的均衡任务:
SHOW PROC "/cluster_balance/pending_tablets";
可以等待均衡任务完成,或者通过以下命令临时禁止均衡操作:
ADMIN SET FRONTEND CONFIG ("disable_balance" = "true");
七. 相关配置
FE配置:
alter_table_timeout_second:作业默认超时时间,86400 秒。
BE配置:
alter_tablet_worker_count:在 BE 端用于执行历史数据转换的线程数。默认为 3。如果希望加快 Schema Change 作业的速度,可以适当调大这个参数后重启 BE。但过多的转换线程可能会导致 IO 压力增加,影响其他操作。该线程和 Rollup 作业共用。
八. 表结构变更实例
8.1 新增列
语法:
向指定 index 的指定位置添加一列
语法:
ADD COLUMN column_name column_type [KEY | agg_type] [DEFAULT "default_value"]
[AFTER column_name|FIRST]
[TO rollup_index_name]
[PROPERTIES ("key"="value", ...)]
注意:
1) 聚合模型如果增加 value 列,需要指定 agg_type
2) 非聚合模型(如 DUPLICATE KEY)如果增加key列,需要指定KEY关键字
3) 不能在 rollup index 中增加 base index 中已经存在的列
如有需要,可以重新创建一个 rollup index)
实例:
mysql> select count(*) from ods_fact_sale_doris;
+-----------+
| count(*) |
+-----------+
| 767830000 |
+-----------+
1 row in set (11.86 sec)
mysql> alter table ods_fact_sale_doris add column cnt largeint sum default "0";
Query OK, 0 rows affected (0.02 sec)
mysql> SHOW ALTER TABLE COLUMN;
+-------+---------------------+---------------------+---------------------+---------------------+---------+---------------+---------------+---------------+----------+------+----------+---------+
| JobId | TableName | CreateTime | FinishTime | IndexName | IndexId | OriginIndexId | SchemaVersion | TransactionId | State | Msg | Progress | Timeout |
+-------+---------------------+---------------------+---------------------+---------------------+---------+---------------+---------------+---------------+----------+------+----------+---------+
| 13977 | ods_fact_sale_doris | 2021-12-14 09:26:26 | 2021-12-14 09:26:27 | ods_fact_sale_doris | 13978 | 13891 | 1:1492711822 | 2114 | FINISHED | | NULL | 86400 |
+-------+---------------------+---------------------+---------------------+---------------------+---------+---------------+---------------+---------------+----------+------+----------+---------+
1 row in set (0.00 sec)
mysql>
mysql> select * from ods_fact_sale_doris limit 10;
+------+-----------------------+-----------+-----------+------+
| id | sale_date | prod_name | sale_nums | cnt |
+------+-----------------------+-----------+-----------+------+
| 30 | 2010-11-26 00:00:00.0 | PROD7 | 63 | 0 |
| 46 | 2011-12-28 00:00:00.0 | PROD9 | 70 | 0 |
| 112 | 2012-07-26 00:00:00.0 | PROD10 | 57 | 0 |
| 114 | 2011-12-07 00:00:00.0 | PROD2 | 51 | 0 |
| 120 | 2011-11-11 00:00:00.0 | PROD7 | 60 | 0 |
| 123 | 2010-06-14 00:00:00.0 | PROD2 | 36 | 0 |
| 128 | 2010-10-04 00:00:00.0 | PROD9 | 47 | 0 |
| 139 | 2010-05-01 00:00:00.0 | PROD2 | 94 | 0 |
| 147 | 2012-09-27 00:00:00.0 | PROD4 | 68 | 0 |
| 148 | 2011-12-10 00:00:00.0 | PROD4 | 88 | 0 |
+------+-----------------------+-----------+-----------+------+
10 rows in set (0.10 sec)
从结果可以看出,1秒钟就给一个7亿的大表增加了字段,速度真的给力
8.2 修改列的顺序
语法:
修改指定 index 的列类型以及列位置
语法:
MODIFY COLUMN column_name column_type [KEY | agg_type] [NULL | NOT NULL] [DEFAULT "default_value"]
[AFTER column_name|FIRST]
[FROM rollup_index_name]
[PROPERTIES ("key"="value", ...)]
注意:
1) 聚合模型如果修改 value 列,需要指定 agg_type
2) 非聚合类型如果修改key列,需要指定KEY关键字
3) 只能修改列的类型,列的其他属性维持原样(即其他属性需在语句中按照原属性显式的写出,参见 example 8)
4) 分区列和分桶列不能做任何修改
5) 目前支持以下类型的转换(精度损失由用户保证)
TINYINT/SMALLINT/INT/BIGINT/LARGEINT/FLOAT/DOUBLE 类型向范围更大的数字类型转换
TINTINT/SMALLINT/INT/BIGINT/LARGEINT/FLOAT/DOUBLE/DECIMAL 转换成 VARCHAR
VARCHAR 支持修改最大长度
VARCHAR/CHAR 转换成 TINTINT/SMALLINT/INT/BIGINT/LARGEINT/FLOAT/DOUBLE
VARCHAR/CHAR 转换成 DATE (目前支持"%Y-%m-%d", "%y-%m-%d", "%Y%m%d", "%y%m%d", "%Y/%m/%d, "%y/%m/%d"六种格式化格式)
DATETIME 转换成 DATE(仅保留年-月-日信息, 例如: `2019-12-09 21:47:05` <--> `2019-12-09`)
DATE 转换成 DATETIME(时分秒自动补零, 例如: `2019-12-09` <--> `2019-12-09 00:00:00`)
FLOAT 转换成 DOUBLE
INT 转换成 DATE (如果INT类型数据不合法则转换失败,原始数据不变)
6) 不支持从NULL转为NOT NULL
实例:
mysql> desc ods_fact_sale_doris;
+-----------+--------------+------+-------+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-------+---------+-------+
| id | BIGINT | Yes | true | 0 | |
| sale_date | VARCHAR(100) | Yes | true | NULL | |
| prod_name | VARCHAR(32) | Yes | true | NULL | |
| sale_nums | BIGINT | Yes | false | NULL | SUM |
| cnt | LARGEINT | Yes | false | 0 | SUM |
+-----------+--------------+------+-------+---------+-------+
5 rows in set (0.00 sec)
mysql> alter table ods_fact_sale_doris MODIFY COLUMN prod_name varchar(32) KEY AFTER id ;
Query OK, 0 rows affected (0.01 sec)
mysql> show alter table column;
+-------+---------------------+---------------------+---------------------+---------------------+---------+---------------+---------------+---------------+----------+------+----------+---------+
| JobId | TableName | CreateTime | FinishTime | IndexName | IndexId | OriginIndexId | SchemaVersion | TransactionId | State | Msg | Progress | Timeout |
+-------+---------------------+---------------------+---------------------+---------------------+---------+---------------+---------------+---------------+----------+------+----------+---------+
| 13977 | ods_fact_sale_doris | 2021-12-14 09:26:26 | 2021-12-14 09:26:27 | ods_fact_sale_doris | 13978 | 13891 | 1:1492711822 | 2114 | FINISHED | | NULL | 86400 |
| 14019 | ods_fact_sale_doris | 2021-12-14 09:57:22 | 2021-12-14 10:15:58 | ods_fact_sale_doris | 14020 | 13978 | 2:2147194682 | 2115 | FINISHED | | NULL | 86400 |
+-------+---------------------+---------------------+---------------------+---------------------+---------+---------------+---------------+---------------+----------+------+----------+---------+
2 rows in set (0.01 sec)
修改表的顺序,7亿条数据,持续大概19分钟