之前几周写过一个关于提升MYSQL查询速度的文章。这次,我们来讨论以下如何有效的提升MongoDB的查询速度。首先,我们在MongoDB中有一个数据集合(可以理解为表,但是又不能叫表,具体MongoDB和MySQL的区别还请大家搜索以下)有将近100万条数据,大概3GB左右大小。数据集合中的每一条数据的数据格式大致相同,可能会有略微多或者少几个字段。该数据集合使用了默认的_id作为索引字段。然而,由于业务需求,我们发现在使用不同的条件进行搜索时,存在严重的性能问题(由于服务器只有8G内存,我们的MongoDB只是用约4G内存,也是有点残忍的)。
首先,我们使用最简单的搜索语句进行搜索,耗时约1.382秒。
db.mydata.find({}).projection({}).sort({_id:-1}).limit(100)
当我们使用_id作为搜索条件时,差不多耗时1.403秒。
db.mydata.find({_id:{$lt:ObjectId('5d2aef47013b950a0ec84ca1')}}).projection({}).sort({_id:-1}).limit(100)
当我们使用以下语句统计整个表中的数据量,大概花费1.280秒。
db.mydata.find({}).count()
当我们使用一个有条件的语句来统计数据量时,大概花费9到13秒(数据量在450条左右)。
db.mydata.find({plan_id:2000}).sort({_id:-1}).count()
因此,我们大概能够得出一个结论,在搜索条件中如果该条件没有做索引,搜索时间可能是做了索引的10倍。下面我们将为该表中的plan_id添加索引,在开始之前我们先做一下备份。
备份MongoDB
我们可以使用以下语句备份MongoDB中的指定数据库中的某个集合:
./bin/mongodump -h 127.0.0.1:27017 --username root --password "123456" --authenticationDatabase "admin" --collection mydata--db mydb --out ./backup/targetfolder
如果要备份整个数据库的话,只需要删除--collection
参数。
在MongoDB中创建索引
接下来我们可以在MongoDB中对某个Collection的字段创建索引,并在后台允许(这样不会阻塞其它操作)。本示例中,我们将对mydata
中的plan_id
创建索引。如下:
db.mydata.createIndex({plan_id: 1}, {background: true})
如果创建索引成功,会有如下内容显示:
{ "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 }
建立好索引以后我们的再次使用以上语句发现搜索时间只有0.03到0.04秒左右。性能提升有点惊人。
注意:即使创建了索引,MongoDB也并不保证搜索一定会使用该索引,即使使用explain得出的结果会使用该索引,但是在真实情况下也不保证会用。因此,如果对于查询需求非常清楚,建议在查询语句中使用hint强制指定索引。具体见《单台MongoDB服务器的不可承受之重》。
优化搜索条件
建立好索引以后搜索速度会有极大的提高,但是如果查询次数多的话,也是架不住1秒千次以上的查询搜索的。按照以上的查询语句为例,如果我们要查询1000个plan_id,最简单的方案是在代码中写一个1000次的for循环来逐个查询,然而这样的查询方式显然是不恰当的。对于以上案例,可以使用in查询条件完成,具体可以参考MongoDB手册。
MongoDB数据库分片
随着数据量的不断增长,同一条查询语句在不同情况下,查询需要消耗的时间是千差万别的。我们知道MongoDB会把数据加载到内存中以提高搜索速度。然而内存是有限的,例如当前我使用的Mongodb数据库服务器是8核32GB的。但是数据量大约有1TB左右。MongoDB不可能把所有数据都加载到内存中,因此只能加载它认为重要的数据。这样当业务需求搜索冷数据时(数据未被加载到内存中),就会花比较多的时间把数据从硬盘上读取出来,存量数据越大,读取所花费的时间就越长。在这种情况下,比较通用的方案是建立MongoDB集群并使用分片功能。
扫码联系船长