DDL Schema變更之添加列有默認值時處理

對于NewSQL而言,是有表的概念的,即schema的概念。NewSQL以單獨的SQL層接入用戶的SQL請求,進行Parse、Logic Plan、Physical Plan,之后將對應(yīng)的計劃,下推到KV層進行執(zhí)行。

TiDB Architecture

只要有表的存在,就會有DDL執(zhí)行,TiDB的在線schema 變更參考了Google F1 Online, Asynchronous Schema Change in F1
關(guān)于這篇論文的討論,我們放在下一篇進行分析,將結(jié)合代碼一起討論。

本篇先只討論單機情況下,加列并存在默認值時是如何做的,我們以TiDB為例,分析如下場景。

考慮有表t1,有2列,id int primary key , age int . 并且插入2行數(shù)據(jù):

id age
1 18
2 19

現(xiàn)在要加入一列,叫 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會將整行數(shù)據(jù)按照編碼格式編碼成一個字符串,作為value。key的構(gòu)成格式請參考TiDB的key編碼

對于存量數(shù)據(jù),本身是沒有這一列的信息的,那么就有兩種做法:

  1. 給存量數(shù)據(jù)的每一行填上這一列的值
  2. 不回填這些值,當根據(jù)新的schema信息去解碼value時候,發(fā)現(xiàn)這一列的值是empty,則用schema信息中的default值作為這一列的value。返回給上層。

TiDB采用第二種方法.

在alter table之后插入的數(shù)據(jù),marriaged_age 這一列無論是否insert時候指定,都會有一個對應(yīng)的值。

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

新插入的數(shù)據(jù)和舊數(shù)據(jù)如下:

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 ;

此之后插入的數(shù)據(jù)該列的值默認值就是24了。

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

新插入的數(shù)據(jù)和舊數(shù)據(jù)如下:

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值,究竟應(yīng)該解析成26?還是24呢?

當然是26,因為在第一次DDL變更的時候(即添加marriaged_age,就相當于已經(jīng)對于存量數(shù)據(jù)做了回填,第二次變更只能對其之后的插入操作造成影響!

TiDB是如何實現(xiàn)這樣的效果的呢?

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)之前的行,沒有這列數(shù)據(jù)的value能夠有默認值填充。

而DefaultValue就表示的是當下這列實時的default value值,以后對這列的default value 如果再有變更,那么只需要記錄在DefaultValue這個變量中就可以了。

感謝磊哥(呂磊)傾情指點

蕭然 2019-07-15 09:56

轉(zhuǎn)載請注明出處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容