DDL Schema变更之添加列有默认值时处理

对于NewSQL而言,是有表的概念的,即schema的概念。NewSQL以单独的SQL层接入用户的SQL请求,进行Parse、Logic Plan、Physical Plan,之后将对应的计划,下推到KV层进行执行。

DDL Schema变更之添加列有默认值时处理_第1张图片
TiDB Architecture

只要有表的存在,就会有DDL执行,TiDB的在线schema 变更参考了Google F1 Online, Asynchronous Schema Change in F1
关于这篇论文的讨论,我们放在下一篇进行分析,将结合代码一起讨论。

本篇先只讨论单机情况下,加列并存在默认值时是如何做的,我们以TiDB为例,分析如下场景。

考虑有表t1,有2列,id int primary key , age int . 并且插入2行数据:

id age
1 18
2 19

现在要加入一列,叫 marriaged_age int default 26

alter table t1 ADD marriaged_age int default 26 ;

通常,schema信息作为一个单独的Key—Value对存储在Kv层,当更新这张表的schema信息时,也就是对这个key-value的更新,我们暂时不考虑分布式场景。

id age marriaged_age
1 18 empty
2 19 empty

TiDB会将整行数据按照编码格式编码成一个字符串,作为value。key的构成格式请参考TiDB的key编码

对于存量数据,本身是没有这一列的信息的,那么就有两种做法:

  1. 给存量数据的每一行填上这一列的值
  2. 不回填这些值,当根据新的schema信息去解码value时候,发现这一列的值是empty,则用schema信息中的default值作为这一列的value。返回给上层。

TiDB采用第二种方法.

在alter table之后插入的数据,marriaged_age 这一列无论是否insert时候指定,都会有一个对应的值。

insert into t1 values(3,20);
insert into t1 values(4,22,28);

新插入的数据和旧数据如下:

id age marriaged_age
1 18 empty
2 19 empty
3 20 26
4 22 28

若这时,对marriaged_age列的default值再次进行修改时:

alter table t1 CHANGE marriaged_age marriaged_age int default 24 ;

此之后插入的数据该列的值默认值就是24了。

insert into t1 values(5,26);
insert into t1 values(6,27,30);

新插入的数据和旧数据如下:

id age marriaged_age
1 18 empty
2 19 empty
3 20 26
4 22 28
5 26 24
6 27 30

这时候,对于记录id为(1,2)的marriaged_age值,究竟应该解析成26?还是24呢?

当然是26,因为在第一次DDL变更的时候(即添加marriaged_age,就相当于已经对于存量数据做了回填,第二次变更只能对其之后的插入操作造成影响!

TiDB是如何实现这样的效果的呢?

TiDB在存Schema信息的时候,每个列的属性中,有一个 OriginalDefaultValue 和 DefaultValue

// model.go中,对列属性的描述

// ColumnInfo provides meta data describing of a table column.
type ColumnInfo struct {
    ID                  int64               `json:"id"`
    Name                CIStr               `json:"name"`
    Offset              int                 `json:"offset"`
    OriginDefaultValue  interface{}         `json:"origin_default"`
    DefaultValue        interface{}         `json:"default"`
    DefaultValueBit     []byte              `json:"default_bit"`
    GeneratedExprString string              `json:"generated_expr_string"`
    GeneratedStored     bool                `json:"generated_stored"`
    Dependences         map[string]struct{} `json:"dependences"`
    types.FieldType     `json:"type"`
    State               SchemaState `json:"state"`
    Comment             string      `json:"comment"`
    // Version means the version of the column info.
    // Version = 0: For OriginDefaultValue and DefaultValue of timestamp column will stores the default time in system time zone.
    //              That is a bug if multiple TiDB servers in different system time zone.
    // Version = 1: For OriginDefaultValue and DefaultValue of timestamp column will stores the default time in UTC time zone.
    //              This will fix bug in version 0. For compatibility with version 0, we add version field in column info struct.
    Version uint64 `json:"version"`
}

当value解码后,存在有的列是empty,那么就采用OriginDefaultValue进行填充。

OriginDefaultValue的存在就是为了保证那些在做DDL变更(这里特指ADD Column)之前的行,没有这列数据的value能够有默认值填充。

而DefaultValue就表示的是当下这列实时的default value值,以后对这列的default value 如果再有变更,那么只需要记录在DefaultValue这个变量中就可以了。

感谢磊哥(吕磊)倾情指点

萧然 2019-07-15 09:56

转载请注明出处

你可能感兴趣的:(DDL Schema变更之添加列有默认值时处理)