big data

mongodb性能调优初探

2018-02-05  本文已影响139人  若尘0328
> show dbs
admin  0.000GB
local  0.000GB
yang   0.615GB
> use yang
switched to db yang
> show collections
system.profile
users
> db.users.find().count()
10000000

这里选取一千万条数据来说明问题,这些数据都是没有索引的,首先我们要做的是找出慢查询。
1.开启慢查询分析器
db.setProfilingLevel(1,300)
第一个参数“1”表示记录慢查询,还可以选择“0”或者“2”,分别代表不记录数据和记录所有读写操作,我们一般会设置成“1”,第二个参数代表慢查询阈值,只有查询执行时间超过300ms才会记录。
2.查询所记录的慢查询
监控结果保存在一个特殊的盖子集合system.profile里,这个集合分配了128kb的空间,要确保监控分析数据不会消耗太多的系统性资源;盖子集合维护了自然的插入顺序,可以使用$natural操作符进行排序,
> db.system.profile.find().sort({'$natural':-1}).limit(5).pretty()会打印出最耗时的5条查询,在这里我们选取其中的一条来看:

{
        "op" : "command",
        "ns" : "yang.users",
        "command" : {
                "explain" : {
                        "find" : "users",        //这里是所查询的集合
                       //重点在这里,查询条件是 "username" = "dede"
                        "filter" : {
                                "username" : "dede"
                        }
                },
                "verbosity" : "allPlansExecution"
        },
        "numYield" : 78375,
        "locks" : {
                "Global" : {
                        "acquireCount" : {
                                "r" : NumberLong(156752)
                        }
                },
                "Database" : {
                        "acquireCount" : {
                                "r" : NumberLong(78376)
                        }
                },
                "Collection" : {
                        "acquireCount" : {
                                "r" : NumberLong(78376)
                        }
                }
        },
        "responseLength" : 848,
        "protocol" : "op_command",
        "millis" : 6800,
        "ts" : ISODate("2018-02-05T10:35:07.576Z"),
        "client" : "127.0.0.1",
        "appName" : "MongoDB Shell",
        "allUsers" : [ ],
        "user" : ""
}

3.上面的还不够直观,我们现在构造这个查询并用explain来分析具体慢在了那里:
> db.users.find({"username":"dede"}).explain('executionStats')
explain的入参可选值为:
"queryPlanner" 是默认值,表示仅仅展示执行计划信息;
"executionStats" 表示展示执行计划信息同时展示被选中的执行计划的执行情况信息;
 "allPlansExecution" 表示展示执行计划信息,并展示被选中的执行计划的执行情况信息,还展示备选的执行计划的执行情况信息;
我们一般会选用executionStats,打印出来的内容如下:

> db.users.find({"username":"dede"}).explain('executionStats')
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "yang.users",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "username" : {
                                "$eq" : "dede"
                        }
                },
         //执行计划会有多种,这里列出的是胜出的执行计划
                "winningPlan" : {
         //重点看下面的stage,collscan代表全表扫描,如果命中索引这里应该是ixscan,index scan
                        "stage" : "COLLSCAN",
                        "filter" : {
                                "username" : {
                                        "$eq" : "dede"
                                }
                        },
                        "direction" : "forward"
                },
         //下面列举的是没有使用的执行计划
                "rejectedPlans" : [ ]
        },
        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 1428802,
                "executionTimeMillis" : 7080,
                "totalKeysExamined" : 0,
                "totalDocsExamined" : 10000000,
                "executionStages" : {
                        "stage" : "COLLSCAN",
                        "filter" : {
                                "username" : {
                                        "$eq" : "dede"
                                }
                        },
                        "nReturned" : 1428802,//查询所返回的条数
                        "executionTimeMillisEstimate" : 6633,//执行总时间
                        "works" : 10000002,
                        "advanced" : 1428802,
                        "needTime" : 8571199,
                        "needYield" : 0,
                        "saveState" : 78368,
                        "restoreState" : 78368,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "direction" : "forward",
                        "docsExamined" : 10000000//总共扫描了一千万条数据
                }
        },
        "serverInfo" : {
                "host" : "localhost.localdomain",
                "port" : 27022,
                "version" : "3.4.10",
                "gitVersion" : "078f28920cb24de0dd479b5ea6c66c644f6326e9"
        },
        "ok" : 1  
}

讲上面的结果简化如下:

queryPlanner(执行计划描述)
        winningPlan(被选中的执行计划) 
                stage(可选项:COLLSCAN 没有走索引;IXSCAN使用了索引) 
        rejectedPlans(候选的执行计划) 
 executionStats(执行情况描述)
        nReturned (返回的文档个数) 
        executionTimeMillis(执行时间ms) 
        totalKeysExamined (检查的索引键值个数) 
        totalDocsExamined (检查的文档个数)

我们最终的优化目标

