当前位置:   article > 正文

《MongoDB权威指南》读书笔记 —— Part Ⅱ:设计应用(1)

《MongoDB权威指南》读书笔记 —— Part Ⅱ:设计应用(1)

第五章 索引

1 简介

  • 不使用索引的查询是全表查询,有索引的话则直接在索引中查找,然后跳转到目标文档的位置

  • 使用 explain( "executionStats" ) 查看MongoDB 在执行查询过程的细节,包括使用了哪个索引以及如何使用

    for (i=0; i<100000;i++) {
    	db.test.insert({
    		"i": i,
    		"username": "user" + i,
    		"age": Math.floor(Math.random()*120),
    		"created": new Date()
    	});
    }
    
    db.test.find({"username": "user101"}).explain("executionStats")
    /*
    ...
    "executionStats" : {
        "executionSuccess" : true,   // 执行是否成功
        "nReturned" : 1,             // 返回的文档数
        "executionTimeMillis" : 81,  // 执行的时间
        "totalKeysExamined" : 0,
        "totalDocsExamined" : 100000, // 总共检查了多少个文档
        "executionStages" : {
            "stage" : "COLLSCAN",
            "filter" : {
              "username" : {
                  	"$eq" : "user101"
               }
            },
            "nReturned" : 1,
            "executionTimeMillisEstimate" : 0,
            "works" : 100002,
            "advanced" : 1,
            "needTime" : 100000,
            "needYield" : 0,
            "saveState" : 781,
            "restoreState" : 781,
            "isEOF" : 1,
            "direction" : "forward",
            "docsExamined" : 100000
        	}
        },
    ...
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
  • 创建索引可以减少每次查询扫描文档的数量,大大地提升查询速度,代价是每次写操作都将更加耗时,因为不仅要更新文档还要更新集合上的所有索引

    db.collection.createIndex(keys, options)

  • 复合索引

    • 复合索引就是建立在多个字段上的索引,db.test.createIndex( { "age": 1, "username": 1 })
    • 每个索引条目包含 ageusername 字段,并指向文档在磁盘上的存储位置
    • 选择索引键的方向:1 - 升序,2 - 降序
    • 使用覆盖索引:即索引中包含用户请求的所有字段
    • 隐式索引:拥有 N 个键的复合索引相当于可以使用前 M(M < N) 个键组成的索引
  • 索引对象(即嵌套文档)和数组

    • 索引嵌套文档

      • 可以对单个子字段索引,使用 childDoc.field 形式
      • 也可以对嵌套文档本身索引,只会提高整个子文档的查询速度
    • 索引数组

      • 实际是对每一个元素建立一个索引条目(会导致更新操作代价高昂
      • 数组索引不包含位置信息,无法通过数组的index查找元素
      • 一个数组索引中的数组字段最多只能有一个
    • 多键索引

      • 某个索引键的值在文档中是数组,则该索引被标记为多键索引(isMultikey: true
      • 多键索引状态不会改变
      • 速度相对慢一些
  • 索引基数

    • 基数 cardinality:集合中某个字段拥有不同值的数量
    • 通常基数越高,索引越有用

2 使用 explain( ) 和 hint( )

  • explain( ):提供大量与查询相关的信息,对于任意查询都可在最后添加一个explain( )

  • hint( ): 强制MongoDB使用某个索引

  • 查询优化器

    • 如一个索引可以精确匹配一个查询,查询优化器直接使用该索引
    • 否则,可能多个索引适合,则从可能的索引子集中为每次查询计划选择一个,每个查询计划并行执行,先返回100个结果的优胜,其它查询计划中止并被缓存,直到集合数据发生大变动

3 索引通常适用的情况

  • 索引通常适用的情况
    • 集合较大
    • 文档较大
    • 选择性查询
  • 否则适合全表扫描

4 索引类型

  • 唯一索引

    • db.test.ensureIndex( { "username": 1 }, { "unique": true } ):确保集合的每个文档的指定键有唯一值
    • 插入含有重复键时会报错 E11000 duplicate key error index: ...
    • _id 就是唯一索引
    • 可以创建复合唯一索引,单个键的值可以相同,但所有键的组合值必须唯一
    • 如果集合中已经存在重复值时,创建唯一索引会失败,可以使用 db.test.ensureIndex( { "username": 1 }, { "unique": true , "dropDups": true } ) 去重
    • dropDups 会强制建立唯一索引,太粗暴,不可控,重要数据尽量避免使用
  • 稀疏索引

    • 唯一索引会把 null 看作值,因此无法插入多个缺少唯一索引中的键的文档
    • 可以使用 db.test.ensureIndex( { "username": 1 }, { "unique": true , "sprase": true } ) 创建稀疏索引,则该字段可选,但是存在该字段则必须唯一

5 索引管理

  • 创建的索引可以在 system.indexes 看到其元信息

  • 可执行 db.collection.getIndexes( ) 查看指定集合上的所有索引信息

    db.test.getIndexes()
    /*
    [
        {
            "v" : 2,
            "key" : {
                "_id" : 1
            },
            "name" : "_id_",
            "ns" : "test.test"
        },
        {
            "v" : 2,
            "key" : {
               "username" : 1
            },
            "name" : "username_1",
            "ns" : "test.test"
        },
        {
            "v" : 2,
            "key" : {
                "age" : 1,
                 "username" : 1
            },
            "name" : "age_1_username_1",
            "ns" : "test.test"
        }
    ]
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
  • 标识索引:使用 db.test.ensureIndex({" age": 1, "username": 1}, {"name": "newIndexName"}) 自定义索引名称

  • 修改索引:使用 db.test.dropIndex("name") 删除不需要的索引

第六章 特殊的索引和集合

1 固定集合

  • 基本概念

    • “普通”集合是动态创建的,会自动增长以容纳更多的数据
    • 固定集合的大小固定,集合空间满后插入文档则会自动删除最老的文档
    • 固定集合不可分片
    • 固定集合可用于记录日志
  • 创建固定集合

    • db.createCollection("my_collection", {"capped": true, "size": 1000, "max": 100}):创建大小为 1000 字节的固定集合,max 指定文档的最大数量
    • db.runCommand({"convertToCapped": "test2", "size": 1000}):将常规集合转为固定集合
    • 固定集合创建之后不可变,只能进行删除操作
  • 自然排序

    db.my_collection.find().sort({ "$natural": 1 }):自然排序即文档的插入顺序

  • 循环游标:只能用于固定集合

  • 没有 _id 索引的集合

    • 默认每个集合都有 _id 索引
    • 创建集合时设置 autoIndexId: false 会创建没有 _id 索引的集合,不建议如此操作

2 TTL 索引

  • 具有声明周期的索引(time-to-live index)

  • TTL 索引为每个文档设置一个超时时间

  • 创建超时时间为24小时的TTL 索引

    db.test.ensureIndex({" age": 1,}, {"expireAfterSecs": 60*60*24})

  • TTL 索引不能是复合索引

3 全文本索引

  • 创建全文本索引开销很大,应该在离线状态下创建或对性能无要求时
  • 创建全文本索引:db.test.ensureIndex({" comments": "text"})
  • 使用全文本索引:db.test.find({$text: {$search: "abc"}})

4 地理空间索引

  • 2dsphere 索引:用于地球表面类型的地图
  • 2d 索引:用于平面地图和时间连续的数据

5 使用 GridFS 存储文件

  • GridFS 是 MongoDB 的一种存储机制,用来存储大型二进制文件

  • 优点:

    • 可代替独立的文件存储工具
    • 同一目录下存储大量文件
    • 文件存储的集中度较高,MongoDB 以 2GB 为单位来分配数据文件
  • 缺陷:

    • 性能较低
    • 无法直接修改文档,需要先删除已有文档,在保存整个新的文档
    • 将文件作为多个文档存储,无法在同一时间问文件的所有块加锁
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/642328
推荐阅读
相关标签
  

闽ICP备14008679号