MongoDB 字段应该扁平还是结构化? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
qiayue
V2EX    MongoDB

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

  •  
  •   qiayue
    PRO
    2013-10-18 09:44:07 +08:00 6252 次点击
    这是一个创建于 4377 天前的主题,其中的信息可能已经有所发展或是发生改变。
    搜索了一下,好像没找到多少相关资料。
    在谷歌 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
    PRO
       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     Solana     3939 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 00:56 PVG 08:56 LAX 17:56 JFK 20:56
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86