對(duì)于NewSQL而言,是有表的概念的,即schema的概念枕磁。NewSQL以單獨(dú)的SQL層接入用戶的SQL請(qǐng)求,進(jìn)行Parse术吝、Logic Plan计济、Physical Plan,之后將對(duì)應(yīng)的計(jì)劃排苍,下推到KV層進(jìn)行執(zhí)行沦寂。
只要有表的存在,就會(huì)有DDL執(zhí)行淘衙,TiDB的在線schema 變更參考了Google F1 Online, Asynchronous Schema Change in F1
關(guān)于這篇論文的討論凑队,我們放在下一篇進(jìn)行分析,將結(jié)合代碼一起討論幔翰。
本篇先只討論單機(jī)情況下,加列并存在默認(rèn)值時(shí)是如何做的西壮,我們以TiDB為例遗增,分析如下場(chǎng)景。
考慮有表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信息作為一個(gè)單獨(dú)的Key—Value對(duì)存儲(chǔ)在Kv層饰及,當(dāng)更新這張表的schema信息時(shí),也就是對(duì)這個(gè)key-value的更新康震,我們暫時(shí)不考慮分布式場(chǎng)景燎含。
id | age | marriaged_age |
---|---|---|
1 | 18 | empty |
2 | 19 | empty |
TiDB會(huì)將整行數(shù)據(jù)按照編碼格式編碼成一個(gè)字符串,作為value腿短。key的構(gòu)成格式請(qǐng)參考TiDB的key編碼
對(duì)于存量數(shù)據(jù)屏箍,本身是沒(méi)有這一列的信息的,那么就有兩種做法:
- 給存量數(shù)據(jù)的每一行填上這一列的值
- 不回填這些值橘忱,當(dāng)根據(jù)新的schema信息去解碼value時(shí)候赴魁,發(fā)現(xiàn)這一列的值是empty,則用schema信息中的default值作為這一列的value钝诚。返回給上層颖御。
TiDB采用第二種方法.
在alter table之后插入的數(shù)據(jù),marriaged_age 這一列無(wú)論是否insert時(shí)候指定凝颇,都會(huì)有一個(gè)對(duì)應(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 |
若這時(shí)疹鳄,對(duì)marriaged_age列的default值再次進(jìn)行修改時(shí):
alter table t1 CHANGE marriaged_age marriaged_age int default 24 ;
此之后插入的數(shù)據(jù)該列的值默認(rèn)值就是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 |
這時(shí)候泽铛,對(duì)于記錄id為(1尚辑,2)的marriaged_age值,究竟應(yīng)該解析成26盔腔?還是24呢杠茬?
當(dāng)然是26,因?yàn)樵诘谝淮蜠DL變更的時(shí)候(即添加marriaged_age弛随,就相當(dāng)于已經(jīng)對(duì)于存量數(shù)據(jù)做了回填瓢喉,第二次變更只能對(duì)其之后的插入操作造成影響!
TiDB是如何實(shí)現(xiàn)這樣的效果的呢舀透?
TiDB在存Schema信息的時(shí)候栓票,每個(gè)列的屬性中,有一個(gè) OriginalDefaultValue 和 DefaultValue
// model.go中愕够,對(duì)列屬性的描述
// 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"`
}
當(dāng)value解碼后走贪,存在有的列是empty,那么就采用OriginDefaultValue進(jìn)行填充惑芭。
OriginDefaultValue的存在就是為了保證那些在做DDL變更(這里特指ADD Column)之前的行坠狡,沒(méi)有這列數(shù)據(jù)的value能夠有默認(rèn)值填充。
而DefaultValue就表示的是當(dāng)下這列實(shí)時(shí)的default value值遂跟,以后對(duì)這列的default value 如果再有變更逃沿,那么只需要記錄在DefaultValue這個(gè)變量中就可以了。
感謝磊哥(呂磊)傾情指點(diǎn)
蕭然 2019-07-15 09:56
轉(zhuǎn)載請(qǐng)注明出處