您的位置:首页 > 数据库 > Mongodb

第四章:mongodb 命令行操作&进程控制&性能优化

2015-06-12 15:49 645 查看


一:命令执行操作

1.通过eval 参数执行指定语句

需要查询test库的t1 表中的记录数有多少,常用方法如下:
db.t1.count()
通过命令行eval参数直接执行语句:
./mongo test --eval "printjson(db.t1.count())"

2.执行指定文件的内容

如果涉及到很多的操作后,才能得到结果,那么用 eval 的方式来做的话是不可能完成的,

那么更灵活的执行指定文件的方式就派上用场了。例如我们仍然要查看 test 库 t1 表中的记录数

user_count.js就是我们要执行的文件,里面的内容如下

[root@localhost bin]# cat user_count.js

var totalcount = db.user.count();

printjson('Total count of user is : ' + totalcount);

printjson('-----------------------');

下面我们将执行这个文件

[root@localhost bin]# ./mongo user_count.js

MongoDB shell version: 1.8.1

connecting to: test

"Total count of user is : 0"

"-----------------------"

大家可以看到最终得到 t1 表的记录数 0,那么一些不必要的说明性文字我们要是不希望出

现该怎么办呢?

[root@localhost bin]# ./mongo --quiet user_count.js

"Total count of t1 is : 0"

"-----------------------"

过指定quiet参数,即可以将一些登录信息屏蔽掉,这样可以让结果更清晰。

二:进程控制

1.查看活动进程

便于了解系统正在做什么,以便做下一步判断

> db.currentOp();
> // 等同于: db.$cmd.sys.inprog.findOne()
> db.currentOp();

{

"inprog" : [

{

"desc" : "conn15",

"threadId" : "1024",

"connectionId" : 15,

"opid" : 100,

"active" : true,

"secs_running" : 0,

"microsecs_running" : NumberLong(78),

"op" : "query",

"ns" : "admin.$cmd",

"query" : { "currentOp" : 1 },

"client" : "127.0.0.1:55068",

"numYields" : 0,

"locks" : {},

"waitingForLock" : false,

"lockStats" : {}

}

],

"ok" : 1

}

字段说明:

Opid: 操作进程号

Op: 操作类型(查询,更新等)

Ns: 命名空间, 指操作的是哪个对象

Query: 如果操作类型是查询的话,这里将显示具体的查询内容

lockType: 锁的类型,指明是读锁还是写锁

2. 结束进程

如果某个异常是由于某个进程产生的,那么一般 DBA 都会毫不留情的杀掉这个罪魁祸首进程,下面将是这操作

db.killOp(1234/*opid*/)
> // 等同于: db.$cmd.sys.killop.findOne({op:1234})

注意:
不要 kill 内部发起的操作,比如说 replica set 发起的 sync 操作等

三:性能篇

1.基础索引

MongoDB 提供了多样性的索引支持,索引信息被保存在system.indexes中,且默认总是为_id创建索引,它的索引使用基本和 MySQL 等关系型数据库一样。其实可以这样说说,索引是凌驾于数据存储系统之上的另一层系统,所以各种结构迥异的存储都有相同或相似的索引实现及使用接口并不足为奇。

1.1 基础索引

在字段age上创建索引,1(升序);-1(降序)
> db.persion.ensureIndex({age:1})

{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}

> db.persion.getIndexes(); --查看persion的index
[
{
"v" : 1,
"key" : { "_id" : 1},
"name" : "_id_",
"ns" : "test.persion"},
{
"v" : 1,
"key" : { "age" : 1 },
"name" : "age_1",
"ns" : "test.persion"
}
]

当系统已有大量数据时,创建索引就是个非常耗时的活,我们可以在后台执行,只需指定“backgroud:true”即可。

> db.persion.ensureIndex({age:1} , {backgroud:true})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"errmsg" : "Index with name: age_1 already exists with different options",
"code" : 85,
"ok" : 0
}

2.文档索引

>db.factories.insert( { name: "wwl", addr: { city: "Beijing", state: "BJ" } } );
//在addr 列上创建索引

>db.factories.ensureIndex( { addr : 1 } );

//下面这个查询将会用到我们刚刚建立的索引

>db.factories.find( { addr: { city: "Beijing", state: "BJ" } } );

{ "_id" : ObjectId("5549acacf69bb9e7f81e632a"), "name" : "wwl", "addr" : { "city" : "Beijing", "state" : "BJ" } }

//但是下面这个查询将不会用到索引,因为查询的顺序跟索引建立的顺序不一样
db.factories.find( { addr: { state: "BJ" , city: "Beijing"} } );

3.组合索引

