经常会遇到这种情况,我们的业务已经稳定地运行一段时间了,并且流量渐渐已经上去了。这时候,却因为某些原因(比如功能调整或者业务扩展),你需要对数据表进行调整,加字段 or 修改表结构。
可能很多人说 alter table add column ... / alter table modify ...,轻轻松松就解决了。 这样其实是有风险的
,对于复杂度比较高、数据量比较大的表。调整表结构、创建或删除索引、触发器,都可能引起锁表,而锁表的时长依你的数据表实际情况而定。 本人有过惨痛的教训,在一次业务上线过程中没有评估好数据规模,导致长时间业务数据写入不进来。
那么有什么办法对数据库的业务表进行无缝升级,让该表对用户透明无感呢?下面我们一个个来讨论。
最简单的一种办法,把新增的字段存储在另外一张辅表上,用外键关联到主表的主键。达到动态扩展的目标。后续功能上线之后,新增的数据会存储到辅表中,主表无需调整,透明、无损。
存在的问题:
假设我们原有表结构如下,为了保障业务的持续发展,后续不间断的会有字段扩展。这时候就需要考虑增加一个可自动扩缩的通用字段。
以MySQL为例子,5.7版本版本之后提供了Json字段类型,方便我们存储复杂的Json对象数据。
use test;
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE "t_user" (
"id" bigint(20) NOT NULL AUTO_INCREMENT,
"name" varchar(20) NOT NULL,
"age" int(11) DEFAULT NULL,
"address" varchar(255) DEFAULT NULL,
"sex" int(11) DEFAULT '1',
"ext_data" json DEFAULT NULL COMMENT 'json字符串',
PRIMARY KEY ("id")
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES ('1', 'brand', '21', 'fuzhou', '1', '{"tel": "13212345678", "name": "brand", "address": "fuzhou"}');
代码中 ext_data 采用Json数据类型,是一种可扩展的对象载体,存放被查询数据的信息补充。
同样的,MySQL提供的这种数据类型,也提供了很强大的Json函数进行操作。
SELECT id,`name`,age,address FROM `t_user` WHERE json_extract(ext_data,'$.tel') = '13212345678';
结果如下:
MySQL Json 的用法,大家可以看一下官网的文档,还是比较清晰的。
Json结构一般来说是向下兼容的,所以你在设计字段扩展的时候,一般建议往前增,不建议删除旧属性。但是这也有个问题,就是业务越复杂,Json复杂度也越高,冗余属性也越多。
比如上文中我们的json包含三个属性,tel、name、address,之后的业务调整中,发现tel没用了,加了个age属性,那tel要不要删除?
有一种比较好的办法,是给表加上version属性,每个时期的业务对应一个version,每个version对应的Json数据结构也不一样。
优点:
不足:
改进:
整个步骤如下:
如果是MySQL数据库,可以通过复制binlog的操作进行数据迁移的,效果一样,比起触发器,更稳定一点。
预留字段 和 字段与表格名称映射的办法。
如果业务流量比较小,可以直接在表上进行字段新增或者修改,短暂的写锁是可以承受的。但如果是高并发、集群化、分布式的系统,则从数据层面上就应该进行主从或者分库分表治理。
以下是典型的的多主要模式下,进行数据库表结构升级的过程。