MongoDB

MongoDB

MongoDB基础知识

MongoDB基本概念

MongoDB是一个基于分布式文件存储的数据库。由C++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。MongoDB最大的特点就是无Schema(模式)限制,灵活度很高。数据格式是BSON,BSON是一种类似JSON的二进制形式的存储格式,简称Binary JSON,它和JSON一样,支持内嵌的文档对象和数组对象。跟关系型数据库概念对比:

Mysql MongoDB
Database(数据库) Database(数据库)
Table(表) Collection(集合)
Row(行) Document(文档)
Column(列) Field(字段)

MongoDB使用场景

1)大数据量存储场景:MongoDB自带副本集和分片,天生就适用于大数量场景,无需开发人员通过中间件去分库分表,非常方便。

2)操作日志存储:很多时候,我们需要存储一些操作日志,可能只需要存储比如最近一个月的,一般的做法是定期去清理,在MongoDB中有固定集合的概念,我们在创建集合的时候可以指定大小,当数据量超过大小的时候会自动移除掉老数据。

3)爬虫数据存储:爬下来的数据有网页,也有Json格式的数据,一般都会按照表的格式去存储,如果我们用了MongoDB就可以将抓下来的Json数据直接存入集合中,无格式限制。

4)社交数据存储:在社交场景中使用MongoDB存储存储用户地址位置信息,通过地理位置索引实现附近的人,附近的地点等。

5)电商商品存储:不同的商品有不同的属性,常见的做法是抽出公共的属性表,然后和SPU进行关联,如果用MongoDB的话那么SPU中直接就可以内嵌属性。

MongoDB数据类型

类型 说明
String(字符串) mongodb中的字符串是UTF-8有效的
Integer(整数) 存储数值。整数可以是32位或64位,具体取决于您的服务器
Boolean(布尔) 存储布尔(true/false)值
Double(双精度) 存储浮点值
Min/ Max keys(最小/最大键) 将值与最低和最高BSON元素进行比较
Arrays(数组) 将数组或列表或多个值存储到一个键中
Timestamp(时间戳) 存储时间戳
Object(对象) 嵌入式文档
Null(空值) 存储Null值
Symbol(符号) 与字符串相同,用于具有特定符号类型的语言
Date(日期) 以UNIX时间格式存储当前日期或时间
Object ID(对象ID) 存储文档ID
Binary data(二进制数据) 存储二进制数据
Code(代码) 将JavaScript代码存储到文档中
Regular expression(正则表达式) 存储正则表达式

MongoDB基本命令

数据库相关(Database)

如果数据库不存在,则创建数据库,否则切换到指定数据库
use DATABASE_NAME

查看当前连接数据库
db

查看所有数据库
show dbs

删除当前数据库,默认为test,你可以使用db命令查看当前数据库名
db.dropDatabase()

集合相关(Collection)

集合就是一组MongoDB文档。它相当于关系型数据库(RDBMS)中的表这种概念。集合位于单独的一个数据库中。一个集合内的多个文档可以有多个不同的字段。一般来说,集合中的文档都有着相同或相关的目的。

创建指定名称集合
db.createCollection(name)

创建指定名称集合,options:可选参数,指定有关内存大小及索引的选项
db.createCollection(name, options)

查看已有集合
show collections 或 show tables

删除集合
db.collection.drop()

options可以是如下参数:

字段 类型 描述
capped 布尔 (可选)如果为true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。当该值为true时,必须指定size参数。
autoIndexId 布尔 (可选)如为true,自动在_id字段创建索引。默认为false。
size 数值 (可选)为固定集合指定一个最大值,以千字节计(KB)。如果capped为true,也需要指定该字段。
max 数值 (可选)指定固定集合中包含文档的最大数量。注意:在插入文档时,MongoDB首先检查固定集合的size字段,然后检查max字段。

案例解析

1)创建固定集合mycol,整个集合空间大小6142800KB,文档最大个数为10000个
>db.createCollection("mycol", { capped : true, autoIndexId : true, size : 6142800, max : 10000 })
{ "ok" : 1 }