> db.factories.ensureIndex( { "addr.city" : 1, "addr.state" : 1 } );
// 下面的查询都用到了这个索引
> db.factories.find( { "addr.city" : "Beijing", "addr.state" : "BJ" } ); --ok
> db.factories.find( { "addr.city" : "Beijing" } ); --ok
> db.factories.find().sort( { "addr.city" : 1, "addr.state" : 1 } ); --ok
> db.factories.find().sort( { "addr.city" : 1 } ) --ok

4.唯一索引

只需在ensureIndex命令中指定”unique:true”即可创建唯一索引。例如,往表 t4 中插入2 条记录
> db.t4.insert({firstname: "wang1", lastname: "wenlong1"});

> db.t4.insert({firstname: "wang2", lastname: "wenlong2"});

在t4表中建立唯一索引
> db.t4.ensureIndex({firstname: 1, lastname: 1}, {unique: true});

5.强制使用索引

> db.t5.insert({name: "zw",age: 20})
> db.t5.ensureIndex({name:1, age:1})
> db.t5.find({age:{$lt:30}}).explain()
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.t5",
"indexFilterSet" : false,
"parsedQuery" : { "age" : { "$lt" : 30 }},
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : { "age" : { "$lt" : 30 } }, "direction" : "forward" }, "rejectedPlans" : [ ] },
"serverInfo" : {
"host" : "BR56",
"port" : 27017,
"version" : "3.1.2",
"gitVersion" : "aa0066050f0a9db81aa47181d0fbd18c109ae991"
},
"ok" : 1 --没有出现indexbounds 没有走索引
}

>db.t5.find({age:{$lt:30}}).hint({name:1, age:1}).explain() --强制使用索引

{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.t5",
"indexFilterSet" : false,
"parsedQuery" : {
"age" : { "$lt" : 30 } },
"winningPlan" : {
"stage" : "KEEP_MUTATIONS",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"age" : { "$lt" : 30 }
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"name" : 1,
"age" : 1
},
"indexName" : "name_1_age_1",
"isMultiKey" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : { --这里使用索引了
"name" : [
"[MinKey, MaxKey]"
],
"age" : [
"[MinKey, MaxKey]"
]
}
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"host" : "BR56",
"port" : 27017,
"version" : "3.1.2",
"gitVersion" : "aa0066050f0a9db81aa47181d0fbd18c109ae991"
},
"ok" : 1
}

6.删除索引

删除索引分为删除某张表的所有索引和删除某张表的某个索引,具体如下:
//删除 t3表中的所有索引
> db.t3.dropIndexes()
//删除 t4表中的 firstname 索引
> db.t4.dropIndex({firstname: 1})

7.执行计划

MongoDB 提供了一个 explain 命令让我们获知系统如何处理查询请求。利用 explain 命令,我们可以很好地观察系统如何使用索引来加快检索,同时可以针对性优化索引。

> db.t5.find({age:{$gt:45}}, {name:1}).explain()

{

"queryPlanner" : {

"plannerVersion" : 1,

"namespace" : "test.t5",

"indexFilterSet" : false,

"parsedQuery" : {

"age" : {

"$gt" : 45

}

},

"winningPlan" : {

"stage" : "PROJECTION",

"transformBy" : {

"name" : 1

},

"inputStage" : {

"stage" : "FETCH",

"inputStage" : {

"stage" : "IXSCAN",

"keyPattern" : {

"age" : 1

},

"indexName" : "age_1",

"isMultiKey" : false,

"indexVersion" : 1,

"direction" : "forward",

"indexBounds" : {

"age" : [

"(45.0, 1.#INF]"

]

}

}

}

},

"rejectedPlans" : [ ]

},

"serverInfo" : {

"host" : "BR56",

"port" : 27017,

"version" : "3.1.2",

"gitVersion" : "aa0066050f0a9db81aa47181d0fbd18c109ae991"

},

"ok" : 1

}

字段说明:

cursor: 返回游标类型(BasicCursor 或 BtreeCursor)

nscanned: 被扫描的文档数量

n: 返回的文档数量

millis: 耗时(毫秒)

indexBounds: 所使用的索引

8.优化器profile

8.1 开启 Profiling 功能

有两种方式可以控制 Profiling 的开关和级别,第一种是直接在启动参数里直接进行设置。 启动MongoDB时加上–profile=级别 即可。 也可以在客户端调用 db.setProfilingLevel(级别) 命令来实时配置,Profiler 信息保存在 system.profile 中。我们可以通过db.getProfilingLevel()命令来获取当前的Profile级别,类似如下操作 .

>db.setProfilingLevel(2);
{ "was" : 0, "slowms" : 100, "ok" : 1 }

上面profile的级别可以取0,1,2 三个值,他们表示的意义如下:

0 – 不开启

1 – 记录慢命令 (默认为>100ms)

2 – 记录所有命令

Profile 记录在级别 1 时会记录慢命令,那么这个慢的定义是什么?上面我们说到其默认为

100ms,当然有默认就有设置,其设置方法和级别一样有两种,一种是通过添加–slowms 启

