正所谓“完事开头难”,在设计技术方案时候,除了前期要做好背景调查、需求调研,开工动手的第一步就是做“数据建模”,也就是存储数据的结构设计,大部分时间是围绕关系型数据库进行的,少部分是在Redis上做K-V延伸,而ESMongoDBHive等几乎都是关系型数据库核心存储的副本,结构上基本保持同步。

数据建模大有讲究,除了基础的必知必会能够让我们少走弯路,不让技术方案显得不伦不类,还需要根据特定场景去进行一些巧妙设计。下面我们从基本部分聊聊常用的设计规范和原理,再从进阶部分展开下某些场景中数据建模的设计实践方法,下面我们主要围绕我们常用的核心存储 ———— 关系型数据库MySQL来论述下数据建模的种种CASE。

『基础』夯实底座,设计不扭曲

必带伙伴,缺一不可

[](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c60996dc343f4767bc954fa5a34f06c7~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp?)

当我们开始进行数据建模的时候,都会先将要表达/存储的数据进行分类、抽象,将其映射成一个个实体对象,然后剖解实体对象的属性,经历从现实到抽象从抽象到存储的两个演变过程完成。

对于关系型数据库来说,实体对象的属性就是数据库的字段,一般是一一对应的关系。对于字段而言,我们可以将其划分为必选字段业务字段辅助字段三类。

字段分类 字段属性 要求 作用
必选字段 主键(id) 递增、唯一 维护数据存储有序性
创建时间(create_time) 时间戳 记录数据创建时间
更新时间(update_time) 时间戳 记录数据最后更新时间binlog记录更新做数据副本拷贝时必不可少
删除时间(delete_time) 时间戳 记录数据删除时间
数据状态(status) 有效、无效 记录数据状态,逻辑删除标记
业务字段 对象属性映射 - 记录业务数据
辅助字段 扩展字段(ext) 一般为字符串,存储JSON格式 记录字段之外的扩展信息,可以根据业务事实表的复杂度扩展
业务状态机(biz_status) 状态机枚举 根据业务事实表进行枚举,驱动数据流转
业务节点时间(xxx_time) 时间戳 有一些业务对时间过程记录非常苛刻,需要根据业务节点进行记录,也可以把一些核心时间记录字段为审计字段

理论上数据库的表字段允许存储的数量足够满足业务诉求,但是我们并不推荐无限在一张表上进行无脑扩张,因为行数据的存储也依赖表字段设计的数量大小,过多的单表字段堆积会降低检索效率,如业务场景丰富且复杂,建模表字段过于多,应该适当进行垂直拆分到多个表中,通过业务字段进行关联。

逻辑删除和物理删除

[](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/31385c82c60343f28576e22b762d5000~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp?)

对于数据的可用性来说只有有效和无效之分,对于无效数据来说,当用户点击删除键而且对提示窗口明确点击确认那一刻开始,它就已经成为历史不会再被使用了,对于不再使用的数据再继续进行存储的话的确是没有任何意义的,一般我们可以选择将其永久从磁盘删除掉。

而特别有意思的是,对于MySQLB+Tree而言,顺序添加数据的成本最低对于树的维护是最友好的,而乱序插入、修改、删除由于变更需要维护树的有序性都会一定程度上造成性能损耗,是比较笨重的操作,特别是在这个树繁衍得非常庞大达到一定数量级时这种删除操作更是不推荐使用。 一般而言,我们会通过一个特殊字段来标记该记录的数据有效性,删除操作并不是直接Delete掉数据库中的的持久化数据,而是通过数据层的标记让其”失效“而不影响物理层存储的结构变化,这就是所谓的”逻辑删除“。

其实对于MySQL而言,进行Delete操作并不会立即对B+Tree数据进行物理清除操作,也是先标记失效,而且硬删除也会影响整颗排序树的结构变化,在业务高频场景下会造成一些性能或“雪崩”风险。

对于硬盘存储来说,整体的数据库表空间文件体积不会因为删除了数据而减少表空间体积,除非进行重建操作进行碎片整理来释放历史空间碎片,而一般不会频繁做这种重建操作,因此业务操作的“删除”不要直接映射理解成“物理删除”,而是在技术层采用“逻辑删除”进行处理,逻辑屏蔽数据即可,因为两种处理对于数据库来说没有太大差别和收益,而对于应用业务来说,“逻辑删除”可以帮助业务进行数据留痕、溯源,会有一定收益价值。

主键规则只有一条

[](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d2cdb5187e8042aca1c933300f9998a7~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp?)

对于MySQLOracle等关系型数据库而言,存储数据最基础的概念之一是要理解什么是数据聚簇,相关概念面试时会问到什么是聚簇索引?什么是非聚簇索引?它们的区别是什么?

真实数据挂载的索引树我们一般称之为聚簇索引,因为数据存储都是挂载在这颗B+Tree上进行组织构建的,而维护这颗树节点顺序的就是主键

因此,主键规则只有一条,那就是维护聚簇索引这颗B+Tree上挂载数据的有序性,除此之外不太应该也不推荐把更多职责能力强加到其身上。开发应用过程中我们都会给表定义一个叫做Id的主键,其意义就在于此。在一些关系型数据库中表的主键也并非是必须要明确定义,数据库可以隐式地使用rowId等来充当表主键角色。