2)在MongoDB中,你不需要创建集合。当你插入一些文档时,MongoDB会自动创建集合
>db.mycol2.insert({"name" : "菜鸟教程"})
>show collections
mycol2

3)删除集合
>use runoob
switched to db runoob
# 先创建集合,类似数据库中的表
>db.createCollection("runoob") 
# show collections 命令会更加准确点
>show tables 
runoob
>db.runoob.drop()
true

文档(Document)

文档类似一行数据,文档的数据结构和JSON基本一样,所有存储在集合中的数据都是BSON格式。BSON是一种类似JSON的二进制形式的存储格式,是Binary JSON的简称。文档由一组key value组成。文档是动态模式,这意味着同一集合里的文档不需要有相同的字段和结构。在关系型数据库中table中的每一条记录相当于MongoDB中的一个文档。

向集合中插入文档
db.COLLECTION_NAME.insert(document)
db.COLLECTION_NAME.save(document)

查看文档
db.col.find()

3.2版本后使用,向指定集合中插入一条文档数据
db.collection.insertOne()

3.2版本后使用,向指定集合中插入多条文档数据
db.collection.insertMany()

更新已存在的文档,参数说明:
query:update的查询条件,类似sql update查询内where后面的。
update:update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的。
upsert:可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
multi:可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
writeConcern:可选,抛出异常的级别。
db.collection.update(<query>,<update>,{upsert:<boolean>,multi:<boolean>,writeConcern:<document>})

删除文档
db.collection.remove(<query>,<justOne>)

2.6版本以后,删除文档,参数说明:
query:(可选)删除的文档的条件。
justOne: (可选)如果设为true或1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
writeConcern:(可选)抛出异常的级别。
db.collection.remove(<query>,{justOne:<boolean>,writeConcern:<document>})

案例解析

1)以下文档可以存储在MongoDB的runoob数据库的col集合中
>db.col.insert({title: 'MongoDB 教程',
description: 'MongoDB 是一个 Nosql 数据库',
by: '菜鸟教程',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
})

2)插入单条数据
>var document = db.collection.insertOne({"a": 3})
>document
{
   "acknowledged" : true,
   "insertedId" : ObjectId("571a218011a82a1d94c02333")
}

3)插入多条数据
>var res = db.collection.insertMany([{"b": 3}, {'c': 4}])
>res
{
   "acknowledged" : true,
   "insertedIds" : [
           ObjectId("571a22a911a82a1d94c02337"),
           ObjectId("571a22a911a82a1d94c02338")
   ]
}

4)通过update()方法来更新标题title
>db.col.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
以上语句只会修改第一条发现的文档,如果你要修改多条相同的文档,则需要设置multi参数为true。
>db.col.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}},{multi:true})

5)只更新第一条记录
db.col.update( { "count" : { $gt : 1 } } , { $set : { "test2" : "OK"} } );

6)全部更新
db.col.update( { "count" : { $gt : 3 } } , { $set : { "test2" : "OK"} },false,true );

7)只添加第一条
db.col.update( { "count" : { $gt : 4 } } , { $set : { "test5" : "OK"} },true,false );

8)全部添加进去
db.col.update( { "count" : { $gt : 5 } } , { $set : { "test5" : "OK"} },true,true );

9)全部更新
db.col.update( { "count" : { $gt : 15 } } , { $inc : { "count" : 1} },false,true );

10)只更新第一条记录
db.col.update( { "count" : { $gt : 10 } } , { $inc : { "count" : 1} },false,false );

11)删除文档
# 删除了两条数据
>db.col.remove({'title':'ssss'})
WriteResult({ "nRemoved" : 2 }) 

12)如果你只想删除第一条找到的记录可以设置justOne为1,如下所示
db.COLLECTION_NAME.remove(DELETION_CRITERIA,1)

13)如果你想删除所有数据,可以使用以下方式(类似常规SQL的truncate命令)
db.col.remove({})
remove() 方法已经过时了,现在官方推荐使用deleteOne()和deleteMany()方法。

14)删除集合下全部文档
db.inventory.deleteMany({})

15)删除status等于A的全部文档
db.inventory.deleteMany({ status : "A" })

14)删除status等于D的一个文档
db.inventory.deleteOne( { status: "D" } )