动参数配置。第二种是调用db.setProfilingLevel时加上第二个参数:

> db.setProfilingLevel( level , slowms )
> db.setProfilingLevel( 1 , 10 );
{ "was" : 2, "slowms" : 100, "ok" : 1 }

8.2 查询 Profiling 记录

与 MySQL 的慢查询日志不同,MongoDB Profile 记录是直接存在系统 db 里的,记录位置
system.profile ,所以,我们只要查询这个 Collection 的记录就可以获取到我们的 Profile 记录了。

列出执行时间长于某一限度(5ms)的 Profile 记录:db.system.profile.find( { millis : { $gt : 5 } } )

查看最新的 Profile 记录:

db.system.profile.find().sort({$natural:-1}).limit(1)

{ "ts" : ISODate("2012-05-20T16:50:36.321Z"),
"info" : "query test.system.profile

reslen
:1219
nscanned:8 \nquery: { query: {}, orderby: { $natural: -1.0 } }
nreturned:8 bytes:1203",
"millis" : 0
}

字段说明 字段说明 字段说明 字段说明:
ts: 该命令在何时执行
info: 本命令的详细信息
reslen: 返回结果集的大小
nscanned: 本次查询扫描的记录数
nreturned: 本次查询实际返回的结果集
millis: 该命令执行耗时,以毫秒记

MongoDB Shell 还提供了一个比较简洁的命令 show profile,可列出最近5 条执行时间超过
1ms的 Profile 记录。

9.性能优化

如果nscanned(扫描的记录数)远大于nreturned(返回结果的记录数)的话,那么我们就要考虑通过加索引来优化记录定位了。

reslen
如果过大,那么说明我们返回的结果集太大了,这时请查看find函数的第二个参数是否只写上了你需要的属性名。

对于创建索引的建议是
:如果很少读,那么尽量不要添加索引,因为索引越多,写操作会越慢。如果读量很大,那么创建索引还是比较划算的。

假设我们按照时间戳查询最近发表的 10篇博客文章:

articles = db.persion.find().sort({ts:-1});
for (var i=0; i< 10; i++) {
print(articles[i].getSummary());
}

9.1 优化方案一:创建索引

在查询条件的字段上,或者排序条件的字段上创建索引,可以显著提高执行效率:
db.posts.ensureIndex({ts:1});

9.2 优化方案二:限定返回结果数

使用limit()限定返回结果集的大小,可以减少database server 的资源消耗,可以减少网络传输数据量。

articles = db.persion.find().sort({ts:-1}).limit(10);

9.3 优化方案三:只查询用到的字段

>articles = db.persion.find({}, {age:1}).sort({age:-1}).limit(10);

{ "_id" : ObjectId("5549aaacf69bb9e7f81e6329"), "age" : 24 }
{ "_id" : ObjectId("5549aaa0f69bb9e7f81e6328"), "age" : 23 }
{ "_id" : ObjectId("5549aa97f69bb9e7f81e6327"), "age" : 22 }
{ "_id" : ObjectId("55472415dd647b87c8344724"), "age" : 20 }
{ "_id" : ObjectId("55471e5ddd647b87c8344721"), "age" : 18 }

注意:如果只查询部分字段的话,不能用返回的对象直接更新数据库。下面的代码是错误的

a_post = db.posts.findOne({}, Post.summaryFields);
a_post.x = 3;
db.posts.save(a_post);

9.4 优化方案四:cappend collection

capped Collections比普通Collections的读写效率高。 Capped Collections是高效率的Collection
类型,它有如下特点:

1、 固定大小;Capped Collections必须事先创建,并设置大小:
> db.createCollection("mycoll", {capped:true, size:100000})
2、 Capped Collections 可以 insert 和 update 操作;不能delete 操作。只能用 drop()方法删除整个Collection。
3、 默认基于Insert的次序排序的。如果查询时没有排序,则总是按照insert的顺序返回。
4、 FIFO。如果超过了 Collection 的限定大小,则用 FIFO 算法,新记录将替代最先 insert 的记录。

9.5 优化方案五:采用Server Side Code Execution

Server-Side Processing类似于SQL数据库的存储过程,使用Server-Side Processing可以减小网络通讯的开销。

9.6 优化方案六:采用hint

一般情况下MongoDB query optimizer 都工作良好,但有些情况下使用hint()可以提高操作效
率。Hint可以强制要求查询操作使用某个索引。例如,如果要查询多个字段的值,如果在其
中一个字段上有索引,可以使用hint:

db.collection.find({user:u, foo:d}).hint({user:1});

9.6 优化方案七:采用profiling

profiling 功能肯定是会影响效率的,但是不太严重,原因是他使用的是 system.profile 来记录,而system.profile 是一个capped collection 这种collection 在操作上有一些限制和特点,但是效率更高。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: