snp是前段时间开始的一个个人项目,受Chrome导航插件 FVD SpeedDial的启发,做一个自定义的导航页,后面会陆续写一些这个项目的文档,今天主要是总结一下到现在为止,MongoDB设计的一些失误。
snp使用MongoDB作为数据存储,MongoDB作为一个老牌的NoSQL数据库,比较适合这个项目的需求,之前一直是使用MySQL做数据库,虽然MySQL肯定也能满足需求,但是一来为了熟练一些MongoDB的使用,而来NoSQL存储对应的数据确实更加方便,就没有选用MySQL了;一开始设计时还考虑过使用SQL Lite做存储,因为SQL Lite足够小,部署比较方便,不过snp本来是一个练手的项目,学习MongoDB也是其中目的,也就没有使用SQL Lite了。
数据描述
我们知道NoSQL的一个很大的优势是没有了关系型数据库的模式约束,可以在一个数据集合中快速存取不同“schema”的数据。
先介绍一下snp中主要使用的数据,如果使用关系型数据库描述,它们主要包括:
package数据
字段 | 类型 | 备注 |
---|---|---|
Id | string | ObjectID |
Created | string | 创建事件 |
Title | string | 标题 |
Owner | string | 创建人 |
Name | string | 名字 |
Password | string | 密码 |
Public | bool | 是否公开 |
Groups | []ObjectID | 组的 ObjectID 数据 |
Theme | string | 主题 |
AdditionalStyle | string | 附件样式表 |
Version | int | 版本 |
应为使用MongoDB,id就是不是MySQL的自增主键了,而是MongoDB自动生产的hash过的id字符串,上表中需要注意的是保存Groups的数组,我们知道NoSQL可以在一个字段存储数组,对应于MongoDB则是类似于JavaScript数组一样的数据类型。
这里为什么不会直接存储Group的对象数组而是存储Group的id的数组呢?这是因为snp的设计中Group是可以包间共享的,也就是follow的概念,所以这里是使用ID的数组。
我现在觉得设计失误的是组和网站的数据集合,由于常年使用关系型数据库(MySQL)的影响,在设计时使用就得思维方式,将Site单独抽象出来了一个集合。这其实是没有必要的,虽然一开始考虑过Site在Group之间的共享,但是后来发现这样是没有意义的,因为不同Group添加的网址有自己的一些属性,如添加时间,特别是组可能会编辑网址,这样如果网址共享的话,会导致一个地方修改,所有人的导航页都被修改了,这显然是不合理的。另一个方案是,可以借鉴 写时复制,在编辑时对Site做一份拷贝,这样可以减少系统中重复网址的开销,不过这样实现有些麻烦,并且省下来的那些空间并没有多少,反而导致过多的计算。
下面看一下现在的Group和Site设计
Group和Site
Group数据
字段 | 类型 | 备注 |
---|---|---|
Id | string | ObjectID |
Created | string | 同上 |
Title | string | 同上 |
Owner | string | 同上 |
GroupLink | []ObjectID | 备用 |
Followable | bool | 是否可Follow |
Localized | bool | 是否本地 |
FollowFrom | ObjectID | 从某Follow |
AdditionalStyle | string | 同上 |
Sites | []ObjectID | 包含的网址 |
Version | int | 版本 |
Site数据
字段 | 类型 | 备注 |
---|---|---|
Id | string | ObjectID |
Title | string | 同上 |
Url | string | 链接 |
Created | string | 同上 |
Available | bool | 备用 |
可以看到这里设计的失误,Group中不应该存储Site的ID数组,而应该直接存储Site的对象数组,因为Site实际就是Group的成员。现在这样的设计导致在查找Group的所有网站时,需要进行更多次的数据库查找,在查找package的所有网站时导致查询次数成倍增加!!
但是现在已经写了不少的接口,再进行修改需要重写那些功能,实在是麻烦,只能将错就错了。。。