MongoDB查询命令

普通查询

# query:可选,使用查询操作符指定查询条件。
# projection:可选,使用投影操作符指定返回的键。查询时返回文档中所有键值,只需省略该参数即可(默认省略)。
db.collection.find(query, projection)

如果你需要以易读的方式来读取数据,可以使用pretty()方法,语法格式如下:
# pretty()方法以格式化的方式来显示所有文档。
db.col.find().pretty()

AND、OR查询

AND条件语法格式如下:
# MongoDB的find()方法可以传入多个键(key),每个键(key)以逗号隔开,即常规SQL的AND条件。
db.col.find({key1:value1, key2:value2}).pretty()

OR条件语法格式如下:
# OR条件语句使用了关键字$or
db.col.find(
{
$or: [
   {key1: value1}, {key2:value2}
]
}
).pretty()

案例解析
1)OR语句
db.col.find({$or:[{"by":"菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()

2)AND 和 OR 联合使用
db.col.find({"likes": {$gt:50}, $or: [{"by": "菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()

条件查询

MongoDB与RDBMS的Where语句比较

操作 格式 范例 RDBMS中的类似语句
等于 {:} db.col.find({"by":"菜鸟教程"}).pretty() where by = '菜鸟教程'
小于 {:{$lt:}} db.col.find({"likes":{$lt:50}}).pretty() where likes < 50
小于或等于 {:{$lte:}} db.col.find({"likes":{$lte:50}}).pretty() where likes <= 50
大于 {:{$gt:}} db.col.find({"likes":{$gt:50}}).pretty() where likes > 50
大于或等于 {:{$gte:}} db.col.find({"likes":{$gte:50}}).pretty() where likes >= 50
不等于 {:{$ne:}} db.col.find({"likes":{$ne:50}}).pretty() where likes != 50

案例解析

新增3条数据用于测试
>db.myDemo.insert({'name':'zs','age':15})
WriteResult({ "nInserted" : 1 })
>db.myDemo.insert({'name':'ls','age':26})
WriteResult({ "nInserted" : 1 })
>db.myDemo.insert({'name':'ww','age':20})
WriteResult({ "nInserted" : 1 })

1、查询大于15小于26岁的数据
db.col.find({age : {$lt :26, $gt : 15}})

2、查询name字段包含"教"字的文档
db.col.find({name:/教/})

3、查询name字段以"教"字开头的文档
db.col.find({name:/^教/})

4、查询name字段以"教"字结尾的文档
db.col.find({name:/教$/})

函数查询

如果你需要在MongoDB中读取指定数量的数据记录,可以使用MongoDB的Limit方法,limit()方法接受一个数字参数,该参数指定从MongoDB中读取的记录条数。
db.COLLECTION_NAME.find().limit(NUMBER)

使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。注:skip()方法默认参数为0
db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)

在MongoDB中使用sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用1和-1来指定排序的方式,其中1为升序排列,而-1是用于降序排列。
db.COLLECTION_NAME.find().sort({KEY:1})

skip()、limit()、sort()三个放在一起执行的时候,执行的顺序是先sort(),然后是skip(),最后是显示的limit()。

索引查询

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。

1)MongoDB使用createIndex()方法来创建索引
注意在3.0.0版本前创建索引方法为db.collection.ensureIndex(),之后的版本使用了db.collection.createIndex()方法,ensureIndex()还能用,但只是createIndex()的别名。createIndex()方法基本语法格式如下所示:
db.collection.createIndex(keys, options)

语法中Key值为你要创建的索引字段,options:1为指定按升序创建索引,如果你想按降序来创建索引指定为-1即可。如:
db.col.createIndex({"title":1})

createIndex()方法中你也可以设置使用多个字段创建索引(关系型数据库中称作复合索引)。
db.col.createIndex({"title":1,"description":-1})

创建索引特别要注意的是将background设置为true,在建索引的过程会阻塞其它数据库操作,background可指定以后台方式创建索引,默认为false。
db.collection.createIndex({user_id: 1, add_time: 1}, {background: true})

2)查看集合索引
db.col.getIndexes()

3)查看集合索引大小
db.col.totalIndexSize()

4)删除集合所有索引
db.col.dropIndexes()

5)删除集合指定索引
db.col.dropIndex("索引名称")

聚合函数查询

MongoDB中聚合的方法使用aggregate(),aggregate()方法的基本语法格式如下所示:
db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
如:
db.mycol.aggregate([{$group:{_id:"$by_user",num_tutorial:{$sum:1}}}])
类似: 
select by_user,count(*) from mycol group by by_user

下表展示了一些聚合的表达式:
1)$sum计算总和:
db.mycol.aggregate([{$group:{_id:"$by_user",num_tutorial:{$sum:"$likes"}}}])

