NoSQL反模式 - 文档数据库篇(2)
id | user_id | blog_id |
0 | 0 | 0 |
1 | 0 | 1 |
坏处:
破坏数据完备性。由于ID是主键,在数据模型上没有约束来保证不出现重复的user_id,blog_id对。一旦数据出现重复,更新删除都是问题。索引过多。由于是关系表,必须在user_id和blog_id上面分别建一个索引。影响性能。
解决方案:使用文档数据库典型的处理多对多的办法。不是建立一张关系表,而是在其中一个文档(如User)中,加入一个List字段。
user_id | user_name | blog_id[] | …… |
0 | Jake | 0,1 | …… |
1 | Rose | 1,2 | …… |
问题二:没有区分"一对多关系"和“多对一关系”症状:关系模型不区分“一对多”和“多对一”,对于文档数据库来讲,关系模型只有“多对一”。就像这张Comment表:
comment_id | user_id | content | …… |
0 | 0 | “NoSQL反模式是好文章” | …… |
1 | 0 | “是啊” | …… |
如果整个模型都是这样的“多对一”关系就需要反思了。
坏处:
额外索引。如果客户端已知user_id,需要获得User信息和Comment信息,需要执行两次查询。其中一次查询需要使用索引。并且要在客户端自己Join。这样可能有潜在性能问题。
解决方案:问题的核心在于是已知user_id查询两张表,还是已知comment_id查询两张表。如果是已知comment_id这样的设计就是合理的,但是如果是已知user_id来查询,把关系放在user表里的设计更合理一些。
user_id | user_name | comment_id[] | …… |
0 | Jake | 0,1 | …… |
1 | Rose | 1,2 | …… |
这样的设计,就可以避免一个索引。同理,对于多对多也是一样的,通过合理的安排字段的位置可以避免索引。
正确使用的场合:
关系型模型是非常成功的数据模型,合理的沿用是非常好的。但是由于文档数据库的特点,需要适当的调整,这样得出的数据模型,尽管性能不是最优,但是有最好的灵活性。并且也有利于和关系数据库转换。
反模式二:处处引用客户端Join
症状:数据库设计中充满了xx_id的字端,在查询的时候需要大量的手动Join操作。就涉及到了这个反模式。正如上面提到的博客的关系模型,如果已知blog_id查询comments,需要至少执行3次查询,并且手动Join。
坏处:
手动Join,麻烦且易出错。文档数据库不支持Join且没有外键保证。因此需要在客户端Join,这样的操作对于软件开发来讲是比较繁琐的。由于没有外键保证,因此不能保证取得的ID在数据库里面是有数据的。在处理的时候需要不断判断,容易出错。多次查询。如果引用过多,查询的时候需要多次查询才能查到足够的数据。本来文档数据库是很快的,但是由于多次查询,给数据库增加了压力,获取全部数据的时间也会增加。事务处理繁琐。文档数据库一般不支持一般意义上事务,只支持行锁。如果文档数据库有给多个连接。在插入的时候,事务的处理就是噩梦。在文档数据库中使用事务,需要使用行锁,在进行大量的处理。太过繁琐,感兴趣的读者可以搜一下。