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

只要有表的存在,就會有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ù),本身是沒有這一列的信息的,那么就有兩種做法:
- 給存量數(shù)據(jù)的每一行填上這一列的值
- 不回填這些值,當根據(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)載請注明出處