2)$avg计算平均值
db.mycol.aggregate([{$group:{_id:"$by_user",num_tutorial:{$avg:"$likes"}}}])

3)$min获取集合中所有文档对应值得最小值
db.mycol.aggregate([{$group:{_id :"$by_user",num_tutorial:{$min:"$likes"}}}])

4)$max获取集合中所有文档对应值得最大值
db.mycol.aggregate([{$group:{_id:"$by_user",num_tutorial:{$max:"$likes"}}}])

5)$push在结果文档中插入值到一个数组中
db.mycol.aggregate([{$group:{_id:"$by_user",url:{$push:"$url"}}}])

6)$addToSet在结果文档中插入值到一个数组中,但不创建副本
db.mycol.aggregate([{$group:{_id:"$by_user",url:{$addToSet:"$url"}}}])

7)$first根据资源文档的排序获取第一个文档数据
db.mycol.aggregate([{$group:{_id:"$by_user",first_url:{$first:"$url"}}}])

8)$last根据资源文档的排序获取最后一个文档数据
db.mycol.aggregate([{$group:{_id:"$by_user",last_url:{$last:"$url"}}}])

聚合管道查询

管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理,管道操作是可以重复的。表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。这里我们介绍一下聚合框架中常用的几个操作:

操作 说明
$project 修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
$match 用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
$limit 用来限制MongoDB聚合管道返回的文档数。
$skip 在聚合管道中跳过指定数量的文档,并返回余下的文档。
$unwind 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
$group 将集合中的文档分组,可用于统计结果。
$sort 将输入文档排序后输出。
$geoNear 输出接近某一地理位置的有序文档。

案例解析

1)$project实例
db.article.aggregate({$project:{title:1,author:1}});
这样的话结果中就只还有_id、tilte和author三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样:
db.article.aggregate({$project:{_id:0,title:1,author:1}});

2)$match实例
db.articles.aggregate([{$match:{score:{$gt:70,$lte:90}}},{$group:{_id:null,count:{$sum:1}}}]);
$match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。

3)$skip实例
db.article.aggregate({$skip:5});
经过$skip管道操作符处理后,前五个文档被"过滤"掉。

MongoDB日志文件

Journal日志文件

1)Journal是MongoDB存储引擎层的概念,目前MongoDB主要支持mmapv1、wiredtiger、mongorocks等存储引擎,都支持配置Journal。MongoDB所有的数据写入、读取最终都是调存储引擎层的接口来存储、读取数据,Journal是存储引擎存储数据时的一种辅助机制。

2)Journal日志,是MongoDB的预写日志WAL(类似Mysql的Redo log)。默认情况下MongoDB每100毫秒往Journal文件中flush一次数据,不过这是在数据文件和Journal文件处于同一磁盘卷上的情况,而如果数据文件和Journal文件不在同一磁盘卷上时,默认刷新输出时间是30毫秒。不过这个毫秒值是可以修改的,可修改范围是2~300,值越低,刷新输出频率越高,数据安全度也就越高,但磁盘性能上的开销也更高。

3)以wiredtiger为例,如果不配置Journal,写入wiredtiger的数据,并不会立即持久化存储;而是每分钟(默认)或者待写入的数据达到2G时,会做一次全量的checkpoint(storage.syncPeriodSecs配置项,默认为1分钟),将所有的数据持久化。如果中间出现宕机,那么数据只能恢复到最近的一次checkpoint(检测点,将内存中的数据变更flush到磁盘中的数据文件中,并做一个标记点,表示此前的数据表示已经持久存储在了数据文件中,此后的数据变更存在于内存和Journal日志),这样最多可能丢掉1分钟的数据。所以建议一定要开启Journal,开启Journal后,每次写入会记录一条操作日志(通过Journal可以重新构造出写入的数据)。这样即使出现宕机,启动时Wiredtiger会先将数据恢复到最近的一次checkpoint的点,然后重放后续的Journal操作日志来恢复数据。

