做者:李瑜寧
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開發手冊(嵩山版)》最新發布,速速下載!