数据模型设计
多年以来,关系型数据库已经成为了企业数据管理的基础,很多工程师对于关系模型和 6 个范式都比较了解,但是如今来构建和运行一个应用,随着数据来源的越发多样和用户量的不断增长,关系数据库的限制逐渐成为业务的瓶颈,因此越来越多的公司开始向其他 NoSQL 数据库进行迁移。
TDS 的数据存储后台大量采用了 MongoDB 这种文档数据库来存储结构化数据,正因如此我们才能提供面向对 象的、海量的、无需创建数据表结构即存即用的存储能力。从传统的关系型数据库转换到 TDS 或者 MongoDB 存储系统,最基础的改变就是「数据建模 Schema 设计」。
首先来梳理一下关系型数据库、MongoDB 和 TDS 数据存储的对应术语:
RDBMS | MongoDB | TDS 数据存储 |
---|---|---|
Database | Database | Application |
Table | Collection | Class |
Row | Document | Object |
Index | Index | Index |
JOIN | Embedded,Reference | Embedded Object, Pointer |
在 TDS 上进行数据建模设计需要数据架构师、开发人员和 DBA 在观念上做一些转变:之前是传统的关系型数据模型,所有数据都会被映射到二维的表结构「行」和「列」;现在是丰富、动态的对象模型,即 MongoDB 的「文档模型」,包括内嵌子对象和数组。
文档模型
后文中我们有时候采用 TDS 数据存储的核心概念 Object(对象),有时候提到 MongoDB 中的名词 Document(文档),它们是等同的。
我们现在使用的大部 分数据都有比较复杂的结构,用「JSON 对象」来建模比用「表」会更加高效。通过内嵌子对象和数组,JSON 对象可以和应用层的数据结构完全对齐。这对开发者来说,会更容易将应用层的数据映射到数据库里的对象。相反,将应用层的数据映射到关系数据库的表,则会降低开发效率。而比较普遍的增加额外的对象关系映射(ORM)层的做法,也同时降低了 schema 扩展和查询优化的灵活性,引入了新的复杂度。
例如,在 RDBMS 中有父子关系的两张表,通常就会变成 TDS 里面含有内嵌子对象的单文档结构。以下图的数据为例:
PERSON 表
Person_ID | Surname | First_Name | City |
---|---|---|---|
0 | 柳 | 红 | 伦敦 |
1 | 杨 | 真 | 北京 |
2 | 王 | 新 | 苏黎世 |
CAR 表
Car_ID | Model | Year | Value | Person_ID |
---|---|---|---|---|
101 | 大众迈腾 | 2015 | 180000 | 0 |
102 | 丰田汉兰达 | 2016 | 240000 | 0 |
103 | 福特翼虎 | 2014 | 220000 | 1 |
104 | 现代索纳塔 | 2013 | 150000 | 2 |
RDBMS 中通过 Person_ID 域来连接 PERSON 表和 CAR 表,以此支持应用中显示每辆车的拥有者信息。使用文档模型,通过内嵌子对象和数组可以将相关数据提前合并到一个单一的数据结构中,传统的跨表的行和列现在都被存储到了一个文档内,完全省略掉了 join 操作。
换成 TDS 来对同样的数据建模,则允许我们创建这样的 schema:一个单一的 Person 对象 ,里面通过一个子对象数组来保存该用户所拥有的每一部 Car,例如:
{
"first_name":"红",
"surname":"柳",
"city":"伦敦",
"location":[
45.123,
47.232
],
"cars":[
{
"model":"大众迈腾",
"year":2015,
"value":180000
},
{
"model":"丰田汉兰达",
"year":2016,
"value":240000
}
]
}
文档数据库里的一篇文档,就相当于 TDS 平台里的一个对象。这个例子里的关系模型虽然只由两张表组成(现实中大部分应用可能需要几十、几百甚至上千张表),但是它并不影响我们思考数据的方式。
为了更好地展示关系模型和文档模型的区别,我们用一个博客平台来举例。从下图中可以看出,依赖 RDBMS 的应用需要 join 五张不同的表来获得一篇博客的完整数据,而在 TDS 中所有的博客数据都包含在一个文档中,博客作者和评论者的用户信息则通过一个到 User 的引用(指针)进行关联。