绝好的MongoDB学习资料之六. Optimization

1. Profiler

MongoDB 自带 Profiler,可以非常方便地记录下所有耗时过长操作,以便于调优。

> db.setProfilingLevel(n)

n: 
   0: Off; 
   1: Log Slow Operations; 
   2: Log All Operations.


通常我们只关心 Slow Operation,Level 1 默认记录 >100ms 的操作,当然我们也可以自己调整 "db.setProfilingLevel(2, 300)"。
Profiler 信息保存在 system.profile (Capped Collection) 中。

准备 1000000 条数据测试一下。

>>> from pymongo import *
>>> from random import randint
>>> conn = Connection()
>>> db = conn.blog

>>> for i in xrange(1000000):
    u = dict(name = "user" + str(i), age = randint(10, 90))
    db.users.insert(u)

开始调优操作。

> db.setProfilingLevel(1)
{ "was" : 0, "ok" : 1 }

> db.users.find().sort({age:-1}).limit(10000)
{ "_id" : ObjectId("4c50dc07499b1404c60f42e5"), "age" : 90, "name" : "user165" }
{ "_id" : ObjectId("4c50dc07499b1404c60f42e8"), "age" : 90, "name" : "user168" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4350"), "age" : 90, "name" : "user272" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4358"), "age" : 90, "name" : "user280" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4375"), "age" : 90, "name" : "user309" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4433"), "age" : 90, "name" : "user499" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4480"), "age" : 90, "name" : "user576" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4484"), "age" : 90, "name" : "user580" }
{ "_id" : ObjectId("4c50dc07499b1404c60f44cf"), "age" : 90, "name" : "user655" }
{ "_id" : ObjectId("4c50dc07499b1404c60f44fb"), "age" : 90, "name" : "user699" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4517"), "age" : 90, "name" : "user727" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4688"), "age" : 90, "name" : "user1096" }
{ "_id" : ObjectId("4c50dc07499b1404c60f46a8"), "age" : 90, "name" : "user1128" }
{ "_id" : ObjectId("4c50dc07499b1404c60f46ae"), "age" : 90, "name" : "user1134" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4740"), "age" : 90, "name" : "user1280" }
{ "_id" : ObjectId("4c50dc07499b1404c60f479b"), "age" : 90, "name" : "user1371" }
{ "_id" : ObjectId("4c50dc07499b1404c60f479d"), "age" : 90, "name" : "user1373" }
{ "_id" : ObjectId("4c50dc07499b1404c60f480f"), "age" : 90, "name" : "user1487" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4842"), "age" : 90, "name" : "user1538" }
{ "_id" : ObjectId("4c50dc07499b1404c60f4844"), "age" : 90, "name" : "user1540" }
has more

> db.system.profile.find()
{ 
    "ts" : "Thu Jul 29 2010 09:47:47 GMT+0800 (CST)", 
    "info" : "query blog.users 
        ntoreturn:10000 scanAndOrder  
        reslen:518677 
        nscanned:1000000 
        query: { query: {}, orderby: { age: -1.0 } }  
        nreturned:10000 1443ms", 
    "millis" : 1443 
}

system.profile 中记录下一条耗时过长的操作。

  • ts: 操作执行时间。
  • info: 操作详细信息。
  • info.query: 查询目标(数据库.集合)。
  • info.ntoreturn: 客户端期望返回的文档数量。
  • info.nscanned: 服务器实际扫描的文档数量。
  • info.reslen: 查询结果字节长度。
  • info.nreturnned: 查询返回文档数。
  • millis: 操作耗时(毫秒)。
很显然,该操作扫描的文档过多(info.nscanned),通常是没有使用索引造成的。我们用 explain() 看看服务器如何执行执行该命令。

> db.users.find().sort({age:-1}).limit(10000).explain()
{
        "cursor" : "BasicCursor",
        "nscanned" : 1000000,
        "nscannedObjects" : 1000000,
        "n" : 10000,
        "scanAndOrder" : true,
        "millis" : 1412,
        "indexBounds" : {

        }
}

没有索引自然很慢了,建个索引看看效果。

> db.users.ensureIndex({age:-1})

> db.users.find().sort({age:-1}).limit(10000).explain()
{
        "cursor" : "BtreeCursor age_-1",
        "nscanned" : 10000,
        "nscannedObjects" : 10000,
        "n" : 10000,
        "millis" : 211,
        "indexBounds" : {
                "age" : [
                        [
                                {
                                        "$maxElement" : 1
                                },
                                {
                                        "$minElement" : 1
                                }
                        ]
                ]
        }
}

速度提升非常明显。最后别忘了 Profiler 本身也会影响服务器性能,不用的时候要关掉。

> db.setProfilingLevel(0)
{ "was" : 1, "ok" : 1 }

除了使用 setProfilingLevel 命令外,也可以在 mongod 参数中启用 profiler,不推荐。

--profile arg             0=off 1=slow, 2=all
--slowms arg (=100)       value of slow for profile and console log

2. Optimization

优化建议:

  • 如果 nscanned 远大于 nreturned,那么需要使用索引。
  • 如果 reslen 返回字节非常大,那么考虑只获取所需的字段。
  • 执行 update 操作时同样检查一下 nscanned,并使用索引减少文档扫描数量。
  • 使用 db.eval() 在服务端执行某些统计操作。
  • 减少返回文档数量,使用 skip & limit 分页。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值