4)Journal文件是以“j._”开头命名的,且是append only的,如果1个Journal文件满了1G大小,MongoDB就会新创建一个Journal文件来使用,一旦某个Journal文件所记载的写操作都被使用过了,MongoDB就会把这个Journal文件删除。通常在Journal文件所在的文件夹下,只会存在2~3个Journal文件,除非你使用MongoDB每秒都写入大量的数据。而使用smallfiles这个运行时选项可以将Journal文件大小减至128M大小。

5)MongoDB里的Journal行为主要由2个参数控制,storage.journal.enabled决定是否开启Journal,storage.journal.commitInternalMs决定Journal刷盘的间隔,默认为100ms,用户也可以通过写入时指定writeConcern为{j: ture}来每次写入时都确保Journal刷盘。

oplog日志文件

oplog是MongoDB主从复制层面的一个概念,通过oplog来实现复制集节点间数据同步,客户端将数据写入到Primary,Primary写入数据后会记录一条oplog,Secondary从Primary(或其他Secondary)拉取oplog并重放,来确保复制集里每个节点存储相同的数据。

oplog在MongoDB里是一个普通的capped collection(固定集合),对于存储引擎来说,oplog只是一部分普通的数据而已。MongoDB的一次写入,MongoDB复制集里写入一个文档时,需要修改如下数据:
1、将文档数据写入对应的集合
2、更新集合的所有索引信息
3、写入一条oplog用于同步
上面3个修改操作,需要确保要么都成功,要么都失败,不能出现部分成功的情况,否则:
1、如果数据写入成功,但索引写入失败,那么会出现某个数据,通过全表扫描能读取到,但通过索引就无法读取;
2、如果数据、索引都写入成功,但oplog写入不成功,那么写入操作就不能正常的同步到备节点,出现主备数据不一致的情况。MongoDB在写入数据时,会将上述3个操作放到一个wiredtiger的事务里,确保原子性。wiredtiger提交事务时,会将所有修改操作应用,并将上述3个操作写入到一条journal操作日志里,后台会周期性的checkpoint,将修改持久化,并移除无用的journal。

oplog与journal谁先写入的问题:
1)oplog与journal是MongoDB里不同层次的概念,放在一起比先后本身是不合理的。
2)oplog在MongoDB里是一个普通的集合,所以oplog的写入与普通集合的写入并无区别。
3)一次写入,会对应数据、索引、oplog的修改,而这3个修改,会对应一条journal操作日志。

MongoDB数据库分析器

开闭数据库分析器

数据库分析器:数据库分析器(Database Profiler)收集有关针对的mongod实例运行操作命令的详细信息。探查器的输出可帮助识别效率低下的查询和操作,这包括CRUD操作以及配置和管理命令。分析器(Profiler)将收集到的所有信息写入system.profile集合,Profile集合是个有固定大小的集合(默认1M),支持基于插入顺序的插入和检索文档的高吞吐量操作。Profile集合的工作方式类似于循环缓冲区,一旦集合填满了它所分配的空间,它就会重写集合中最旧的文档,从而为新文档腾出空间。

慢查询:我们将超过指定时间的查询称为“慢查询”。在MongoDB中你可以设置一个查询时长的限额值来确定是否是慢查询,默认是100毫秒。通过这些慢查询你可以方便的监测这些查询是否使用了全集合扫描或者索引扫描。MongoDB的数据库分析器默认是关闭的。长时间开启会对数据库有性能方面的影响,所以最好是使用完性能分析后,将此功能关闭,以避免以数据库性能造成影响。

开启分析器:db.setProfilingLevel(<level>, <options>)
level(integer)配置分析器级别。提供以级别:
0:分析器已关闭,并且不收集任何数据。这是默认的分析器级别。
1:分析器收集的数据花费的时间超过的值slowms。
2:分析器收集所有操作的数据。