1.根据需求建立索引
2.每个查询都要使用索引以提高查询效率, winningPlan. stage 必须为IXSCAN ;
3.追求totalDocsExamined = nReturned
属性名 类型 说明
background boolean 是否后台构建索引,在生产环境中,如果数据量太大,构建索引可能会消耗很长时间,为了不影响业务,可以加上此参数,后台运行同时还会为其他读写操作让路
unique boolean 是否为唯一索引
name string 索引名字
sparse boolean 是否为稀疏索引,索引仅引用具有指定字段的文档。

那么mongodb的索引又是怎么建立的呢?
首先,mongodb的索引分为单建索引db.users. createIndex({age:-1}); 、复合索引db.users. createIndex({username:1,age:-1,country:1});、多键索引(在数组的属性上建立索引)db.users. createIndex({favorites.city:1})
创建索引的语法如下:
db.collection.createIndex(keys, options)
语法中 Key 值为要创建的索引字段,1为指定按升序创建索引,如果你想按降序来创建索引指定为-1,也可以指定为hashed(哈希索引)。
语法中options为索引的属性,属性说明见下表;

属性名 类型 说明
background boolean 是否后台构建索引,在生产环境中,如果数据量太大,构建索引可能会消耗很长时间,为了不影响业务,可以加上此参数,后台运行同时还会为其他读写操作让路
unique boolean 是否为唯一索引
name string 索引名字
sparse boolean 是否为稀疏索引,索引仅引用具有指定字段的文档。

举例如下:
单键唯一索引:db.users. createIndex({username :1},{unique:true});
单键唯一稀疏索引:db.users. createIndex({username :1},{unique:true,sparse:true});
复合唯一稀疏索引:db.users. createIndex({username:1,age:-1},{unique:true,sparse:true});
创建哈希索引并后台运行:db.users. createIndex({username :'hashed'},{background:true});
删除索引
根据索引名字删除某一个指定索引:db.users.dropIndex("username_1");
删除某集合上所有索引:db.users.dropIndexs();
重建某集合上所有索引:db.users.reIndex();
查询集合上所有索引:db.users.getIndexes();
既然已经知道了如何创建索引,那么我们就给users表的username字段创建索引
> db.users.createIndex({"username":1},{"name":"username_1","sparse":true,"background":true});
看看有没有创建成功

> db.users.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "yang.users"
        },
        {
                "v" : 2,
                "key" : {
                        "username" : 1
                },
                "name" : "username_1",
                "ns" : "yang.users",
                "sparse" : true,
                "background" : true
        }
]

可以看到索引已经创建成功,索引得名字就是username_1,我们再查看一下执行计划

> db.users.find({"username":"dede"}).explain("executionStats")
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "yang.users",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "username" : {
                                "$eq" : "dede"
                        }
                },
                "winningPlan" : {
                        "stage" : "FETCH",
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "username" : 1
                                },
                                "indexName" : "username_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "username" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : true,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "username" : [
                                                "[\"dede\", \"dede\"]"
                                        ]
                                }
                        }
                },
                "rejectedPlans" : [ ]
        },
        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 1428802,
                "executionTimeMillis" : 4399,
                "totalKeysExamined" : 1428802,
                "totalDocsExamined" : 1428802,
                "executionStages" : {
                        "stage" : "FETCH",
                        "nReturned" : 1428802,
                        "executionTimeMillisEstimate" : 4002,
                        "works" : 1428803,
                        "advanced" : 1428802,
                        "needTime" : 0,
                        "needYield" : 0,
                        "saveState" : 11308,
                        "restoreState" : 11308,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "docsExamined" : 1428802,
                        "alreadyHasObj" : 0,
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "nReturned" : 1428802,
                                "executionTimeMillisEstimate" : 765,
                                "works" : 1428803,
                                "advanced" : 1428802,
                                "needTime" : 0,
                                "needYield" : 0,
                                "saveState" : 11308,
                                "restoreState" : 11308,
                                "isEOF" : 1,
                                "invalidates" : 0,
                                "keyPattern" : {
                                        "username" : 1
                                },
                                "indexName" : "username_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "username" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : true,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "username" : [
                                                "[\"dede\", \"dede\"]"
                                        ]
                                },
                                "keysExamined" : 1428802,
                                "seeks" : 1,
                                "dupsTested" : 0,
                                "dupsDropped" : 0,
                                "seenInvalidated" : 0
                        }
                }
        },
        "serverInfo" : {
                "host" : "localhost.localdomain",
                "port" : 27022,
                "version" : "3.4.10",
                "gitVersion" : "078f28920cb24de0dd479b5ea6c66c644f6326e9"
        },
        "ok" : 1
}

我们可以看到,查询时间从原来的6s变成了765ms,并且执行计划中的stage:IXSCAN,再看看"docsExamined" : 1428802,"nReturned" : 1428802,,两者相等,至此我们完成了mongodb的初步调优,这里只是列举的非常简单的场景,具体的调优还是要看各位的实际情况去分析。

上一篇 下一篇

猜你喜欢

热点阅读