From 9e486a0668d97be6b7e022c2f0a3072e14cb880e Mon Sep 17 00:00:00 2001 From: tangenta Date: Fri, 15 Apr 2022 19:45:49 +0800 Subject: [PATCH 01/27] docs: add a proposal doc for multi-schema change --- docs/design/2022-04-15-multi-schema-change.md | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 docs/design/2022-04-15-multi-schema-change.md diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md new file mode 100644 index 0000000000000..c52bd0c2d0085 --- /dev/null +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -0,0 +1,157 @@ +# Proposal: Support Multi-Schema Change + +- Author(s): [tangenta](https://github.com/tangenta) +- Tracking Issue: https://github.com/pingcap/tidb/issues/14766 + +## Abstract + +This proposes an implementation of Multi-Schema Change. + +## Background + +Multi-Schema Change refers to defining multiple schema changes in one SQL statement, including column and index `ADD` , `ALTER` , `DROP` and `CHANGE` , as well as table option changes. For example: + +```sql +CREATE TABLE t (a INT, c INT); +ALTER TABLE t ADD COLUMN b INT AUTO_INCREMENT PRIMARY KEY, + MODIFY COLUMN c CHAR(5), + ADD INDEX idx(a), + AUTO_INCREMENT = 1000; +``` + +Currently, TiDB only supports one schema change per SQL statement, and multi-schema changes in few cases. + +When users try to migrate data from MySQL-like databases, they have to spend extra effort to rewrite a multi-schema change DDL to several single-schema change DDL. For the users who rely on ORM frameworks like flyway to construct SQLs automatically, rewriting SQL is not even possible. + +Above all, Multi-Schema Change can be a blocking issue for POC procedure. + +## Features + +Multi-Schema Change is one of MySQL's extended features to the SQL standard. It has the following features: + +- Atomicity: Schema changes from the same SQL statement either all succeed or all fail. In the above example, adding column b, modifying column c, adding the index of column a, and modifying the auto-increment ID of the table must all succeed or fail. The intermediate states are invisible to users. + +- Cascading: Whether the previously generated object in the same statement can be referenced for the schema object currently being changed. + ```sql + CREATE TABLE t (a INT); + ALTER TABLE t ADD COLUMN b INT, ADD INDEX idx(b); + -- Query OK, 0 rows affected (0.07 sec) + ALTER TABLE t ADD COLUMN c INT, ADD COLUMN d INT as (c+1); + -- Query OK, 0 rows affected (0.07 sec) + ``` + Only adding columns and adding indexes can be cascaded, and other schema object changes cannot be cascaded. For example, modifying columns cannot be cascaded: + ```sql + ALTER TABLE t ADD COLUMN d INT, CHANGE COLUMN d e INT; + -- ERROR 1054 (42S22): Unknown column 'd' in 't' + ``` + +- Uniqueness: Multiple changes to the same schema object in the same SQL statement are not allowed. + ```sql + CREATE TABLE t (a INT, b INT); + ALTER TABLE t MODIFY COLUMN a CHAR(5), DROP COLUMN a; + -- ERROR 1054 (42S22): Unknown column 'a' in 't' + ``` + +## Proposal + +The implementation is based on the [online DDL architecture](https://github.com/pingcap/tidb/blob/e0c461a84cf4ad55c7b51c3f9db7f7b9ba51bb62/docs/design/2018-10-08-online-DDL.md). Similar to the DDL job, we introduce a new structure "sub-job", which represents a single schema change. As its name suggests, a job can contain zero or more sub-jobs. + +The Multi-Schema Change DDL jobs have the type `ActionMultiSchemaChange`. In current worker model, there is a dedicated code path (`onMultiSchemaChange()`) to run these jobs. Only Multi-Schema Change job can have sub-jobs. + +For example, the DDL + +```sql +ALTER TABLE t ADD COLUMN b INT, MODIFY COLUMN a CHAR(255); +``` + +can be modeled as a job like + +```go +job := &Job { + Type: ActionMultiSchemaChange, + MultiSchemaInfo: &MultiSchemaInfo { + SubJobs: []*SubJob { + &SubJob { + Type: ActionAddColumn, + Args: ... + }, + &SubJob { + Type: ActionModifyColumn, + Args: ... + }, + } + } +} +``` + +In this way, we pack multiple schema changes into one job. Just like the other jobs, it enters the DDL job queue stored in TiKV, and waits for a suitable worker to pick it. + +In the normal cases, the worker executes the sub-jobs one by one serially as if they are plain jobs. However, the thing gets complex in the abnormal cases. + +To ensure the atomicity of a Multi-Schema Change execution, we need to maintain the changing schema object states carefully. Take the above SQL as an example, if the 2nd sub-job `MODIFY COLUMN a CHAR(255)` failed for some reasons, the 1st sub-job should be able to revert its changes(rollback the added column `b`). + +This requirement means we cannot public the schema object when a sub-job is finished. Instead, the schema object should be **paused** in a "ready-to-public" state, which is still invisible to users. These schema object are finally public all at once when all the sub-jobs are confirmed to be success. This method is kind of like 2PC: the "commit" can only be started until all "prewrites" are done. + +Here is the table of states that should be paused under different schema changes. Note the "Next State" column shows the states that are visible to users. + +| DDL Type (Schema Change) | Pausing State | Next State | +|--------------------------|---------------|------------| +| Add Column | Write-Reorg | Public | +| Add Index | Write-Reorg | Public | +| Drop Column | Public | Write-Only | +| Drop Index | Public | Write-Only | +| Non-reorg Modify Column | Public | None | +| Reorg Modify Column | Write-Reorg | None | + +To achieve this behavior, we introduce a flag in sub-job named "non-revertible". This flag is set when a schema object has steped to a pausing state. When all sub-jobs are non-revertible, all the related schema objects step to the next state in one transaction. + +On the other hand, if there is any error thrown by any sub-job before all of them become non-revertible, the whole job is switched to a `rollingback` state. For the executed sub-jobs, we set them to `cancelling`; for the sub-job that have not been executed, we set them to `cancelled`. + +Finally we consider the extreme case: an error occurs while all the sub-jobs are non-revertible. In this situation, we tend to believe that the error can be solved by a trivial way like retrying. This behavior is compatible with the current DDL implementation. Take `DROP COLUMN` as an example, once the column steps to "Write-Only" state, there is no way to cancel this job. + +## Compatibility + +MySQL may reorder some schema changes: + +```sql +CREATE TABLE t (b INT, a INT, INDEX i(b)); +ALTER TABLE t DROP COLUMN b, RENAME COLUMN a TO b, ADD INDEX i(b), DROP INDEX i; -- success! + +SHOW CREATE TABLE t; +``` + +```console ++-------+-------------------------------------------------------------------------------------------------------------------------------+ +| Table | Create Table | ++-------+-------------------------------------------------------------------------------------------------------------------------------+ +| t | CREATE TABLE `t` ( + `b` int DEFAULT NULL, + KEY `i` (`b`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci | ++-------+-------------------------------------------------------------------------------------------------------------------------------+ +``` + +MySQL's execution order is as follows: + +1. `DROP INDEX i` +2. `DROP COLUMN b` +3. `RENAME a TO b` +4. `ADD INDEX i(b)` + + +Since this behavior is a bit counter-intuitive, we decided not to be fully compatible with MySQL. + +```sql +ALTER TABLE t DROP COLUMN b, RENAME COLUMN a TO b, ADD INDEX i(b), DROP INDEX i; +ERROR 1060 (42S21): Duplicate column name 'b' +``` + +TiDB validates the schema changes against a snapshot schema structure retrived before the execution, regardless of the previous changes in the same DDL SQL statement. The only exception is `ADD INDEX` following `ADD COLUMN`: + +```sql +ALTER TABLE t ADD COLUMN b INT, ADD INDEX idx(b); +``` + +## Alternative Proposals + +An alternative solution is to give up the atomicity, and uses multiple DDL jobs to complete the task. This is much less complex and requires little effort. However, it may lead to a visible intermediate state confusion. From facd66df113c73c8be389ea48af2a5d6439693cb Mon Sep 17 00:00:00 2001 From: tangenta Date: Fri, 15 Apr 2022 19:57:59 +0800 Subject: [PATCH 02/27] remove the cascading exception --- docs/design/2022-04-15-multi-schema-change.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index c52bd0c2d0085..e873dc6bb222f 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -146,11 +146,7 @@ ALTER TABLE t DROP COLUMN b, RENAME COLUMN a TO b, ADD INDEX i(b), DROP INDEX i; ERROR 1060 (42S21): Duplicate column name 'b' ``` -TiDB validates the schema changes against a snapshot schema structure retrived before the execution, regardless of the previous changes in the same DDL SQL statement. The only exception is `ADD INDEX` following `ADD COLUMN`: - -```sql -ALTER TABLE t ADD COLUMN b INT, ADD INDEX idx(b); -``` +TiDB validates the schema changes against a snapshot schema structure retrived before the execution, regardless of the previous changes in the same DDL SQL statement. ## Alternative Proposals From 95803cf7bee0c095344202b8094bd8889640062c Mon Sep 17 00:00:00 2001 From: tangenta Date: Mon, 18 Apr 2022 11:59:59 +0800 Subject: [PATCH 03/27] address comment --- docs/design/2022-04-15-multi-schema-change.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index e873dc6bb222f..981320ca08e6e 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -88,24 +88,24 @@ In this way, we pack multiple schema changes into one job. Just like the other j In the normal cases, the worker executes the sub-jobs one by one serially as if they are plain jobs. However, the thing gets complex in the abnormal cases. -To ensure the atomicity of a Multi-Schema Change execution, we need to maintain the changing schema object states carefully. Take the above SQL as an example, if the 2nd sub-job `MODIFY COLUMN a CHAR(255)` failed for some reasons, the 1st sub-job should be able to revert its changes(rollback the added column `b`). +To ensure the atomicity of a Multi-Schema Change execution, we need to maintain the changing schema object states carefully. Take the above SQL as an example, if the second sub-job `MODIFY COLUMN a CHAR(255)` failed for some reasons, the first sub-job should be able to rollback its changes(rollback the added column `b`). -This requirement means we cannot public the schema object when a sub-job is finished. Instead, the schema object should be **paused** in a "ready-to-public" state, which is still invisible to users. These schema object are finally public all at once when all the sub-jobs are confirmed to be success. This method is kind of like 2PC: the "commit" can only be started until all "prewrites" are done. +This requirement means we cannot simply public the schema object when a sub-job is finished. Instead, it should stay in a state which is still unnoticeable to users, and wait for the other sub-jobs, finally public all at once when all the sub-jobs are confirmed to be success. This method is kind of like 2PC: the "commit" can only be started when "prewrites" are complete. -Here is the table of states that should be paused under different schema changes. Note the "Next State" column shows the states that are visible to users. +Here is the table of states that should stay for different DDL. Note the "Next State" column means the changes are noticeable to the users. -| DDL Type (Schema Change) | Pausing State | Next State | -|--------------------------|---------------|------------| -| Add Column | Write-Reorg | Public | -| Add Index | Write-Reorg | Public | -| Drop Column | Public | Write-Only | -| Drop Index | Public | Write-Only | -| Non-reorg Modify Column | Public | None | -| Reorg Modify Column | Write-Reorg | None | +| DDL (Schema Change) | Unnoticeable State | Next State | +|-------------------------|--------------------|------------| +| Add Column | Write-Reorg | Public | +| Add Index | Write-Reorg | Public | +| Drop Column | Public | Write-Only | +| Drop Index | Public | Write-Only | +| Non-reorg Modify Column | None | Public | +| Reorg Modify Column | Write-Reorg | Public | -To achieve this behavior, we introduce a flag in sub-job named "non-revertible". This flag is set when a schema object has steped to a pausing state. When all sub-jobs are non-revertible, all the related schema objects step to the next state in one transaction. +To achieve this behavior, we introduce a flag in sub-job named "non-revertible". This flag is set when a schema object has steped to the last unnoticeable state. When all sub-jobs become non-revertible, all the related schema objects step to the next state in one transaction. After that, the sub-jobs are executed serially to do the rest. -On the other hand, if there is any error thrown by any sub-job before all of them become non-revertible, the whole job is switched to a `rollingback` state. For the executed sub-jobs, we set them to `cancelling`; for the sub-job that have not been executed, we set them to `cancelled`. +On the other hand, if there is any error returned by any sub-job before all of them become non-revertible, the whole job is switched to a `rollingback` state. For the executed sub-jobs, we set them to `cancelling`; for the sub-job that have not been executed, we set them to `cancelled`. Finally we consider the extreme case: an error occurs while all the sub-jobs are non-revertible. In this situation, we tend to believe that the error can be solved by a trivial way like retrying. This behavior is compatible with the current DDL implementation. Take `DROP COLUMN` as an example, once the column steps to "Write-Only" state, there is no way to cancel this job. From e2ae63fe04f50f312746a827dc3d0a50bb203ea6 Mon Sep 17 00:00:00 2001 From: tangenta Date: Mon, 18 Apr 2022 12:10:13 +0800 Subject: [PATCH 04/27] polish the words with grammarly --- docs/design/2022-04-15-multi-schema-change.md | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 981320ca08e6e..a7d23857835ca 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -9,7 +9,7 @@ This proposes an implementation of Multi-Schema Change. ## Background -Multi-Schema Change refers to defining multiple schema changes in one SQL statement, including column and index `ADD` , `ALTER` , `DROP` and `CHANGE` , as well as table option changes. For example: +Multi-Schema Change refers to defining multiple schema changes in one SQL statement, including column and index `ADD`, `ALTER`, `DROP`, and `CHANGE`, as well as table option changes. For example: ```sql CREATE TABLE t (a INT, c INT); @@ -19,11 +19,11 @@ ALTER TABLE t ADD COLUMN b INT AUTO_INCREMENT PRIMARY KEY, AUTO_INCREMENT = 1000; ``` -Currently, TiDB only supports one schema change per SQL statement, and multi-schema changes in few cases. +Currently, TiDB only supports one schema change per SQL statement and multi-schema changes in rare cases. When users try to migrate data from MySQL-like databases, they have to spend extra effort to rewrite a multi-schema change DDL to several single-schema change DDL. For the users who rely on ORM frameworks like flyway to construct SQLs automatically, rewriting SQL is not even possible. -Above all, Multi-Schema Change can be a blocking issue for POC procedure. +Above all, Multi-Schema Change can be a blocking issue for the POC procedure. ## Features @@ -40,7 +40,7 @@ Multi-Schema Change is one of MySQL's extended features to the SQL standard. It -- Query OK, 0 rows affected (0.07 sec) ``` Only adding columns and adding indexes can be cascaded, and other schema object changes cannot be cascaded. For example, modifying columns cannot be cascaded: - ```sql + ```SQL ALTER TABLE t ADD COLUMN d INT, CHANGE COLUMN d e INT; -- ERROR 1054 (42S22): Unknown column 'd' in 't' ``` @@ -56,11 +56,11 @@ Multi-Schema Change is one of MySQL's extended features to the SQL standard. It The implementation is based on the [online DDL architecture](https://github.com/pingcap/tidb/blob/e0c461a84cf4ad55c7b51c3f9db7f7b9ba51bb62/docs/design/2018-10-08-online-DDL.md). Similar to the DDL job, we introduce a new structure "sub-job", which represents a single schema change. As its name suggests, a job can contain zero or more sub-jobs. -The Multi-Schema Change DDL jobs have the type `ActionMultiSchemaChange`. In current worker model, there is a dedicated code path (`onMultiSchemaChange()`) to run these jobs. Only Multi-Schema Change job can have sub-jobs. +The Multi-Schema Change DDL jobs have the type `ActionMultiSchemaChange`. In the current worker model, there is a dedicated code path (`onMultiSchemaChange()`) to run these jobs. Only Multi-Schema Change jobs can have sub-jobs. For example, the DDL -```sql +```SQL ALTER TABLE t ADD COLUMN b INT, MODIFY COLUMN a CHAR(255); ``` @@ -84,15 +84,15 @@ job := &Job { } ``` -In this way, we pack multiple schema changes into one job. Just like the other jobs, it enters the DDL job queue stored in TiKV, and waits for a suitable worker to pick it. +In this way, we pack multiple schema changes into one job. Just like the other jobs, it enters the DDL job queue stored in TiKV and waits for a suitable worker to pick it up. -In the normal cases, the worker executes the sub-jobs one by one serially as if they are plain jobs. However, the thing gets complex in the abnormal cases. +In normal cases, the worker executes the sub-jobs one by one serially as if they are plain jobs. However, the thing gets complex in abnormal cases. -To ensure the atomicity of a Multi-Schema Change execution, we need to maintain the changing schema object states carefully. Take the above SQL as an example, if the second sub-job `MODIFY COLUMN a CHAR(255)` failed for some reasons, the first sub-job should be able to rollback its changes(rollback the added column `b`). +To ensure the atomicity of a Multi-Schema Change execution, we need to maintain the changing schema object states carefully. Take the above SQL as an example, if the second sub-job `MODIFY COLUMN a CHAR(255)` failed for some reason, the first sub-job should be able to rollback its changes(rollback the added column `b`). -This requirement means we cannot simply public the schema object when a sub-job is finished. Instead, it should stay in a state which is still unnoticeable to users, and wait for the other sub-jobs, finally public all at once when all the sub-jobs are confirmed to be success. This method is kind of like 2PC: the "commit" can only be started when "prewrites" are complete. +This requirement means we cannot simply public the schema object when a sub-job is finished. Instead, it should stay in a state which is still unnoticeable to users, and wait for the other sub-jobs, finally public all at once when all the sub-jobs are confirmed to be successful. This method is kind of like 2PC: the "commit" can only be started when "prewrites" are complete. -Here is the table of states that should stay for different DDL. Note the "Next State" column means the changes are noticeable to the users. +Here is the table of states that should stay for different DDLs. Note the "Next State" means the changes are noticeable to the users. | DDL (Schema Change) | Unnoticeable State | Next State | |-------------------------|--------------------|------------| @@ -103,17 +103,17 @@ Here is the table of states that should stay for different DDL. Note the "Next S | Non-reorg Modify Column | None | Public | | Reorg Modify Column | Write-Reorg | Public | -To achieve this behavior, we introduce a flag in sub-job named "non-revertible". This flag is set when a schema object has steped to the last unnoticeable state. When all sub-jobs become non-revertible, all the related schema objects step to the next state in one transaction. After that, the sub-jobs are executed serially to do the rest. +To achieve this behavior, we introduce a flag in the sub-job named "non-revertible". This flag is set when a schema object has stepped to the last unnoticeable state. When all sub-jobs become non-revertible, all the related schema objects step to the next state in one transaction. After that, the sub-jobs are executed serially to do the rest. -On the other hand, if there is any error returned by any sub-job before all of them become non-revertible, the whole job is switched to a `rollingback` state. For the executed sub-jobs, we set them to `cancelling`; for the sub-job that have not been executed, we set them to `cancelled`. +On the other hand, if there is an error returned by any sub-job before all of them become non-revertible, the whole job is switched to a `rollingback` state. For the executed sub-jobs, we set them to `cancelling`; for the sub-job that have not been executed, we set them to `cancelled`. -Finally we consider the extreme case: an error occurs while all the sub-jobs are non-revertible. In this situation, we tend to believe that the error can be solved by a trivial way like retrying. This behavior is compatible with the current DDL implementation. Take `DROP COLUMN` as an example, once the column steps to "Write-Only" state, there is no way to cancel this job. +Finally, we consider the extreme case: an error occurs while all the sub-jobs are non-revertible. In this situation, we tend to believe that the error can be solved in a trivial way like retrying. This behavior is compatible with the current DDL implementation. Take `DROP COLUMN` as an example, once the column steps to the "Write-Only" state, there is no way to cancel this job. ## Compatibility MySQL may reorder some schema changes: -```sql +```SQL CREATE TABLE t (b INT, a INT, INDEX i(b)); ALTER TABLE t DROP COLUMN b, RENAME COLUMN a TO b, ADD INDEX i(b), DROP INDEX i; -- success! @@ -141,13 +141,13 @@ MySQL's execution order is as follows: Since this behavior is a bit counter-intuitive, we decided not to be fully compatible with MySQL. -```sql +```SQL ALTER TABLE t DROP COLUMN b, RENAME COLUMN a TO b, ADD INDEX i(b), DROP INDEX i; ERROR 1060 (42S21): Duplicate column name 'b' ``` -TiDB validates the schema changes against a snapshot schema structure retrived before the execution, regardless of the previous changes in the same DDL SQL statement. +TiDB validates the schema changes against a snapshot schema structure retrieved before the execution, regardless of the previous changes in the same DDL SQL statement. ## Alternative Proposals -An alternative solution is to give up the atomicity, and uses multiple DDL jobs to complete the task. This is much less complex and requires little effort. However, it may lead to a visible intermediate state confusion. +An alternative solution is to give up the atomicity and uses multiple DDL jobs to complete the task. This is much less complex and requires little effort. However, it may lead to a visible intermediate state confusion. From a1f67df0870899ea108d04607e0d4c15251680ba Mon Sep 17 00:00:00 2001 From: tangenta Date: Mon, 18 Apr 2022 12:16:53 +0800 Subject: [PATCH 05/27] address comments --- docs/design/2022-04-15-multi-schema-change.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index a7d23857835ca..89270275c21c1 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -21,15 +21,15 @@ ALTER TABLE t ADD COLUMN b INT AUTO_INCREMENT PRIMARY KEY, Currently, TiDB only supports one schema change per SQL statement and multi-schema changes in rare cases. -When users try to migrate data from MySQL-like databases, they have to spend extra effort to rewrite a multi-schema change DDL to several single-schema change DDL. For the users who rely on ORM frameworks like flyway to construct SQLs automatically, rewriting SQL is not even possible. +When users try to migrate data from MySQL-like databases, they have to spend extra effort to rewrite a multi-schema change DDL to several single-schema change DDLs. For the users who rely on ORM frameworks like flyway to construct SQLs automatically, rewriting SQL is not even possible. -Above all, Multi-Schema Change can be a blocking issue for the POC procedure. +Above all, Multi-Schema Change can be a blocking issue for those who want to use TiDB. ## Features Multi-Schema Change is one of MySQL's extended features to the SQL standard. It has the following features: -- Atomicity: Schema changes from the same SQL statement either all succeed or all fail. In the above example, adding column b, modifying column c, adding the index of column a, and modifying the auto-increment ID of the table must all succeed or fail. The intermediate states are invisible to users. +- Atomicity: Schema changes from the same SQL statement either all succeed or fail. In the above example, adding column b, modifying column c, adding the index of column a, and modifying the auto-increment ID of the table must all succeed or fail. The intermediate states are invisible to users. - Cascading: Whether the previously generated object in the same statement can be referenced for the schema object currently being changed. ```sql @@ -84,7 +84,7 @@ job := &Job { } ``` -In this way, we pack multiple schema changes into one job. Just like the other jobs, it enters the DDL job queue stored in TiKV and waits for a suitable worker to pick it up. +In this way, we pack multiple schema changes into one job. Just like any other jobs, it enters the DDL job queue stored in TiKV and waits for a suitable worker to pick it up. In normal cases, the worker executes the sub-jobs one by one serially as if they are plain jobs. However, the thing gets complex in abnormal cases. From 561d8dcc2d9bb77e0643347de1a2f4983202f2cf Mon Sep 17 00:00:00 2001 From: tangenta Date: Mon, 18 Apr 2022 14:32:50 +0800 Subject: [PATCH 06/27] remove the feature part --- docs/design/2022-04-15-multi-schema-change.md | 29 +------------------ 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 89270275c21c1..b271e3279aa2c 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -9,7 +9,7 @@ This proposes an implementation of Multi-Schema Change. ## Background -Multi-Schema Change refers to defining multiple schema changes in one SQL statement, including column and index `ADD`, `ALTER`, `DROP`, and `CHANGE`, as well as table option changes. For example: +Multi-Schema Change is one of MySQL's extended features to the SQL standard. It allows the users to atomically make multiple schema changes in one SQL statement, including column and index `ADD`, `ALTER`, `DROP`, and `CHANGE`, as well as table option changes. For example: ```sql CREATE TABLE t (a INT, c INT); @@ -25,33 +25,6 @@ When users try to migrate data from MySQL-like databases, they have to spend ext Above all, Multi-Schema Change can be a blocking issue for those who want to use TiDB. -## Features - -Multi-Schema Change is one of MySQL's extended features to the SQL standard. It has the following features: - -- Atomicity: Schema changes from the same SQL statement either all succeed or fail. In the above example, adding column b, modifying column c, adding the index of column a, and modifying the auto-increment ID of the table must all succeed or fail. The intermediate states are invisible to users. - -- Cascading: Whether the previously generated object in the same statement can be referenced for the schema object currently being changed. - ```sql - CREATE TABLE t (a INT); - ALTER TABLE t ADD COLUMN b INT, ADD INDEX idx(b); - -- Query OK, 0 rows affected (0.07 sec) - ALTER TABLE t ADD COLUMN c INT, ADD COLUMN d INT as (c+1); - -- Query OK, 0 rows affected (0.07 sec) - ``` - Only adding columns and adding indexes can be cascaded, and other schema object changes cannot be cascaded. For example, modifying columns cannot be cascaded: - ```SQL - ALTER TABLE t ADD COLUMN d INT, CHANGE COLUMN d e INT; - -- ERROR 1054 (42S22): Unknown column 'd' in 't' - ``` - -- Uniqueness: Multiple changes to the same schema object in the same SQL statement are not allowed. - ```sql - CREATE TABLE t (a INT, b INT); - ALTER TABLE t MODIFY COLUMN a CHAR(5), DROP COLUMN a; - -- ERROR 1054 (42S22): Unknown column 'a' in 't' - ``` - ## Proposal The implementation is based on the [online DDL architecture](https://github.com/pingcap/tidb/blob/e0c461a84cf4ad55c7b51c3f9db7f7b9ba51bb62/docs/design/2018-10-08-online-DDL.md). Similar to the DDL job, we introduce a new structure "sub-job", which represents a single schema change. As its name suggests, a job can contain zero or more sub-jobs. From 7f68a9fa0d189dd2fd99f65923d4515506bdfae5 Mon Sep 17 00:00:00 2001 From: tangenta Date: Mon, 18 Apr 2022 16:43:28 +0800 Subject: [PATCH 07/27] correct non-reorg modify column states --- docs/design/2022-04-15-multi-schema-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index b271e3279aa2c..706338fcdcd3e 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -73,7 +73,7 @@ Here is the table of states that should stay for different DDLs. Note the "Next | Add Index | Write-Reorg | Public | | Drop Column | Public | Write-Only | | Drop Index | Public | Write-Only | -| Non-reorg Modify Column | None | Public | +| Non-reorg Modify Column | Public | Public | | Reorg Modify Column | Write-Reorg | Public | To achieve this behavior, we introduce a flag in the sub-job named "non-revertible". This flag is set when a schema object has stepped to the last unnoticeable state. When all sub-jobs become non-revertible, all the related schema objects step to the next state in one transaction. After that, the sub-jobs are executed serially to do the rest. From a2ee2c2ab93e9c806f9b30076f466b5b703faa43 Mon Sep 17 00:00:00 2001 From: tangenta Date: Fri, 22 Apr 2022 14:49:05 +0800 Subject: [PATCH 08/27] add upgrade compatibility --- docs/design/2022-04-15-multi-schema-change.md | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 706338fcdcd3e..0dfeb9ac57626 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -65,18 +65,18 @@ To ensure the atomicity of a Multi-Schema Change execution, we need to maintain This requirement means we cannot simply public the schema object when a sub-job is finished. Instead, it should stay in a state which is still unnoticeable to users, and wait for the other sub-jobs, finally public all at once when all the sub-jobs are confirmed to be successful. This method is kind of like 2PC: the "commit" can only be started when "prewrites" are complete. -Here is the table of states that should stay for different DDLs. Note the "Next State" means the changes are noticeable to the users. +Here is the table of schema states that may occur in different DDLs. Note the "Revertible States" means the changes are unnoticeable to the users. -| DDL (Schema Change) | Unnoticeable State | Next State | -|-------------------------|--------------------|------------| -| Add Column | Write-Reorg | Public | -| Add Index | Write-Reorg | Public | -| Drop Column | Public | Write-Only | -| Drop Index | Public | Write-Only | -| Non-reorg Modify Column | Public | Public | -| Reorg Modify Column | Write-Reorg | Public | +| DDL (Schema Change) | Revertible States | Non-revertible States | +|-------------------------|--------------------------------------------|---------------------------------------------| +| Add Column | None, Delete-Only, Write-Only, Write-Reorg | Public | +| Add Index | None, Delete-Only, Write-Only, Write-Reorg | Public | +| Drop Column | Public | Write-Only, Delete-Only, Delete-Reorg, None | +| Drop Index | Public | Write-Only, Delete-Only, Delete-Reorg, None | +| Non-reorg Modify Column | Public (before meta change) | Public (after meta change) | +| Reorg Modify Column | None, Delete-Only, Write-Only, Write-Reorg | Public | -To achieve this behavior, we introduce a flag in the sub-job named "non-revertible". This flag is set when a schema object has stepped to the last unnoticeable state. When all sub-jobs become non-revertible, all the related schema objects step to the next state in one transaction. After that, the sub-jobs are executed serially to do the rest. +To achieve this behavior, we introduce a flag in the sub-job named "non-revertible". This flag is set when a schema object has stepped to the last revertible state. When all sub-jobs become non-revertible, all the related schema objects step to the next state in one transaction. After that, the sub-jobs are executed serially to do the rest. On the other hand, if there is an error returned by any sub-job before all of them become non-revertible, the whole job is switched to a `rollingback` state. For the executed sub-jobs, we set them to `cancelling`; for the sub-job that have not been executed, we set them to `cancelled`. @@ -84,6 +84,17 @@ Finally, we consider the extreme case: an error occurs while all the sub-jobs ar ## Compatibility +### Upgrade Compatibility + +When a TiDB cluster performs a rolling upgrade, there are 2 cases: + +1. DDL owner is the new version of TiDB. When users execute a Multi-Schema Change statement on an old version of TiDB, they receive an "Unsupported Multi-Schema Change" error message. +2. DDL owner is the old version of TiDB. When users execute a Multi-Schema Change statement on a new version of TiDB, they receive the "invalid ddl job type" error message. + +In both cases, the Multi-Schema Change statement cannot be executed. Therefore, users should avoid executing Multi-Schema Change during a rolling upgrade. + +### MySQL Compatibility + MySQL may reorder some schema changes: ```SQL From 5e30b5025cc3b3dcd17bf3584766dde38f774021 Mon Sep 17 00:00:00 2001 From: tangenta Date: Thu, 5 May 2022 15:32:38 +0800 Subject: [PATCH 09/27] Update docs/design/2022-04-15-multi-schema-change.md Co-authored-by: bb7133 --- docs/design/2022-04-15-multi-schema-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 0dfeb9ac57626..13f136b21b23a 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -5,7 +5,7 @@ ## Abstract -This proposes an implementation of Multi-Schema Change. +This proposes an implementation of applying multiple schema changes in one `ALTER TABLE` SQL statement. ## Background From 35b4a7081fb0184663309e45188734420a8de039 Mon Sep 17 00:00:00 2001 From: tangenta Date: Thu, 5 May 2022 15:32:58 +0800 Subject: [PATCH 10/27] Update docs/design/2022-04-15-multi-schema-change.md Co-authored-by: bb7133 --- docs/design/2022-04-15-multi-schema-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 13f136b21b23a..76aa8646357a4 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -19,7 +19,7 @@ ALTER TABLE t ADD COLUMN b INT AUTO_INCREMENT PRIMARY KEY, AUTO_INCREMENT = 1000; ``` -Currently, TiDB only supports one schema change per SQL statement and multi-schema changes in rare cases. +Currently, TiDB only supports one schema change per SQL statement and limited multi-schema changes for some rare cases. When users try to migrate data from MySQL-like databases, they have to spend extra effort to rewrite a multi-schema change DDL to several single-schema change DDLs. For the users who rely on ORM frameworks like flyway to construct SQLs automatically, rewriting SQL is not even possible. From 120133fa9f2fb8d58071ed98255dbb1a9f2bed6e Mon Sep 17 00:00:00 2001 From: tangenta Date: Thu, 5 May 2022 15:33:29 +0800 Subject: [PATCH 11/27] Update docs/design/2022-04-15-multi-schema-change.md Co-authored-by: bb7133 --- docs/design/2022-04-15-multi-schema-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 76aa8646357a4..287b9a4c355db 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -21,7 +21,7 @@ ALTER TABLE t ADD COLUMN b INT AUTO_INCREMENT PRIMARY KEY, Currently, TiDB only supports one schema change per SQL statement and limited multi-schema changes for some rare cases. -When users try to migrate data from MySQL-like databases, they have to spend extra effort to rewrite a multi-schema change DDL to several single-schema change DDLs. For the users who rely on ORM frameworks like flyway to construct SQLs automatically, rewriting SQL is not even possible. +When users try to migrate data from MySQL-like databases, they have to spend extra effort to rewrite a multi-schema change DDL to several single-schema change DDLs. For the users who rely on ORM frameworks like [Flyway](https://flywaydb.org/) to construct SQLs automatically, rewriting SQL could be tedious and the scripts are hard to maintain. Above all, Multi-Schema Change can be a blocking issue for those who want to use TiDB. From 33fdb87ef9d5327633c8a29563665f6be23c9f3c Mon Sep 17 00:00:00 2001 From: tangenta Date: Thu, 5 May 2022 15:33:42 +0800 Subject: [PATCH 12/27] Update docs/design/2022-04-15-multi-schema-change.md Co-authored-by: bb7133 --- docs/design/2022-04-15-multi-schema-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 287b9a4c355db..ffde68cf4c808 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -23,7 +23,7 @@ Currently, TiDB only supports one schema change per SQL statement and limited mu When users try to migrate data from MySQL-like databases, they have to spend extra effort to rewrite a multi-schema change DDL to several single-schema change DDLs. For the users who rely on ORM frameworks like [Flyway](https://flywaydb.org/) to construct SQLs automatically, rewriting SQL could be tedious and the scripts are hard to maintain. -Above all, Multi-Schema Change can be a blocking issue for those who want to use TiDB. +Above all, the lack of this capability can be a blocking issue for those who want to use TiDB. ## Proposal From 1181041b2d65225bdf74deba3634fa88adafcd6c Mon Sep 17 00:00:00 2001 From: tangenta Date: Thu, 5 May 2022 15:34:07 +0800 Subject: [PATCH 13/27] Update docs/design/2022-04-15-multi-schema-change.md Co-authored-by: bb7133 --- docs/design/2022-04-15-multi-schema-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index ffde68cf4c808..84506f42bd090 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -27,7 +27,7 @@ Above all, the lack of this capability can be a blocking issue for those who wan ## Proposal -The implementation is based on the [online DDL architecture](https://github.com/pingcap/tidb/blob/e0c461a84cf4ad55c7b51c3f9db7f7b9ba51bb62/docs/design/2018-10-08-online-DDL.md). Similar to the DDL job, we introduce a new structure "sub-job", which represents a single schema change. As its name suggests, a job can contain zero or more sub-jobs. +The implementation is based on the [online DDL architecture](https://github.com/pingcap/tidb/blob/e0c461a84cf4ad55c7b51c3f9db7f7b9ba51bb62/docs/design/2018-10-08-online-DDL.md). Similar to the existing [Job](https://github.com/pingcap/tidb/blob/6bd54bea8a9ec25c8d65fcf1157c5ee7a141ab0b/parser/model/ddl.go/#L262) structure, we introduce a new structure "SubJob", which represents one single schema change. As its name suggests, a job can contain zero or more sub-jobs. The Multi-Schema Change DDL jobs have the type `ActionMultiSchemaChange`. In the current worker model, there is a dedicated code path (`onMultiSchemaChange()`) to run these jobs. Only Multi-Schema Change jobs can have sub-jobs. From 676a3bb1e6b573e2aad43038764ac990e6baf238 Mon Sep 17 00:00:00 2001 From: tangenta Date: Thu, 5 May 2022 15:34:22 +0800 Subject: [PATCH 14/27] Update docs/design/2022-04-15-multi-schema-change.md Co-authored-by: bb7133 --- docs/design/2022-04-15-multi-schema-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 84506f42bd090..ed03626e57c0a 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -31,7 +31,7 @@ The implementation is based on the [online DDL architecture](https://github.com/ The Multi-Schema Change DDL jobs have the type `ActionMultiSchemaChange`. In the current worker model, there is a dedicated code path (`onMultiSchemaChange()`) to run these jobs. Only Multi-Schema Change jobs can have sub-jobs. -For example, the DDL +For example, the DDL statement ```SQL ALTER TABLE t ADD COLUMN b INT, MODIFY COLUMN a CHAR(255); From 1187118d1c3ca1c18a20d28d1ab5df606786b3c3 Mon Sep 17 00:00:00 2001 From: tangenta Date: Thu, 5 May 2022 15:35:24 +0800 Subject: [PATCH 15/27] Update docs/design/2022-04-15-multi-schema-change.md Co-authored-by: bb7133 --- docs/design/2022-04-15-multi-schema-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index ed03626e57c0a..be0ae17ce7b51 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -57,7 +57,7 @@ job := &Job { } ``` -In this way, we pack multiple schema changes into one job. Just like any other jobs, it enters the DDL job queue stored in TiKV and waits for a suitable worker to pick it up. +In this way, we pack multiple schema changes into one job. Just like any other jobs, it enters the DDL job queue stored in the storage, waits for a suitable worker to pick it up and handle it. In normal cases, the worker executes the sub-jobs one by one serially as if they are plain jobs. However, the thing gets complex in abnormal cases. From 6d8bf5e3c874d8ede9be315f07e6075adf9b0a12 Mon Sep 17 00:00:00 2001 From: tangenta Date: Fri, 6 May 2022 10:43:06 +0800 Subject: [PATCH 16/27] add some details --- docs/design/2022-04-15-multi-schema-change.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index be0ae17ce7b51..76101e5c80764 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -27,7 +27,10 @@ Above all, the lack of this capability can be a blocking issue for those who wan ## Proposal -The implementation is based on the [online DDL architecture](https://github.com/pingcap/tidb/blob/e0c461a84cf4ad55c7b51c3f9db7f7b9ba51bb62/docs/design/2018-10-08-online-DDL.md). Similar to the existing [Job](https://github.com/pingcap/tidb/blob/6bd54bea8a9ec25c8d65fcf1157c5ee7a141ab0b/parser/model/ddl.go/#L262) structure, we introduce a new structure "SubJob", which represents one single schema change. As its name suggests, a job can contain zero or more sub-jobs. +The implementation is based on the [online DDL architecture](https://github.com/pingcap/tidb/blob/e0c461a84cf4ad55c7b51c3f9db7f7b9ba51bb62/docs/design/2018-10-08-online-DDL.md). Similar to the existing [Job](https://github.com/pingcap/tidb/blob/6bd54bea8a9ec25c8d65fcf1157c5ee7a141ab0b/parser/model/ddl.go/#L262) structure, we introduce a new structure "SubJob": + +- "Job": A job is generally the internal representation of one DDL statement. +- "SubJob": A sub-job is a representation of one DDL schema change. A job may contain zero(when multi-schema change is not applicable) or more sub-jobs. The Multi-Schema Change DDL jobs have the type `ActionMultiSchemaChange`. In the current worker model, there is a dedicated code path (`onMultiSchemaChange()`) to run these jobs. Only Multi-Schema Change jobs can have sub-jobs. @@ -122,7 +125,6 @@ MySQL's execution order is as follows: 3. `RENAME a TO b` 4. `ADD INDEX i(b)` - Since this behavior is a bit counter-intuitive, we decided not to be fully compatible with MySQL. ```SQL @@ -134,4 +136,8 @@ TiDB validates the schema changes against a snapshot schema structure retrieved ## Alternative Proposals -An alternative solution is to give up the atomicity and uses multiple DDL jobs to complete the task. This is much less complex and requires little effort. However, it may lead to a visible intermediate state confusion. +- The 'job group' way. Unlike the 'SubJob', we can use a job group to represent multiple schema changes. However, this exposes the internal details of the job groups to the DDL job queue, making it more difficult to maintain. + +- The existing way, which is a case-by-case implementation (for example, [#15540](https://github.com/pingcap/tidb/pull/15540)). Although some cases can be supported, it is not extensible. + +- Another solution is to abandon atomization and use multiple DDL jobs to accomplish the task. This is much less complex and requires little effort. However, it can lead to confusion in the visible intermediate state. From c292f49bcabb8b7aa38c6eda57f706170e7f37bb Mon Sep 17 00:00:00 2001 From: tangenta Date: Fri, 6 May 2022 11:03:58 +0800 Subject: [PATCH 17/27] polish the words --- docs/design/2022-04-15-multi-schema-change.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 76101e5c80764..d30f1381d5fc9 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -21,9 +21,9 @@ ALTER TABLE t ADD COLUMN b INT AUTO_INCREMENT PRIMARY KEY, Currently, TiDB only supports one schema change per SQL statement and limited multi-schema changes for some rare cases. -When users try to migrate data from MySQL-like databases, they have to spend extra effort to rewrite a multi-schema change DDL to several single-schema change DDLs. For the users who rely on ORM frameworks like [Flyway](https://flywaydb.org/) to construct SQLs automatically, rewriting SQL could be tedious and the scripts are hard to maintain. +When users attempt to migrate data from MySQL-like databases, they may expend additional effort to rewrite a multi-schema change DDL to several single-schema change DDLs. For users who rely on ORM frameworks such as [Flyway](https://flywaydb.org/) to automatically construct SQLs, rewriting SQL could be tedious and the scripts are difficult to maintain. -Above all, the lack of this capability can be a blocking issue for those who want to use TiDB. +Above all, the lack of this capability can be a blocking issue for those who wish to use TiDB. ## Proposal @@ -60,15 +60,15 @@ job := &Job { } ``` -In this way, we pack multiple schema changes into one job. Just like any other jobs, it enters the DDL job queue stored in the storage, waits for a suitable worker to pick it up and handle it. +In this way, we pack multiple schema changes into one job. Like any other job, it enqueue the DDL job queue stored in the storage and waits for an appropriate worker to pick it up and process it. -In normal cases, the worker executes the sub-jobs one by one serially as if they are plain jobs. However, the thing gets complex in abnormal cases. +Normally, the worker executes the sub-jobs one by one serially as if they were plain jobs. However, in abnormal cases, things become complex. -To ensure the atomicity of a Multi-Schema Change execution, we need to maintain the changing schema object states carefully. Take the above SQL as an example, if the second sub-job `MODIFY COLUMN a CHAR(255)` failed for some reason, the first sub-job should be able to rollback its changes(rollback the added column `b`). +To ensure atomic execution of a Multi-Schema Change execution, we need to carefully manage the states of the changing schema objects. Let's take the above SQL as an example: If the second sub-job `MODIFY COLUMN a CHAR (255)` fails for some reason, the first sub-job should be able to roll back its changes (roll back the added column `b`). -This requirement means we cannot simply public the schema object when a sub-job is finished. Instead, it should stay in a state which is still unnoticeable to users, and wait for the other sub-jobs, finally public all at once when all the sub-jobs are confirmed to be successful. This method is kind of like 2PC: the "commit" can only be started when "prewrites" are complete. +This requirement means that we cannot simply publish the schema object when a sub-job is finished. Instead, it should remain in a state unnoticeable to users, waiting for the other sub-jobs to complete, eventually publishing all at once when it is confirmed that all sub-jobs have succeeded. This method is similar to 2PC: the "commit" cannot be started until the "prewrites" are completed. -Here is the table of schema states that may occur in different DDLs. Note the "Revertible States" means the changes are unnoticeable to the users. +Here is the table of schema states that can occur in different DDLs. Note that the "Revertible States" means that the changes are unnoticeable to the users. | DDL (Schema Change) | Revertible States | Non-revertible States | |-------------------------|--------------------------------------------|---------------------------------------------| @@ -79,11 +79,11 @@ Here is the table of schema states that may occur in different DDLs. Note the "R | Non-reorg Modify Column | Public (before meta change) | Public (after meta change) | | Reorg Modify Column | None, Delete-Only, Write-Only, Write-Reorg | Public | -To achieve this behavior, we introduce a flag in the sub-job named "non-revertible". This flag is set when a schema object has stepped to the last revertible state. When all sub-jobs become non-revertible, all the related schema objects step to the next state in one transaction. After that, the sub-jobs are executed serially to do the rest. +To achieve this behavior, we introduce a flag named "non-revertible" in the sub-job. This flag is set when a schema object has reached the last revertible state. When all sub-jobs are non-revertible, all associated schema objects change to the next state in one transaction. After that, the sub-jobs are executed serially to do the rest. -On the other hand, if there is an error returned by any sub-job before all of them become non-revertible, the whole job is switched to a `rollingback` state. For the executed sub-jobs, we set them to `cancelling`; for the sub-job that have not been executed, we set them to `cancelled`. +On the other hand, if there is an error returned by any sub-job before all of them become non-revertible, the entire job is placed in to a `rollingback` state. For the executed sub-jobs, we set them to `cancelling`; for the unexecuted sub-jobs, we set them to `cancelled`. -Finally, we consider the extreme case: an error occurs while all the sub-jobs are non-revertible. In this situation, we tend to believe that the error can be solved in a trivial way like retrying. This behavior is compatible with the current DDL implementation. Take `DROP COLUMN` as an example, once the column steps to the "Write-Only" state, there is no way to cancel this job. +Finally, we consider the extreme case: an error occurs while all the sub-jobs are non-revertible. In this situation, we tend to assume that the error can be resolved in a trivial way, e.g., by retrying. This behavior is consistent with the current DDL implementation. Let's take `DROP COLUMN` as an example: Once the column enters the "Write-Only" state, there is no way to abort this job. ## Compatibility From b9172d4fa1f1136f991583ce176e4db37a856683 Mon Sep 17 00:00:00 2001 From: tangenta Date: Tue, 10 May 2022 15:10:36 +0800 Subject: [PATCH 18/27] add future work part --- docs/design/2022-04-15-multi-schema-change.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index d30f1381d5fc9..f07640b2ce5c9 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -132,7 +132,17 @@ ALTER TABLE t DROP COLUMN b, RENAME COLUMN a TO b, ADD INDEX i(b), DROP INDEX i; ERROR 1060 (42S21): Duplicate column name 'b' ``` -TiDB validates the schema changes against a snapshot schema structure retrieved before the execution, regardless of the previous changes in the same DDL SQL statement. +TiDB validates the schema changes against a snapshot schema structure retrieved before the execution, regardless of the previous changes in the same DDL statement. This may affect some common use cases. For example, `ALTER TABLE t ADD COLUMN a, ADD INDEX i1(a);` is not supported. However, it is not difficult to support such statements: removing the specific validation is enough. + +## Future work + +In the future, this implementation can be utilized to develop other features or achieve some optimizations: + +- The table-level data reorganization like `ALTER TABLE CONVERT TO CHARSET` can be implemented by splitting a job into multiple sub-jobs. + +- In the scenario of adding multiple indexes, we can improve the performance by reducing the read cost: merge multiple "ADD INDEX" sub-jobs into one sub-job, and read the data once instead of multiple times. + +Furthermore, to help users understand how multi-schema change works, it is a good choice to support "EXPLAIN DDL", which can display some useful information like execution order, sub-jobs, and others. ## Alternative Proposals From 003d0e0e9836b2e03c514095d2b2f92d891bf9ad Mon Sep 17 00:00:00 2001 From: tangenta Date: Mon, 6 Jun 2022 16:01:54 +0800 Subject: [PATCH 19/27] Update docs/design/2022-04-15-multi-schema-change.md Co-authored-by: bb7133 --- docs/design/2022-04-15-multi-schema-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index f07640b2ce5c9..62cb1be5c9fb9 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -66,7 +66,7 @@ Normally, the worker executes the sub-jobs one by one serially as if they were p To ensure atomic execution of a Multi-Schema Change execution, we need to carefully manage the states of the changing schema objects. Let's take the above SQL as an example: If the second sub-job `MODIFY COLUMN a CHAR (255)` fails for some reason, the first sub-job should be able to roll back its changes (roll back the added column `b`). -This requirement means that we cannot simply publish the schema object when a sub-job is finished. Instead, it should remain in a state unnoticeable to users, waiting for the other sub-jobs to complete, eventually publishing all at once when it is confirmed that all sub-jobs have succeeded. This method is similar to 2PC: the "commit" cannot be started until the "prewrites" are completed. +This requirement means that we cannot simply publish the schema object when a sub-job is finished. Instead, it should remain in a state invisible to users, waiting for the other sub-jobs to complete, eventually publishing all at once when it is confirmed that all sub-jobs have succeeded. This method is similar to 2PC: the "commit" cannot be started until the "prewrites" are completed. Here is the table of schema states that can occur in different DDLs. Note that the "Revertible States" means that the changes are unnoticeable to the users. From 89777fbb2e0876c1dd2e516463c878024309ac8d Mon Sep 17 00:00:00 2001 From: tangenta Date: Mon, 6 Jun 2022 16:03:23 +0800 Subject: [PATCH 20/27] Update docs/design/2022-04-15-multi-schema-change.md --- docs/design/2022-04-15-multi-schema-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 62cb1be5c9fb9..c67eb6670cf69 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -68,7 +68,7 @@ To ensure atomic execution of a Multi-Schema Change execution, we need to carefu This requirement means that we cannot simply publish the schema object when a sub-job is finished. Instead, it should remain in a state invisible to users, waiting for the other sub-jobs to complete, eventually publishing all at once when it is confirmed that all sub-jobs have succeeded. This method is similar to 2PC: the "commit" cannot be started until the "prewrites" are completed. -Here is the table of schema states that can occur in different DDLs. Note that the "Revertible States" means that the changes are unnoticeable to the users. +Here is the table of schema states that can occur in different DDLs. Note that the "Revertible States" means that the changes are invisible to the users. | DDL (Schema Change) | Revertible States | Non-revertible States | |-------------------------|--------------------------------------------|---------------------------------------------| From 80215d9270657e1fd3ad977ff1b9331edd241a7a Mon Sep 17 00:00:00 2001 From: tangenta Date: Tue, 7 Jun 2022 17:06:26 +0800 Subject: [PATCH 21/27] add goal/non-goals, data structure and job execution explaination --- docs/design/2022-04-15-multi-schema-change.md | 77 +++++++++++++++++-- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index c67eb6670cf69..1461e3ac27d47 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -25,19 +25,56 @@ When users attempt to migrate data from MySQL-like databases, they may expend ad Above all, the lack of this capability can be a blocking issue for those who wish to use TiDB. +### Goal + +- Support MySQL-compatible Multi-Schema Change that used commonly, including `ADD/DROP COLUMN`, `ADD/DROP INDEX`, `MODIFY COLUMN`, `RENAME COLUMN`, etc. + +### Non-goals + +- Support TiDB-specific Multi-Schema Change like `ADD TIFLASH REPLICA`, `ADD PARTITION`, `ALTER PARTITION`, etc. +- Resolve the 'schema is change' error when DDL and DML are executed concurrently. +- Be 100% compatible with MySQL. MySQL may reorder the execution of schema changes, which makes the behavior counter-intuitive. + ## Proposal +### Data Structure + The implementation is based on the [online DDL architecture](https://github.com/pingcap/tidb/blob/e0c461a84cf4ad55c7b51c3f9db7f7b9ba51bb62/docs/design/2018-10-08-online-DDL.md). Similar to the existing [Job](https://github.com/pingcap/tidb/blob/6bd54bea8a9ec25c8d65fcf1157c5ee7a141ab0b/parser/model/ddl.go/#L262) structure, we introduce a new structure "SubJob": - "Job": A job is generally the internal representation of one DDL statement. - "SubJob": A sub-job is a representation of one DDL schema change. A job may contain zero(when multi-schema change is not applicable) or more sub-jobs. +```SQL +// Job represents a DDL action. +type Job struct { + Type ActionType `json:"type"` + State JobState `json:"state"` + // ... + MultiSchemaInfo *MultiSchemaInfo `json:"multi_schema_info"` +} + +// MultiSchemaInfo contains information for multi-schema change. +type MultiSchemaInfo struct { + // ... + SubJobs []SubJob `json:"sub_jobs"` +} + +// SubJob represents one schema change in a multi-schema change DDL. +type SubJob struct { + Type ActionType `json:"type"` + State JobState `json:"state"` + // ... +} +``` + +The field `ActionType` stands for the type of DDL. For example, `ADD COLUMN` is mapped to `ActionAddColumn`; `MODIFY COLUMN` is mapped to `ActionModifyColumn`. + The Multi-Schema Change DDL jobs have the type `ActionMultiSchemaChange`. In the current worker model, there is a dedicated code path (`onMultiSchemaChange()`) to run these jobs. Only Multi-Schema Change jobs can have sub-jobs. For example, the DDL statement ```SQL -ALTER TABLE t ADD COLUMN b INT, MODIFY COLUMN a CHAR(255); +ALTER TABLE t ADD COLUMN b INT, MODIFY COLUMN a CHAR(10); ``` can be modeled as a job like @@ -62,9 +99,39 @@ job := &Job { In this way, we pack multiple schema changes into one job. Like any other job, it enqueue the DDL job queue stored in the storage and waits for an appropriate worker to pick it up and process it. -Normally, the worker executes the sub-jobs one by one serially as if they were plain jobs. However, in abnormal cases, things become complex. +### Job/Sub-job Execution + +As shown in the code above, there is a field `State` in both `Job` and `SubJob`. All the possible states and the changes are listed here: + +``` + ┌-->--- Done --------->----------┐ +None -> Running -> Rollingback -> RollbackDone -> Synced + └-->--- Cancelling -> Cancelled -->-┘ +``` + +We can divided the states into four types: + +| States | Normal | Abnormal | +|-------------|---------------|-------------------------| +| Uncompleted | None, Running | Rollingback, Cancelling | +| Completed | Done | RollbackDone, Cancelled | + +Since a `Job` is executed by a DDL worker, the sub-jobs are executed in a single thread. The general principal to select a sub-job is as follows: + +- For the normal state, the first uncompleted sub-job is selected in ascending order, i.e., the sub-job with the smaller order number is executed first. +- For the abnormal state, the first uncompleted sub-job is selected in decending order, i.e., the sub-job with the larger number is executed first. + +When one of the sub-job becomes abnormal, the parent job and all the other sub-jobs are changed to an abnormal state. + +### Schema Object State Management + +To ensure atomic execution of Multi-Schema Change, we need to carefully manage the states of the changing schema objects. Let's take the above SQL as an example: + +```SQL +ALTER TABLE t ADD COLUMN b INT, MODIFY COLUMN a CHAR(10); +``` -To ensure atomic execution of a Multi-Schema Change execution, we need to carefully manage the states of the changing schema objects. Let's take the above SQL as an example: If the second sub-job `MODIFY COLUMN a CHAR (255)` fails for some reason, the first sub-job should be able to roll back its changes (roll back the added column `b`). +If the second sub-job `MODIFY COLUMN a CHAR (10)` fails for some reason(rows have more than ten characters), the first sub-job should be able to roll back its changes (roll back the added column `b`). This requirement means that we cannot simply publish the schema object when a sub-job is finished. Instead, it should remain in a state invisible to users, waiting for the other sub-jobs to complete, eventually publishing all at once when it is confirmed that all sub-jobs have succeeded. This method is similar to 2PC: the "commit" cannot be started until the "prewrites" are completed. @@ -81,9 +148,9 @@ Here is the table of schema states that can occur in different DDLs. Note that t To achieve this behavior, we introduce a flag named "non-revertible" in the sub-job. This flag is set when a schema object has reached the last revertible state. When all sub-jobs are non-revertible, all associated schema objects change to the next state in one transaction. After that, the sub-jobs are executed serially to do the rest. -On the other hand, if there is an error returned by any sub-job before all of them become non-revertible, the entire job is placed in to a `rollingback` state. For the executed sub-jobs, we set them to `cancelling`; for the unexecuted sub-jobs, we set them to `cancelled`. +On the other hand, if there is an error returned by any sub-job before all of them become non-revertible, the entire job is placed in to a `Rollingback` state. For the executed sub-jobs, we set them to `Cancelling`; for the unexecuted sub-jobs, we set them to `Cancelled`. -Finally, we consider the extreme case: an error occurs while all the sub-jobs are non-revertible. In this situation, we tend to assume that the error can be resolved in a trivial way, e.g., by retrying. This behavior is consistent with the current DDL implementation. Let's take `DROP COLUMN` as an example: Once the column enters the "Write-Only" state, there is no way to abort this job. +Finally, we consider the extreme case: an error occurs while all the sub-jobs are non-revertible. There are two kinds of errors in general, the logical error(such as the violation of unique constraint, out-of-range data) and the physical error(such as unavailablity of the network, unusability of the storage). In this situation, the error is guaranteed to be a physical one: we tend to assume that it can be resolved in a trivial way, e.g., by retrying. This behavior is consistent with the current DDL implementation. Take `DROP COLUMN` as an example, once the column enters the "Write-Only" state, there is no way to abort this job. ## Compatibility From 98a80bafed2708af1914ac47288b0e0aadfa6134 Mon Sep 17 00:00:00 2001 From: tangenta Date: Tue, 7 Jun 2022 17:24:21 +0800 Subject: [PATCH 22/27] polish the words --- docs/design/2022-04-15-multi-schema-change.md | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 1461e3ac27d47..b3121542f09ea 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -32,7 +32,7 @@ Above all, the lack of this capability can be a blocking issue for those who wis ### Non-goals - Support TiDB-specific Multi-Schema Change like `ADD TIFLASH REPLICA`, `ADD PARTITION`, `ALTER PARTITION`, etc. -- Resolve the 'schema is change' error when DDL and DML are executed concurrently. +- Resolve the 'schema is changed' error when DDL and DML are executed concurrently. - Be 100% compatible with MySQL. MySQL may reorder the execution of schema changes, which makes the behavior counter-intuitive. ## Proposal @@ -44,7 +44,7 @@ The implementation is based on the [online DDL architecture](https://github.com/ - "Job": A job is generally the internal representation of one DDL statement. - "SubJob": A sub-job is a representation of one DDL schema change. A job may contain zero(when multi-schema change is not applicable) or more sub-jobs. -```SQL +```go // Job represents a DDL action. type Job struct { Type ActionType `json:"type"` @@ -97,29 +97,31 @@ job := &Job { } ``` -In this way, we pack multiple schema changes into one job. Like any other job, it enqueue the DDL job queue stored in the storage and waits for an appropriate worker to pick it up and process it. +In this way, we pack multiple schema changes into one job. Like any other job, it enqueue the DDL job queue to the persistent storage and waits for an appropriate worker to pick it up and process it. ### Job/Sub-job Execution As shown in the code above, there is a field `State` in both `Job` and `SubJob`. All the possible states and the changes are listed here: ``` - ┌-->--- Done --------->----------┐ + ┌-->--- Done ->------------------┐ + | | None -> Running -> Rollingback -> RollbackDone -> Synced + | | └-->--- Cancelling -> Cancelled -->-┘ ``` -We can divided the states into four types: +We can divided these states into four types: -| States | Normal | Abnormal | -|-------------|---------------|-------------------------| -| Uncompleted | None, Running | Rollingback, Cancelling | -| Completed | Done | RollbackDone, Cancelled | +| States | Normal | Abnormal | +|-----------------|-------------------|-----------------------------| +| **Uncompleted** | `None`, `Running` | `Rollingback`, `Cancelling` | +| **Completed** | `Done` | `RollbackDone`, `Cancelled` | Since a `Job` is executed by a DDL worker, the sub-jobs are executed in a single thread. The general principal to select a sub-job is as follows: - For the normal state, the first uncompleted sub-job is selected in ascending order, i.e., the sub-job with the smaller order number is executed first. -- For the abnormal state, the first uncompleted sub-job is selected in decending order, i.e., the sub-job with the larger number is executed first. +- For the abnormal state, the first uncompleted sub-job is selected in descending order, i.e., the sub-job with the larger order number is executed first. When one of the sub-job becomes abnormal, the parent job and all the other sub-jobs are changed to an abnormal state. @@ -131,7 +133,7 @@ To ensure atomic execution of Multi-Schema Change, we need to carefully manage t ALTER TABLE t ADD COLUMN b INT, MODIFY COLUMN a CHAR(10); ``` -If the second sub-job `MODIFY COLUMN a CHAR (10)` fails for some reason(rows have more than ten characters), the first sub-job should be able to roll back its changes (roll back the added column `b`). +If the second sub-job `MODIFY COLUMN a CHAR (10)` fails for some reason(e.g., a row has more than ten characters), the first sub-job should be able to roll back its changes (roll back the added column `b`). This requirement means that we cannot simply publish the schema object when a sub-job is finished. Instead, it should remain in a state invisible to users, waiting for the other sub-jobs to complete, eventually publishing all at once when it is confirmed that all sub-jobs have succeeded. This method is similar to 2PC: the "commit" cannot be started until the "prewrites" are completed. @@ -146,7 +148,7 @@ Here is the table of schema states that can occur in different DDLs. Note that t | Non-reorg Modify Column | Public (before meta change) | Public (after meta change) | | Reorg Modify Column | None, Delete-Only, Write-Only, Write-Reorg | Public | -To achieve this behavior, we introduce a flag named "non-revertible" in the sub-job. This flag is set when a schema object has reached the last revertible state. When all sub-jobs are non-revertible, all associated schema objects change to the next state in one transaction. After that, the sub-jobs are executed serially to do the rest. +To achieve this behavior, we introduce a flag named "non-revertible" in the sub-job. This flag is set when a schema object has reached the last revertible state. A sub-job with this flag is considered temporary completed, so that the worker can select the next sub-job. When all sub-jobs are non-revertible, all associated schema objects change to the next state in one transaction. After that, the sub-jobs are executed serially to do the rest. On the other hand, if there is an error returned by any sub-job before all of them become non-revertible, the entire job is placed in to a `Rollingback` state. For the executed sub-jobs, we set them to `Cancelling`; for the unexecuted sub-jobs, we set them to `Cancelled`. From 730f4a221895fbdd5532a237b0621962233847e7 Mon Sep 17 00:00:00 2001 From: tangenta Date: Wed, 8 Jun 2022 10:43:15 +0800 Subject: [PATCH 23/27] Update docs/design/2022-04-15-multi-schema-change.md Co-authored-by: bb7133 --- docs/design/2022-04-15-multi-schema-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index b3121542f09ea..d3751f5ac8003 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -33,7 +33,7 @@ Above all, the lack of this capability can be a blocking issue for those who wis - Support TiDB-specific Multi-Schema Change like `ADD TIFLASH REPLICA`, `ADD PARTITION`, `ALTER PARTITION`, etc. - Resolve the 'schema is changed' error when DDL and DML are executed concurrently. -- Be 100% compatible with MySQL. MySQL may reorder the execution of schema changes, which makes the behavior counter-intuitive. +- Be 100% compatible with MySQL. MySQL may reorder the execution of schema changes, which makes the behavior counter-intuitive sometimes. ## Proposal From ed0def9a25f361d89af5d813a9f8cbb8336192fc Mon Sep 17 00:00:00 2001 From: tangenta Date: Wed, 8 Jun 2022 10:43:25 +0800 Subject: [PATCH 24/27] Update docs/design/2022-04-15-multi-schema-change.md Co-authored-by: bb7133 --- docs/design/2022-04-15-multi-schema-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index d3751f5ac8003..ee151e21647d3 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -160,7 +160,7 @@ Finally, we consider the extreme case: an error occurs while all the sub-jobs ar When a TiDB cluster performs a rolling upgrade, there are 2 cases: -1. DDL owner is the new version of TiDB. When users execute a Multi-Schema Change statement on an old version of TiDB, they receive an "Unsupported Multi-Schema Change" error message. +1. DDL owner is the new version of TiDB. When users execute a Multi-Schema Change statement on an old version of TiDB, they receive an "unsupported multi-schema change" error message. 2. DDL owner is the old version of TiDB. When users execute a Multi-Schema Change statement on a new version of TiDB, they receive the "invalid ddl job type" error message. In both cases, the Multi-Schema Change statement cannot be executed. Therefore, users should avoid executing Multi-Schema Change during a rolling upgrade. From b75aeabc27648f1d6a0404a906125803a25946a7 Mon Sep 17 00:00:00 2001 From: tangenta Date: Wed, 8 Jun 2022 10:54:56 +0800 Subject: [PATCH 25/27] Update docs/design/2022-04-15-multi-schema-change.md --- docs/design/2022-04-15-multi-schema-change.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index ee151e21647d3..2ea0610ec7c37 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -34,6 +34,7 @@ Above all, the lack of this capability can be a blocking issue for those who wis - Support TiDB-specific Multi-Schema Change like `ADD TIFLASH REPLICA`, `ADD PARTITION`, `ALTER PARTITION`, etc. - Resolve the 'schema is changed' error when DDL and DML are executed concurrently. - Be 100% compatible with MySQL. MySQL may reorder the execution of schema changes, which makes the behavior counter-intuitive sometimes. +- Improve the performance of DDL, although it introduces a way to improve the performance of long-running jobs(see 'Future work' part). ## Proposal From 9978ec5cfc90f62b949cc1af958e4b2738c0544f Mon Sep 17 00:00:00 2001 From: tangenta Date: Wed, 8 Jun 2022 20:34:53 +0800 Subject: [PATCH 26/27] Update 2022-04-15-multi-schema-change.md --- docs/design/2022-04-15-multi-schema-change.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 2ea0610ec7c37..97e309dcf6463 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -5,18 +5,19 @@ ## Abstract -This proposes an implementation of applying multiple schema changes in one `ALTER TABLE` SQL statement. +This proposes an implementation of applying multiple schema changes in one `ALTER TABLE` statement. ## Background -Multi-Schema Change is one of MySQL's extended features to the SQL standard. It allows the users to atomically make multiple schema changes in one SQL statement, including column and index `ADD`, `ALTER`, `DROP`, and `CHANGE`, as well as table option changes. For example: +Multi-Schema Change is one of MySQL's extended features to the SQL standard. It allows the users to atomically make multiple schema changes in one statement, including column and index `ADD`, `ALTER`, `DROP`, and `CHANGE`, as well as table option changes. For example: ```sql CREATE TABLE t (a INT, c INT); -ALTER TABLE t ADD COLUMN b INT AUTO_INCREMENT PRIMARY KEY, +ALTER TABLE t ADD COLUMN b INT, MODIFY COLUMN c CHAR(5), ADD INDEX idx(a), - AUTO_INCREMENT = 1000; + ADD PRIMARY KEY (c), + COMMENT = "comments for t"; ``` Currently, TiDB only supports one schema change per SQL statement and limited multi-schema changes for some rare cases. From 699950f897e5f047dfc7e1c1a9088e364da14e88 Mon Sep 17 00:00:00 2001 From: tangenta Date: Wed, 8 Jun 2022 20:36:57 +0800 Subject: [PATCH 27/27] Update 2022-04-15-multi-schema-change.md --- docs/design/2022-04-15-multi-schema-change.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/design/2022-04-15-multi-schema-change.md b/docs/design/2022-04-15-multi-schema-change.md index 97e309dcf6463..1ffad0666e307 100644 --- a/docs/design/2022-04-15-multi-schema-change.md +++ b/docs/design/2022-04-15-multi-schema-change.md @@ -30,12 +30,12 @@ Above all, the lack of this capability can be a blocking issue for those who wis - Support MySQL-compatible Multi-Schema Change that used commonly, including `ADD/DROP COLUMN`, `ADD/DROP INDEX`, `MODIFY COLUMN`, `RENAME COLUMN`, etc. -### Non-goals +### Non-Goals - Support TiDB-specific Multi-Schema Change like `ADD TIFLASH REPLICA`, `ADD PARTITION`, `ALTER PARTITION`, etc. - Resolve the 'schema is changed' error when DDL and DML are executed concurrently. - Be 100% compatible with MySQL. MySQL may reorder the execution of schema changes, which makes the behavior counter-intuitive sometimes. -- Improve the performance of DDL, although it introduces a way to improve the performance of long-running jobs(see 'Future work' part). +- Improve the performance of DDL, although it introduces a way to improve the performance of long-running jobs(see 'Future Work' part). ## Proposal @@ -205,7 +205,7 @@ ERROR 1060 (42S21): Duplicate column name 'b' TiDB validates the schema changes against a snapshot schema structure retrieved before the execution, regardless of the previous changes in the same DDL statement. This may affect some common use cases. For example, `ALTER TABLE t ADD COLUMN a, ADD INDEX i1(a);` is not supported. However, it is not difficult to support such statements: removing the specific validation is enough. -## Future work +## Future Work In the future, this implementation can be utilized to develop other features or achieve some optimizations: