天天看点

mongo实战2——第8-9章 索引全文搜索

文章目录

    • 索引的核心概念
      • 单建索引
      • 复合索引
    • 索引的效率
    • 索引实战
      • 索引类型
        • 唯一索引
        • 稀疏索引
        • 多建索引
        • 哈希索引
        • 地理空间索引
      • 索引管理
        • 创建和删除索引
        • 后台索引
        • 重建索引
    • 查询优化
      • 找出慢速查询
      • mongo慢日志
    • 执行计划
      • hint 强制使用某个索引执行查询
    • 总结
  • 全文搜索
    • mongo全文搜索的功能
    • mongo全文搜索的使用步骤
      • 创建全文索引
      • 使用全文索引查询

索引可以大大减少查询时处理文档的数量。没有适当的索引,查询时通常会扫描整个集合。

索引的核心概念

单建索引

针对文档的一个字段建立索引,最好的单建索引的例子就是集合的_id字段

复合索引

如果在一个查询里使用多个查询条件,我们可以在这些条件上建立复合索引,这种情况下,复合索引的查询效率要高于两个单建索引。

索引的效率

  1. 索引虽然可以提高查询效率,但是索引的维护也是需要成本的,当插入或修改文档的时候,都需要维护索引结构。
  2. 当索引和数据没有加载到内存时,即使所有的索引都创建的恰当,也可能无法加快查询。当数据的大小超过内存很多时,每次查询都需要从磁盘读取数据导致性能变慢。这个情况的专业称呼为“颠簸”。
  3. 为了避免索引的数量太大,尽量不要创建不必要的索引
  4. 不要在选择性比较差的字段上创建索引,如性别字段。
  5. 我们可以通过索引的大小来预估mongo需要内存的大小。

索引实战

索引类型

唯一索引

如果要保证文档中的字段唯一,我们可以使用唯一索引。如果像集合中插入存在的唯一索引值,mongo会抛出异常

创建唯一索引
db.users.createIndex({name:1},{unique:true})
           

稀疏索引

索引默认是密集型的,意味着,对于索引集合中的文档,如果索引建值缺失,也会维护在索引的结构中。默认为null

不是每个文档都包含此字段时,不需要建立密集索引

多建索引

当对数据字段建立索引时,数组中每个元素都会出现在索引的结构中,既通过每个元素查询都可以提高查询效率

哈希索引

hash索引的限制

  1. 等值查询很快,不支持范围查询
  2. 不支持多建哈希

当使用分片部署时,hash索引可以将数据均匀分布到各个节点中

通过hashed指定为哈希索引
db.user.createIndex({name:'hashed'})
           

地理空间索引

当查询某个地点附近的文档时,可以使用地理空间索引提高查询效率

索引管理

创建和删除索引

db.collection.createIndex({open:1,close:1})
db.collection.dropIndex("indexName")
           

后台索引

db.collection.createIndex({open:1,close:1},{background:true})
           

重建索引

db.collection.reIndex()
           

查询优化

查询优化是找出慢速查询,发现问题解决问题的过程

找出慢速查询

mongodb日志会打印所有查询时间大于100ms的操作,这里是慢速查询的第一个入口.

启动时可以通过–slows 50修改慢查询日志参数

  1. 使用grep命令查询慢日志
grep -E '[0-9]+ms' mongod.log
           
  1. 使用PROFILER分析器

    profiler设置方法

1.选择要监控的数据库
use dbName
2.设置profiler级别
db.serProfilingLevel(2)
	0 关闭profile分析器
	1 分析器只会记录超时的操作 传递毫秒作为第二个参数
	2 记录所有的查询操作
	db.serProfilingLevel(1,50)
           

监控结果会保存在一个system.profile的集合中

db.system.profile.find({millis:{gt:150}}) 获取大于150ms的操作
           

mongo慢日志

