声明:部分内容来自网络收集


需求

    线上User表目前存在四个字段user(uid, name, passwd, nick),现在需要增加两个字段age, sex,变为user(uid, name, passwd, nick, age, sex)

背景

    目前user表数据量较大,且并发请求量较大

解决方案:

方案一、

alter table add column

优势:方案最为简单

因为背景以及交代了,数据量较大,且并发请求高,MySQL在alter table时会锁表,且数据量较大,导致锁表时间过长,无法接受

方案二、

在项目上线初期,预留字段

优势:可以在需要扩展表字段的时候,自动使用预留的字段,因为字段在第一次建表时已经创建了,因此不需要扩充,直接使用已经存在的字段

劣势:1、预留字段的数量无法确定,预留多了,浪费存储空间,预留少了,未来还是要在表上增加字段(只是推迟了时间)

2、预留的字段只能定义为varchar类型的可辩字符串,无法支持其他类型字段,如果需要检索,varchar类型建立索引非理想方案

3、预留字段,字段名称不具有语义化,例如在建立初期,可能叫ext1、ext2等,但是ext1、ext2不具有语义化,后期维护沟通成本高

 

 

方案三

使用纵表存储用户信息,例如之前用户数据


uid

key

value

1

name

张三

1

passport

123

1

nick

NULL

2

name

李四

2

passport

431

2

nick

aaa


需要扩展age, sex时,数据存储格式如下


uid

key

value

1

name

张三

1

passport

123

1

nick

NULL

2

name

李四

2

passport

431

2

nick

aaa

3

name

王五

3

passport

123

3

nick

NULL

3

age

11

3

sex

1


优势:

1、可以随意新增字段,字段可以无限扩展

2、新旧数据可以同时存在

劣势:

1、同一个用户,单行数据变为多行存储,数据量翻倍

2、在key列不能建立索引(建立索引无不能起到检索作用),只能在uid列建立索引,检索方式太单一

 

方案四、

版本号+通用列

最开始上线的时候,版本为0,此时只有passwd和nick两个属性,那么数据为


uid

name

version

ext

1

张三

0

{"passport":"123",“nick”:NULL}

2

李四

0

{"passport":"431",“nick”:"aaa"}


当需要扩展新字段时,将新数据版本升级为1,新增加age, sex两个字段,数据变为


uid

name

version

ext

1

张三

0

{"passport":"123",“nick”:NULL}

2

李四

0

{"passport":"431",“nick”:"aaa"}

3

王五

1

{"passport":"431",“nick”:"aaa","age":3,"sex":1}


优势:

1、不需要做在线DDL,字段可以无限扩展(但是总长度不能超过768个字节)

2、新数据和老数据可以共存

3、迁移方便,可以线上写个脚本慢慢将老数据修改为新版本数据,并将version修改为1

劣势:

1、无法检索(虽然MySQL5.7开始支持JSON和JSON所以,目前本人未进行实际的性能测试)

2、ext列存在大量冗余的key,虽然可以将Key的值缩短,但是会降低key的语义

当然,也有一些将ext这种扩展信息存储在类似mongodb的NoSQL数据库中。

 

方案五、

新表+触发器+迁移数据+rename

基本原理是:
(1)先创建一个扩充字段后的新表user_new(uid, name, passwd, age, sex)
(2)在原表user上创建三个触发器,对原表user进行的所有insert/delete/update操作,都会对新表user_new进行相同的操作(这个操作有些公司也侵入到代码层面来实现)
(3)分批将原表user中的数据insert到新表user_new,直至数据迁移完成
(4)删掉触发器,把原表移走(默认是drop掉)
(5)把新表user_new重命名(rename)成原表user,扩充字段完成。

优势:整个过程不需要锁表,可以持续对外提供服务

劣势:

1、整个过程需要进行数据的迁移,如果数据量较大,可能周期较长

2、变更过程中,可能存在数据冲突

3、通过提供触发器或者代码来实现两次insert/delete/update操作,如果是建立触发器,会影响原表性能。

方案六、

增加1对1关联的扩展表

例如原来user(uid, name, passwd, nick)

新建一个user_ext表user(uid, age, sex)

通过uid进行一对一的关联

优势:

1、在不对原表进行任何操作的情况下,实现字段扩展

2、可持续对外提供服务,user表中历史数据,可以通过脚本在user_ext慢慢补齐或者在代码存在添加默认值,更新的时候做merge操作补齐

劣势:

1、两张表需要做join操作,在大量数据情况下,存在性能瓶颈。(互联网公司一般严格限制join的使用)

2、user_ext表未来也存在扩展字段的问题。



 

目前,如果存在对online db进行DDL时,一般公司都会采用第五种方案,虽然操作步骤比较多,周期较长,但是其优势也比较明显,不会增长数据量、不会丢失关系型数据库特性等等