做者:李瑜宁
java
笔者的动态字段扩展解决方案主要针对 Mysql 5.7.8 如下版本,在 Mysql 5.7.8 已经新增 JSON Data Type,一样适用该方案,并且状况变得更加简单。mysql
软件行业惟一不变的就是变化,好比功能上线以后,客户或 PM 须要对已有的功能增长一些合理的需求,完成这些工做必须经过添加字段解决,或者某些功能的实现须要经过增长字段来下降实现的复杂性等等。面试
这些问题都会改动线上的数据库表结构,一旦改动就会致使锁表,会使全部的写入操做一直等待,直到表锁关闭,特别是对于数据量大的热点表,添加一个字段可能会由于锁表时间过长而致使部分请求超时,这可能会对企业间接形成经济上的损失。sql
增长 json 格式的扩展字段。数据库
下面配合一些代码来描述这个解决方案,读者便于去理解。json
mysql 数据库脚本:ide
DROP TABLE IF EXISTS `cs_dustbin`; CREATE TABLE IF NOT EXISTS `cs_dustbin` ( `id` VARCHAR(45) NOT NULL COMMENT '主键自增id', `rfid_no` VARCHAR(20) NOT NULL COMMENT 'rfid 卡号', `state` INT(1) NOT NULL COMMENT '垃圾桶状态:0:已注销;1:未使用;2:待使用;3:已使用(绑定收集点);', `user_id` INT NOT NULL COMMENT '登记人,负责录入垃圾桶的人', `type` INT(1) NOT NULL DEFAULT 1 COMMENT '垃圾桶类型:1:餐厨垃圾桶', `street_code` INT(11) DEFAULT NULL COMMENT '所在镇街 code,根据状态,这里的含义多是领用镇街、退还镇街。', `create_time` DATETIME NOT NULL DEFAULT now() COMMENT '建立时间', `update_time` DATETIME NOT NULL DEFAULT now() COMMENT '更新时间', `ext` VARCHAR(1000) NOT NULL DEFAULT '{}' COMMENT '扩展字段', ... PRIMARY KEY (`id`)) ENGINE = InnoDB COMMENT = '垃圾桶表';
Java 代码:工具
import com.alibaba.fastjson.JSON; import lombok.Data; import javax.validation.constraints.NotNull; import java.util.Date; import java.util.List; /** * 垃圾桶实体 * Created by Blink on 6/28/2018 AD. * * @author Blink */ @Data public class Dustbin { private String id; /** * rfid 卡号 */ @NotNull private String rfidNo; /** * 垃圾桶状态:0:已注销;1:未使用;2:待使用;3:已使用(绑定收集点); * 对应 Dustbin.StateEnum 类 */ @NotNull private Integer state; /** * 录入垃圾桶的人员id */ @NotNull private Long userId; /** * 垃圾桶类型:1:餐厨垃圾桶 * DefaultValue: 1 */ @NotNull private Integer type; /** * 所在镇街 code * 根据状态,这里的含义多是领用镇街、退还镇街 */ private Integer streetCode; /** * 建立时间 * defaultValue : now() */ @NotNull private Date createTime; /** * 更新时间 */ @NotNull private Date updateTime; /** * 扩展字段,详细数据查看 DustbinExt.java * DefaultValue: {} */ private String ext; ... public DustbinExt getExtObject() { return JSON.parseObject(this.getExt(), DustbinExt.class); } public void setExtObject(DustbinExt ext) { this.ext = JSON.toJSONString(ext); } /** * 垃圾桶扩展属性 * Created by Blink on 6/28/2018 AD. * * @author Blink */ @Data public static class DustbinExt { /** * 所在镇街 * 根据状态,这里的含义多是领用镇街、退还镇街、绑定的镇街 */ private String street; /** * 客户(收集点)id,绑定收集点的时候须要填入 * 根据目前的需求(2018-06-29),当收集点解绑的时候 * 须要保存垃圾桶最新绑定收集点名称,因此在解绑垃圾桶的时候不会把这个信息删掉 * 只有当绑定收集点的时候才把他覆盖 */ private Long customerId; /** * 客户(收集点)名称,绑定收集点的时候须要填入 * 根据目前的需求(2018-06-29),当收集点解绑的时候 * 须要保存垃圾桶最新绑定收集点名称,因此在解绑垃圾桶的时候不会把这个信息删掉 * 只有当绑定收集点的时候才把他覆盖 */ private String customer; /** * 损坏部位 * 1:桶盖;2:桶口;3:桶身;4:桶轴;5:桶底;6:桶轮; * 对应 DustbinDamagePartEnum 类 */ private List<Integer> parts; } ... }
mysql 脚本能够看到扩展字段的信息:this
ext VARCHAR(1000) NOT NULL DEFAULT '{}' COMMENT '扩展字段'
能够看到这么一段 Java 代码:设计
... /** * 扩展字段,详细字段查看 DustbinExt 类 * DefaultValue: {} */ private String ext; public DustbinExt getExtObject() { return JSON.parseObject(this.getExt(), DustbinExt.class); } public void setExtObject(DustbinExt ext) { this.ext = JSON.toJSONString(ext); } ...
能够看到 ext
字段就是用来存储 json 格式的数据,它能够动态地增长任何字段,甚至是对象,不须要经过 DDL(Data Definition Language) 去建立字段,很是适合用来解决上面提到的问题。
Java 代码在这里起到辅助性做用,经过定义一个内部类来管理扩展字段的属性,方便咱们了解和管理扩展字段,提升代码的可读性和可维护性,java 这种方式也是笔者总结出来的较为优雅的作法(我的观点)。
有经验的读者可能会提出,ext
字段在 Mysql 5.7.8 如下版本没法对扩展字段中的某一个或一部分字段创建索引,由于 Mysql 5.7.8 版本如下不支持(Mysql 5.7.8 支持为 Json Data Type 创建索引)。
没错,这是这个解决方案的一个局限性,在 Mysql 5.7.8 如下版本,个人建议是, ext 扩展字段不要存储热点数据,只存储非热点数据,这样就能够避免查询操做,下降维护 ext
字段带来的成本和风险,那如何识别新增字段是否是热点数据呢?这个须要结合实际业务需求来判断,也能够询问对业务和技术更有经验的同事,便于读者更快得出结论。
在一些极端的状况下,变化可能来得太快,而咱们要的是减小变化带来的成本和风险,因此在表设计之初能够根据自身经验,或者找更有经验的人寻求帮助,预估一下须要预留多少个备用字段,再配合扩展字段,基本上能够把改变(添加字段)表结构的次数降至一个很是少的次数。
在特殊状况下,经过扩展字段 + 预留字段基本上能够作到动态扩展字段,又不会影响为热点数据创建索引的状况,这样咱们获得了一个很是灵活的表结构,便于咱们应对将来的变化,可是请注意,要维护好咱们的实体,包括里面的每个字段,敬畏每一行代码。
近期热文推荐:
1.1,000+ 道 Java面试题及答案整理(2021最新版)
2.终于靠开源项目弄到 IntelliJ IDEA 激活码了,真香!
3.阿里 Mock 工具正式开源,干掉市面上全部 Mock 工具!
4.Spring Cloud 2020.0.0 正式发布,全新颠覆性版本!
5.《Java开发手册(嵩山版)》最新发布,速速下载!