V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐工具
RoboMongo
推荐书目
50 Tips and Tricks for MongoDB Developers
Related Blogs
Snail in a Turtleneck
qiayue
V2EX  ›  MongoDB

MongoDB 字段应该扁平还是结构化?

  •  
  •   qiayue · 2013-10-18 09:44:07 +08:00 · 5959 次点击
    这是一个创建于 4041 天前的主题,其中的信息可能已经有所发展或是发生改变。
    搜索了一下,好像没找到多少相关资料。
    在谷歌 JSON 风格指南 https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md 中说:
    JSON中的数据元素应以扁平化方式呈现。不能为了方便而将数据任意分组。

    考虑到 MongoDB 也是以类 JSON 格式存储数据,那么,在 MongoDB 中的字段是否也应该是扁平化的呢?
    13 条回复    1970-01-01 08:00:00 +08:00
    breeswish
        1
    breeswish  
       2013-10-18 09:53:18 +08:00   ❤️ 1
    每个小颗粒单位尽量扁平化,如果有整体小数据则可以在这里结构化。(因为MongoDB的查询和处理语言对于扁平化很方便,对于结构化的数据不方便)
    Ricepig
        2
    Ricepig  
       2013-10-18 11:21:01 +08:00   ❤️ 1
    结构化和分组的目的只有一个,那就是可读性吧
    shiny
        3
    shiny  
       2013-10-18 11:23:36 +08:00   ❤️ 1
    这个命题比一般人想象的更复杂,这个不应该参照普通 JSON 数据来。
    应该搜索 「MongoDB 范式化 反范式化」。
    举个我的血泪栗子:内嵌的 JSON 如果频繁更新,它就不应该是内嵌的,否则会有性能问题。
    hepochen
        4
    hepochen  
       2013-10-18 11:48:11 +08:00   ❤️ 1
    JSON是JSON, MongoDB是MongoDB。

    不论是扁平还是多层内嵌,是个人的选择问题;但只有一个前提,就是不能影响数据库的性能。

    扁平相对而言是会简单一些,内嵌的,具体场景中,性能反倒会更高。

    因为在MongoDB的底层存储中,对内嵌文档的处理并非是大家想象中的那般结构化,但比如要更新`collection.field1.field2.field3.field5`这到底更新了哪段信息?如果我有个字段名就是`field1.field2`的联合呢?

    @shiny 内嵌文档的大小也在频繁变化么?
    turing
        5
    turing  
       2013-10-18 11:52:03 +08:00   ❤️ 1
    我觉得嵌入文档还是分表存取决于数据库是主要是用来读还是写的

    如果仅是展示用,比如从其他地方获取结构化的数据,保存在单一集合里,不用修改的方式,内嵌文档很好用,也很方便,如果频繁操作修改内嵌文档,就不太方便了。
    shiny
        6
    shiny  
       2013-10-18 11:55:44 +08:00   ❤️ 1
    除了性能,还要考虑需求实现是否方便。比如涉及日期范围的,用内嵌将会很痛苦(比如很多人把日期当做键而不是一个值……),操作内嵌文档,大多数命令也和范式化的不同。尤其是为了简洁,没什么多余字段的内嵌文档。
    其次是大尺寸内嵌文档更难控制需要的字段,以前线上发现 tornado 内存不断增长却找不到原因,最后谨慎选择了下字段排除了不需要的、数据量大的字段就好了。

    @hepochen 我的情况是不断 insert 的数据,卡得掉眼泪。
    qiayue
        7
    qiayue  
    OP
       2013-10-18 12:49:40 +08:00
    @breeswish @Ricepig @shiny @hepochen @turing @shiny 多谢!
    所以还是要看具体的业务场景,并且扁平化一些,性能好些
    hepochen
        8
    hepochen  
       2013-10-18 12:56:22 +08:00   ❤️ 3
    呃,@shiny 同学,你可以考虑请我吃饭了。

    用日期作为key,也未必是坏事。比如用作统计的,而且年/月/日这些固定的(非range性质)的,可以用正则的前向匹配,同时,也是可以使用索引的。所以,这反而是一个高效的处理办法。

    但有一个问题,比如`2012-09`这样的key来存储一个月的数据(按天),那么,(有人)推荐的做法,是创建这个document的时候,就填充好31天的空数据。

    呃,为什么是`2012-09`而不是`2012-9`呢? 这跟`2012-10`对比,会产生大小的问题,也可以很漂亮地解决时间被str化后的排序问题。

    - - - - -

    所有的字段如何控制,都是个人的一种选择。但是内嵌字段中出现一些特殊值,比如`$`开头的, 带`.`的,则要对MongoDB要更了解一些,不然很容易入坑。

    - - - -

    用内嵌文档,不会产生性能的问题。

    用内嵌文档,并且不断insert,这会是性能问题产生的原因。

    MongoDB是文档型的数据库,一个文档被创建的时候,默认会有保留多一点的空区域(这种机制,也会MongoDB等NoSQL数据库比较占磁盘空间的原因),可以方便以后的更新操作。但是,这个区域占满之后,磁盘空间就需要重新分派(这是极低效的)。

    如果这是过程中,有多一点量的写操作涌进来,呃,肯定会“卡得掉眼泪”了。
    zack
        9
    zack  
       2013-10-18 13:05:08 +08:00   ❤️ 1
    从存储空间的占用角度来看,尽量优先使用扁平化
    shiny
        10
    shiny  
       2013-10-18 13:05:19 +08:00   ❤️ 1
    @hepochen 谢谢
    1、年月日我们是涉及一定范围内的统计的,更别说什么排序之类的,除非把数据全部取出来自己排。而如果是范式化的,就有现成的——当然数据处理不应该交给 MongoDB,这个时候才念起 SQL 的好来。
    而且时间久了有可能出现巨大的内嵌文档(当然这个是设计错误,但是处理起来范式化的设计更容易处理点)。

    2、字段是指有的字段包含大量数据,而不是说特殊字符。包含大量数据的字段,而且不是 PHP 这类用完就销毁的语言很可能内存爆满。范式化也有个好处是精准控制需要的数据。

    3、我是在翻书后才知道,原话是「不断增长的内嵌文档将导致 IO 瓶颈」。如果 update 数据增长,也会是有问题的。当时我就一个进程后台默默写 MongoDB,也差不多写到了 IO 瓶颈——当然,数据量越大卡得越明显,刚设计完的时候还没发现。
    shiny
        11
    shiny  
       2013-10-18 13:18:36 +08:00   ❤️ 1
    @hepochen 有时需求很难实现,我会借助 map reduce,好像内嵌文档的 map reduce 比范式化的不易?

    因为就一个人搞技术,情况复杂时有问题也没人讨论。

    反正经历过这么一次后,把数据放 MongoDB 是谨慎又谨慎了。
    breeswish
        12
    breeswish  
       2013-10-18 17:25:16 +08:00
    @shiny 很多东西要试了才知道
    goofansu
        13
    goofansu  
       2013-10-19 01:10:11 +08:00 via iPhone
    有本书叫mongodb design pattern,觉得还不错
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2798 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 08:47 · PVG 16:47 · LAX 00:47 · JFK 03:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.