options(document or integer)可选的。接受整数或配置文档。如果将整数值作为options参数而不是配置文档作为参数传递,则该值将分配给slowms。提供以下选项:
slowms(默认值:100,类型:integer):慢操作的时间阈值,单位为毫秒。运行时间超过此阈值的操作被认为是慢查询。当logLevel设置为0,MongoDB以由slowOpSampleRate确定的速率将慢速操作记录到诊断日志中。
sampleRate(默认值:1.0,类型:double):慢查询分析的百分比率。sampleRate接受0到1之间的值(包括0和1)。sampleRate 值默认为1,表示都采集,0.35表示采集35%的内容。
filter(类型: object)过滤器表达式,控制哪些操作分析和记录。New in version 4.4.2。

开启之后,该方法返回一个包含先前设置值的文档:
{
   "was" : 0,
   "slowms" : 100,
   "sampleRate" : 1,
   "ok" : 1,
   "operationTime" : Timestamp(1572991499, 2),
   "$clusterTime" : {
      "clusterTime" : Timestamp(1572991499, 2),
      "signature" : {
         "hash" : BinData(0,"nhCquIxUw7thlrBudXe3PnsnvP0="),
         "keyId" : NumberLong("6755946491040235540")
      }
   }
}
was:是上一个level设置。
slowms:是先前的slowms设置。
sampleRate:是先前的sampleRate设置。
filter:是先前的filter设置(New in MongoDB 4.4.2)
note:是解释filter行为的字符串。此字段仅在filter也出现时出现在输出中(New in MongoDB 4.4.2)

查看当前配置:db.getProfilingStatus()

改变分析器集合的大小:
db.setProfilingLevel(0)
db.system.profile.drop()
# 创建4M大小的集合
db.createCollection( "system.profile", { capped: true, size:4000000 } )
db.setProfilingLevel(1)

关闭数据库分析器:
use test
db.setProfilingLevel(0)

数据库分析器使用案例

#  为所有数据库开启慢查询记录
db.setProfilingLevel(2)

#  指定数据库,并指定阈值慢查询,超过20毫秒的查询被记录
use test
db.setProfilingLevel(1, { slowms: 20 })

#  随机采集慢查询的百分比值,sampleRate值默认为1,表示都采集,0.42表示采集42%的内容。
db.setProfilingLevel(1, { sampleRate: 0.42 }) 

# 记录所有操作日志,过滤查询操作超过2秒的
db.setProfilingLevel( 2, { filter: { op: "query", millis: { $gt: 2000 } } } )

# 查询慢查询级别和其它信息
db.getProfilingStatus()

# 仅返回慢查询级别
db.getProfilingLevel()

数据库分析器数据分析

# 查询最近的10个慢查询日志
db.system.profile.find().limit(10).sort( { ts : -1 } ).pretty()

# 查询除命令类型为command的日志
db.system.profile.find( { op: { $ne : 'command' } } ).pretty()

# 查询数据库为mydb集合为test的日志
db.system.profile.find( { ns : 'mydb.test' } ).pretty()

# 查询低于5毫秒的日志
db.system.profile.find( { millis : { $gt : 5 } } ).pretty()

# 查询时间从2012-12-09 3点整到2012-12-09 3点40分之间的日志
db.system.profile.find({
  ts : {
    $gt: new ISODate("2012-12-09T03:00:00Z"),
    $lt: new ISODate("2012-12-09T03:40:00Z")
  }
}).pretty()

# 下面的示例查看时间范围,将用户字段从输出中删除以使其更容易阅读,并根据每个操作运行的时间对结果进行排序
db.system.profile.find({
  ts : {
    $gt: new ISODate("2011-07-12T03:00:00Z"),
    $lt: new ISODate("2011-07-12T03:40:00Z")
  }
}, { user: 0 }).sort( { millis: -1 } )

system.profile集合字段解析:
{
   "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" : "someuser@admin"  # 经过验证的用户
}
posted @ 2023-07-06 11:24  肖德子裕  阅读(24)  评论(0编辑  收藏  举报