EMC颜开纵论NoSQL文档数据库反模式

SQL的反模式已经有不少人熟悉,但NoSQL是否也有反模式?

在自己的一篇博客文章中,EMC中国研究院的颜开对NoSQL中的文档数据库进行了分析,并提炼出六种反模式。

在文章开头,颜开指出:

好的反模式可以在我们设计Schema告诉哪里是陷阱和悬崖。NoSQL宣传的时候往往宣称是SchemaLess的,这会让人误解其不需要设计Schema。但如果不意识到设计Schema的必要,陷阱就在一直在黑暗中等着我们。

接下来,颜开分析了三种主流的NoSQL数据库:

  • 文档数据库,以MongoDB为代表
  • 列存数据库,以HBase为代表
  • 键值数据库,以Redis为代表

加上MySQL,他将这四种开源数据库产品做了比喻和对比:

MySQL——菜刀

尽管其没有什么大的改进,但是新兴的互联网使用的最多的数据库。就像传统的菜刀,结构简单,几百年没有改进。但是不妨碍产生各式各样的刀法,只要有一把,就能胜任厨房里的大部分事务。MySQL也是一样,核心已经稳定。但是切库,分表,备份,监控,等等手段一应俱全。

MongoDB——瑞士军刀

提供更灵活的Schema,Capped Collection,异步提交,地理位置索引等五花十色的功能。就像瑞士军刀,不但可以当刀用,还可以开瓶盖,剪指甲。但是他也不比MySQL强,因为 还缺乏时间的磨砺。一是系统本身的稳定性,二是开发,运维需要更多经验才能流行。

HBase——大象兵

依仗着Hadoop的生态环境,可以有很好的扩展性。但是就像象兵一样,使用者需要养一头大象(Hadoop),才能驱使它。

Redis——金箍棒

键值存储的代表,功能最简单。提供随机数据存储。就像一根棒子一样,没有多余的构造。但是也正是因此,它的伸缩性特别好。就像悟空手里的金箍棒,大可捅破天,小能成缩成针。

颜开指出SQL关系模型的两个弱点:

  • 必须支持Join:因为数据不能够有重复。所以使用范式的关系模型会不可避免的大量Join。如果参与Join的是一张比内存小的表还好。但是如果大表Join或者表分布在多台机器上的话,Join就是性能的噩梦。
  • 计算与存储耦合:关系模型作为统一的数据模型既可以用于数据分析,也可以用于在线业务。但这两者一个强调高吞吐,一个强调低延时,已经演化出完全不同的架构。用同一套模型来抽象显然是不合适的。

颜开认为:

针对这两个梦魇。文档数据库如MongoDB的主要目的是:提供更丰富的数据结构来抛弃Join来适应在线业务

……文档数据库并不比关系数据库强大,由于对Join的弱支持,功能会弱许多。设计关系模型的时候,通常只需要考虑好数据直接的关系,定义数据模 型。而设计文档数据库模型的时候,还需要考虑应用如何使用。因此设计好一个的文档数据库Schema比设计关系模型更加的困难。除此之外,由于文档数据库 事务的支持也是比较弱,一般NoSQL只会提供一个行锁。这也给设计Schema更加增加了难度。

接下来,颜开从模型设计部分提炼出六种反模式。

反模式一:惯性思维/沿用关系模型

由于文档数据库不支持Join(包括和外键息息相关的外键约束)等特性,习惯性的沿用关系模型有的时候会出现问题。需要利用起文档数据库提供的丰富的数据模型来应对。

颜开提出一个例子,指出这个例子存在两个问题:

  1. 存在描述多对多的关系表
  2. 没有区分”一对多关系”和“多对一关系”

颜开提出一个正确使用的场合:

关系型模型是非常成功的数据模型,合理的沿用是非常好的。但是由于文档数据库的特点,需要适当的调整,这样得出的数据模型,尽管性能不是最优,但是有最好的灵活性。并且也有利于和关系数据库转换。

反模式二:处处引用客户端Join

这样的坏处是:

  1. 手动Join,麻烦且易出错。
  2. 多次查询。如果引用过多,查询的时候需要多次查询才能查到足够的数据。
  3. 事务处理繁琐。

解决方法是:

在以下三种场合,适当使用内联数据结构。

  1. 使用内联可以解决读性能问题,明显减少Query的次数的时候。
  2. 可以简化数据模型,化简表之间的关系,而同时不会影响灵活性的时候。
  3. 事务可以得到简化为单行事务的时候

正确的场合是:

范式化的使用场景,文档数据库会被多个应用使用。由于数据库设计无法估计多个应用现在及将来的查询情况,需要极大的灵活性。在这个时候,使用引用比内联靠谱。

反模式三:滥用内联后患无穷

有四种主要症状:

  • 妨碍到查询的内联
  • 无限膨胀的内联
  • 无法维护的内联
  • 盯死应用的内联

反模式四:在线计算

主要症状:有一些运行时间很长的Query,由于有聚合计算,索引也不能解决。随着数据量的增长,逐渐成为性能瓶颈。

坏处有二:

  • 影响用户体验。
  • 影响数据库性能。

解决方案:要权衡聚合操作是否必要,是否需要实时完成。如果务必要,可采取离线模式。如必须实时,则可以新建一个字段,用“incr”这样的操作, 在运行的时候,实时聚合结果。如果逻辑比较复杂,或者觉得大量“incr”操作给数据库系统带来了压力,可以使用Storm之类的实时数据处理框架。总 之,要慎用长查询。

反模式五:把内联Map对象的Key当作ID用

其症状是:

文档数据库支持内联Map类型。将其中Map的Key当作数据库的主键来用。

颜开指出:“这个反模式很容易犯,因为在编程语言中Map数据结构就是这么用的。但是对于数据库模型来说,这是不折不扣的反模式。”

主要坏处有二:

  • 无法通过数据库做各种(><=)查询。
  • 无法通过索引查询。

解决方案:使用数组+Map来解决。。Map类型中的Key是属性名,Value是属性值。这样的用法是文档数据库数据模型的本意,因此其提供的各种功能才能利用上。否则就无法使用。

反模式六:不合理的ID

症状:使用String甚至更复杂数据结构作为的ID,或者全部使用数据库提供的自生成ID

两个坏处是:

  • ID混乱。
  • 索引庞大,性能低下。

解决方案:

尽量使用有一定意义的字段做ID,并且不在其他字段中重复出现。不使用复杂的数据类型做ID,只使用int,long或者系统提供的主键类型做ID。

在文章最后,颜开用一个表格涵盖了所有反模式,读者可点击原文链接查看。

颜开最后说道:

现在关于NoSQL数据模型设计模式的讨论才刚刚起步,将来也许会逐渐自成体系。对于列数据库和Key-Value的反模式,笔者等到有了足够积累的时候,再和大家分享。

郑柯 郑柯,实用的理想主义者,相信:每天改变一点点,这个世界会更好。

This entry was posted in Best Practices. Bookmark the permalink.

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s