{
   "op" : "query",   # 操作类型,值可为command、count、distinct、geoNear、getMore、group、insert、mapReduce、query、remove、update
   "ns" : "test.report", # 操作的数据库和集合
   "command" : {     # 命令
      "find" : "report",  # 操作的集合
      "filter" : { "a" : { "$lte" : 500 } }, # 查询条件
      "lsid" : {    
         "id" : UUID("5ccd5b81-b023-41f3-8959-bf99ed696ce9") #用户的会话id
      },
      "$db" : "test"  # 操作的数据库
   },
   "cursorid" : 33629063128,  # query和getmore 的游标id
   "keysExamined" : 101, # MongoDB为执行操作而扫描的索引键的数量
   "docsExamined" : 101, # MongoDB为了执行操作	而扫描的集合中的文档数。
   "numYield" : 2, # 让步次数,操作时让其他的操作完成的次数。
   "nreturned" : 101, # 操作返回的文档数
   "queryHash" : "811451DD", # 查询的hash值
   "planCacheKey" : "759981BA", 
   "locks" : {  # 操作期间的锁和所的类型
      "Global" : {  #表示全局锁定
         "acquireCount" : { #锁定的次数
            "r" : NumberLong(3)  # 表示共享锁 
         }
      },
      "Database" : {   # 数据库锁
         "acquireCount" : { "r" : NumberLong(1) },
         "acquireWaitCount" : { "r" : NumberLong(1) },
         "timeAcquiringMicros" : { "r" : NumberLong(69130694) }
      },
      "Collection" : {  # 集合锁
         "acquireCount" : { "r" : NumberLong(1) }
      }
   },
   "storage" : { # 储存
      "data" : {
         "bytesRead" : NumberLong(14736), #操作 从磁盘放到缓存的数据的字节数
         "timeReadingMicros" : NumberLong(17) # 操作 花费在磁盘读取的时间,以微妙为单位
      }
   },
   "responseLength" : 1305014, # 操作返回结果的文档长度,单位为字节
   "protocol" : "op_msg", # 消息的协议
   "millis" : 69132, # 从 MongoDB 操作开始到结束耗费的时间
   "planSummary" : "IXSCAN { a: 1, _id: -1 }",  # 摘要
   "execStats" : {  # 操作执行过程中的详细信息
      "stage" : "FETCH", # 操作形式 ,COLLSCAN 用于集合扫描,IXSCAN 用于扫描索引键,FETCH 用于检索文档
      "nReturned" : 101, # 返回的文档数量
      "executionTimeMillisEstimate" : 0,
      "works" : 101,
      "advanced" : 101,
      "needTime" : 0,
      "needYield" : 0,
      "saveState" : 3,
      "restoreState" : 2,
      "isEOF" : 0,
      "invalidates" : 0,
      "docsExamined" : 101,
      "alreadyHasObj" : 0,
      "inputStage" : {
         ...
      }
   },
   "ts" : ISODate("2019-01-14T16:57:33.450Z"), #操作的时间戳
   "client" : "127.0.0.1",  # 客户端的ip
   "appName" : "MongoDB Shell", #客户端应用标识符
   "allUsers" : [
      {
         "user" : "someuser", # 用户
         "db" : "admin"  # 验证的数据库
      }
   ],
   "user" : "[email protected]"  # 经过验证的用户
}
           

执行计划

获取执行计划explain(true)

db.values.find({}).sort({close:-1}).limit(5).explain(true)
           

hint 强制使用某个索引执行查询

db.values.find({}).sort({close:-1}).limit(5).hint("_id_");
           

总结

  1. 索引非常有用,但是也有成本,他会让写入变慢
  2. mongoDB通常在一个查询里使用一个索引,所以多个字段的查询使用复合索引效率更高
  3. 当声明复合索引时顺序非常重要
  4. 避免慢速的查询,使用explain命令可以查看执行计划
  5. 为了避免影响应用程序,应该在流量增加之前尽早的优化查询
  6. 通过减少扫描的文档数量来优化查询

全文搜索

与其他的全文搜索引擎不同,mongo不允许我们编辑分词列表

mongo全文搜索的功能

  1. 基于词根分词的自动化实例索引
  2. 根据字段名可分配权重
  3. 多语言支持
  4. 分词
  5. 精确词汇匹配
  6. 根据给定短语或单词排出某些结果的能力

mongo全文搜索的使用步骤

  1. 首先定义全文搜索需要的索引
  2. 然后,在基于查询和聚合框架使用文本搜索

创建全文索引

在name、description、tags三个字段上创建全文索引
db.products.createIndex({
			name:'text',
			description:'text',
			tags:'text'
})

创建索引并制定权重

db.books.createIndex(
		{title:'text',
		shortDescription:'text',
		longDesctiption:'text',
		authors:'text',
		categories:'text'},
		{weight:
			{title:10,
			shortDescription:1,
			longDesctiption:1,
			authors:1,
			categories:5}
		name:'book_text_index' 指定索引的名字
});
           

使用全文索引查询

db.products.find(
		{$text:${search:'gardens'}},
		{_id:0,name:1,description:1,tags:1}