MongoDB数据模型和索引学习



  • 1. MongoDB数据模型:

    • MongoDB数据存储结构: MongoDB针对文档(大文件采用GridFS协议)采用BSON(binary json,采用二进制编码)数据格式来存储和交换数据。Bson吸收了JSON schema-less的特点,存储结构松散,不需要像RDB(关系数据)那样事先定义数据存储的元数据结构,另外增加了多种数据类型的支持和优化,使读写更加高效。 (1) BSON 支持的数据类型:

      Double、String、Object、Array、Binary Data、Undefined、Object id、Boolean、Date、Null、Regular Expression、JavaScript、Symbol、JavaScript(with scope)、32-bit integer、Timestamp、64-bitInteger、Min key、Max key
      

      (2) BSON 在表现形式如下:

      { "_id" : ObjectId("542c2b97bac0595474108b48"), "ts" : Timestamp(1412180887, 1),"name":"steven"}
      

      (3) BSON 是MongoDB中的通信协议和数据存储格式: 在MongoDB中客户端和服务端通信采用的是BSON的文档格式,例如查询一段数据,需要这样写:

      db.steven.find({"name":"steven"})
      

      更新一段数据需要这样写:

      db.steven.update({"name":"steven"},{$set:{"name":"jianying"}})
      

      删除一段数据需要这样写:

      db.steven.remove({"name":"steven"})
      

      总之MongoDB中针对文档的CRUD的RPC通信格式均支持采用了BSON的数据格式。并且其存储格式也采用了BSON的格式类似:

      { "_id" : ObjectId("542c2b97bac0595474108b48"), "ts" : Timestamp(1412180887, 1),"name":"steven"}
      

      (4) BSON数据格式的编码: BSON的String类型均采用UTF-8编码,其中KV结构中 K值 和 字符串类型的V值,均采用UTF-8格式编码。如果使用的是其他格式则需要转码。并且针对K 值可以采用除以下要求外的任意UTF-8字符:

      a.键不能含有\o(空字符)
      b.$和.有特殊的含义,只有在特定环境下采用使用
      c.以下划线"_"开头的键是保留的(不是严格要求的)
      

      而其它值类型的编码则按照具体数据类型的内置协议编码。MongoDB在数据模型的组织方式上,支持文档的引用和嵌套。具体介绍如下。

    • 数据模型设计模式 - 引用 和 嵌套: 以引用的方式存储数据是一种MongoDB组织数据存储结构的模式,即一个文档中存储了检索另一个文档需要的必要信息,举例如下:

      {
         _id: "joe",
         name: "Joe Bookreader"
      }
      {
         patron_id: "joe",
         street: "123 Fake Street",
         city: "Faketon",
         state: "MA",
         zip: "12345"
      }
      

      上面的文档是用户joe的信息,而下面那个文档则记录了他的地址信息,要根据joe的name检索地址信息,则需要先检索第一个文档,然后再检索第二个文档。而设计成 嵌套模式则表现为:

      {
         _id: "joe",
         name: "Joe Bookreader",
         addresses: [
                      {
                        street: "123 Fake Street",
                        city: "Faketon",
                        state: "MA",
                        zip: "12345"
                      }
                    ]
       }
      

      这两种设计模式的均有各自的优缺点,引用模式被认为是规范化的模式,减小了数据存储的冗余,结构设计清爽简单。符合我们一般设计原则,但是要获取完整数据的通信开销比较大,而且多个文档操作的原子性在MongoDB层面无法保证。 而被认为非规范化的嵌套设计模式,则具备相反的特性,其有点是减少了通讯的成本,并且原子性在单条文档得以保证,缺点就是数据存在冗余。选择哪种数据组织方式其实是一种权衡(trade-off)。

    • 注意点: (1) MongoDB 文档的大小必须小于16M,超过这个大小的话,要考虑使用GirdFs。 (2) 加入的文档大小超出原先分配给它的空间,MongoDB会把这个文档移动到磁盘的另外一个位置。迁移文档比原位更新更要耗时,也会因此导致磁盘碎片问题。 (3) 在MongoDB里面,操作的原子性级别保证到 document级别。 (4) Bson 字符串采用UTF-8编码。

    2. MongoDB索引结构:

    • MongoDB支持索引的类型: MongoDB采用B树的结构来组织索引(有效的支持等值查询和范围查询),支持针对文档中任意字段构建索引,不论是单值、数组、文本、嵌套结构的字段,均可构建索引。MongoDB 针对BSON存储格式是一种全索引的支持策略。面对多而强大的Mongo索引,索引的设计对性能的提升有比较大的影响。目前最新MongoV3.0版本支持的索引类型有如下几种:

      索引类型           简述   
      Default _id       默认ID索引:Mongo默认构建唯一性索引的id字段,每个文档都有一个_id字段。
      Single Field      单值索引:针对文档的某一字段或或嵌套文档的某一字段构建索引。
      Compound Index    组合索引:将多个字段放在一起构建索引,字段索引间组成上下层的树形结构。
      Multikey Index    多值索引:针对数组类型的索引结构,为数组的每个值建立一个索引。
      Geospatial Index  地理位置索引: 针对地理坐标结构,构建索引,能高效定位坐标范围,属额外福利。
      Text indexes      文本索引:类似搜索引擎的文本检索,涉及到分词操作,可惜不支持中文,而且查询语法的支持相对单一。
      Hashed Indexes    哈希索引:为了支持 基于Hash的Sharding(一种部署方式)而生,只支持等值检索,不支持范围检索。
      

      以上介绍了索引的类型,而不同类型的索引又可以带有以下属性,间接如下:

    • 索引的属性:

    (1) 唯一索引: 和RDB(关系型数据库)的唯一性索引的概念一致,为了避免出现重复的值而设计。 构建方式如:

        db.members.createIndex( { "user_id": 1 }, { unique: true } )
    

    (2) 稀疏索引: 稀疏索引的稀疏性体现在,其只为那些包含索引字段的文档构建索引Entry。忽略那些不包含索引字段的文档。 构建方式如:

        db.addresses.createIndex( { "xmpp_id": 1 }, { sparse: true } )
    

    (3) TTL索引: TTL顾名思义是生命周期的意思,即存储的document存储带有过期时间属性,超过生命周期自动删除,像日志数据、系统自动产生的临时数据、会话数据等均符合这一场景。 构建方式如:

        db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 3600 } )
    
    • 索引结构和特性:

    (1) B树结构,顺序存储:MongoDB的索引均采用B树的结构组织,支持高效的等值查询和范围查询。且内部索引项(entry)是默认有序的,可以天然保证返回结果有序。 (2) 索引的排序:构建索引是可以指定索引项是按照升序或降序构建,升序或降序的选择对于单值索引来说是等效的,但是对于组合索引则不等学效,组合索引被组织成上下级的树形结构,升序或降序选择错误,会对性能产生较大影响。 (3) 索引的交集:2.6版本以后,索引的查询优化策略支持索引的交集,可以将多条索引组合来使用,最高效的检索数据。例如可以构建两条单独的索引,当查询条件关联到这两条索引的时候,索引优化计划会自动组合这两条索引来检索。 例如构建了如下2条索引:

    { qty: 1 }
    { item: 1 }
    

    则以下查询语句会命中以上两条索引:

    db.orders.find( { item: "abc123", qty: { $gt: 15 } } )
    

    另外索引的交集和包括:

    索引的前缀交集:主要针对组合索引,查询计划会优化组合索引的前缀来查询。
    
    • 索引分析方法:

    (1) 评估RAM容量,尽量保证索引在内存中: 查询索引大小的命令(单位是字节):

    db.collection.totalIndexSize() 
    db.collection.stats()
    

    (2) 分析查看索引的计划:

    MongoDB中使用explain和hint可以查看索引的策略:

    db.collection.find().explain()
    

    可以看出那条索引策略生效,以及索引交集的使用情况。

    db.collection.find().hint({"name":1})
    

    hint的命令则可以指定强制使用某条索引。

    (3) 索引的管理信息: 每个DB下面都会有一个system.indexes集合,这个集合记录着DB下,索引构建的元数据信息。

    db.system.indexes.find()
    
    • 注意点:
      (1) 每个索引需要至少8K的空间。 (2) MongoDB 会对 _id字段自动创建唯一索引。 (3) 一个特别的索引类型支撑了TTL集合的实现,TTL依赖一个在Mongod中的后台线程,该线程读取索引中日期类型的值并从集合中删除过期的documents。

登录后回复
 

与 萌阔论坛 的连接断开,我们正在尝试重连,请耐心等待