首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >MongoDB + Python -非常慢的简单查询

MongoDB + Python -非常慢的简单查询
EN

Stack Overflow用户
提问于 2014-01-28 20:29:51
回答 3查看 3K关注 0票数 0

我有一个开源能源监视器(http://openenergymonitor.org),它每隔5秒钟记录一次我家的电力使用情况,所以我认为这将是一个非常适合使用MongoDB的应用程序。我在Apache中运行了一个Flask应用程序,它使用MongoEngine与MongoDB进行接口。

现在我正在一个RaspberryPi上运行所有这些,所以我并不期望有惊人的性能,但是一个简单的查询大约需要20秒,即使对于这个有限的硬件来说,这看起来也很慢。

我有以下模式:

代码语言:javascript
运行
复制
class Reading(db.Document):
    created_at = db.DateTimeField(default=datetime.datetime.now, required=True)
    created_at_year = db.IntField(default=datetime.datetime.now().year, required=True)
    created_at_month = db.IntField(default=datetime.datetime.now().month, required=True)
    created_at_day = db.IntField(default=datetime.datetime.now().day, required=True)
    created_at_hour = db.IntField(default=datetime.datetime.now().hour, required=True)
    battery = db.IntField()
    power = db.IntField()
    meta = {
        'indexes': ['created_at_year', 'created_at_month', 'created_at_day', 'created_at_hour']
    }

最近几天,我有大约36,000份阅读资料。下面的代码运行得非常快:

代码语言:javascript
运行
复制
def get_readings_count():
    count = '<p>Count: %d</p>' % Reading.objects.count()
    return count

def get_last_24_readings_as_json():
    readings = Reading.objects.order_by('-id')[:24]
    result = "["
    for reading in reversed(readings):
        result += str(reading.power) + ","
    result = result[:-1]
    result += "]"
    return result

但是做一个简单的过滤器:

代码语言:javascript
运行
复制
def get_today_readings_count():
    todaycount = '<p>Today: %d</p>' % Reading.objects(created_at_year=2014, created_at_month=1, created_at_day=28).count()
    return todaycount

大约需要20秒--今天大约有11,000个读数。

我是应该放弃对Pi的期望,还是说我可以做一些调整来从MongoDB中获得更多的性能?

Mongo2.1.1在Debian Wheezy上

更新29/1/2014:

以下是getIndexes()和explain()的结果:

代码语言:javascript
运行
复制
> db.reading.getIndexes()
[
    {
        "v" : 1,
        "key" : {
            "_id" : 1
        },
        "ns" : "sensor_network.reading",
        "name" : "_id_"
    },
    {
        "v" : 1,
        "key" : {
            "created_at_year" : 1
        },
        "ns" : "sensor_network.reading",
        "name" : "created_at_year_1",
        "background" : false,
        "dropDups" : false
    },
    {
        "v" : 1,
        "key" : {
            "created_at_month" : 1
        },
        "ns" : "sensor_network.reading",
        "name" : "created_at_month_1",
        "background" : false,
        "dropDups" : false
    },
    {
        "v" : 1,
        "key" : {
            "created_at_day" : 1
        },
        "ns" : "sensor_network.reading",
        "name" : "created_at_day_1",
        "background" : false,
        "dropDups" : false
    },
    {
        "v" : 1,
        "key" : {
            "created_at_hour" : 1
        },
        "ns" : "sensor_network.reading",
        "name" : "created_at_hour_1",
        "background" : false,
        "dropDups" : false
    }
]

> db.reading.find({created_at_year: 2014, created_at_month: 1, created_at_day: 28 }).explain()
{
    "cursor" : "BtreeCursor created_at_day_1",
    "isMultiKey" : false,
    "n" : 15689,
    "nscannedObjects" : 15994,
    "nscanned" : 15994,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 5,
    "nChunkSkips" : 0,
    "millis" : 25511,
    "indexBounds" : {
        "created_at_day" : [
            [
                28,
                28
            ]
        ]
    },
    "server" : "raspberrypi:27017"
}

更新2月4日

好的,我删除了索引,在created_at上设置了一个新的索引,删除了所有的记录,并留了一天来收集新的数据。我刚刚对今天的数据运行了一个查询,花费了更长的时间(48秒):

代码语言:javascript
运行
复制
> db.reading.find({'created_at': {'$gte':ISODate("2014-02-04")}}).explain()
{
    "cursor" : "BtreeCursor created_at_1",
    "isMultiKey" : false,
    "n" : 14189,
    "nscannedObjects" : 14189,
    "nscanned" : 14189,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 9,
    "nChunkSkips" : 0,
    "millis" : 48653,
    "indexBounds" : {
        "created_at" : [
            [
                ISODate("2014-02-04T00:00:00Z"),
                ISODate("292278995-12-2147483314T07:12:56.808Z")
            ]
        ]
    },
    "server" : "raspberrypi:27017"
}

数据库中只有16177条记录,只有一个索引。大约有111 in的空闲内存,所以内存中的索引不应该出现问题。我想我得把它写下来,因为Pi不够强大,不能胜任这项工作。

EN

回答 3

Stack Overflow用户

发布于 2014-01-28 23:41:55

确定您的索引已经创建了吗?您能否提供您的集合的getIndexes()输出?

例:db.my_collection.getIndexes()

以及对查询的解释

代码语言:javascript
运行
复制
db.my_collection.find({created_at_year: 2014, created_at_month: 1, created_at_day: 28 }).explain()

PS:当然,我必须同意@美学的观点,即你储存的东西比你所需要的要多得多。

29/1/2014更新

太棒了!正如您所看到的,您有四个不同的索引,可以创建一个包含所有索引的复合索引。

定义

db.my_collection.ensureIndex({created_at_year: 1, created_at_month: 1, created_at_day: 1, created_at_hour: 1 })

将为您提供更精确的索引,使您能够查询:

  • year
  • yearmonth
  • yearmonthday
  • yearmonthdayhour

这将使您的查询(四个键)更快,因为索引数据将满足您的所有条件!

请注意,键在ensureIndex()中的顺序是至关重要的,该顺序实际上定义了上述查询列表!

还请注意,如果您只需要这4个字段,而不是指定正确的投影

例:

db.my_collection.find({created_at_year: 2014, created_at_month: 1, created_at_day: 28}, { created_at_year: 1, created_at_month: 1, created_at_day: 1 })

然后只使用索引,这是最大的性能!

票数 1
EN

Stack Overflow用户

发布于 2014-01-28 20:50:11

可能与您保存日期5次有关(保存created_at),然后如果您想在视图中保存月份、日期等,只需将created_at值转换为只显示月份、日期等。

票数 0
EN

Stack Overflow用户

发布于 2014-01-29 21:50:15

我想知道索引是否不适合你的覆盆子π的记忆。由于MongoDB每个查询只能使用一个索引,而且它似乎只使用created_by_day查询,所以可以尝试删除索引,并在created_at时间戳上用索引替换它们。然后,您可以通过去掉created_at_*字段来缩小文档的大小。

您可以轻松地从映射缩减函数中的ISO日期中提取日期、月份、年份等,或者使用聚合框架日期运算符

然后,对today的查询如下所示:

代码语言:javascript
运行
复制
db.reading.find({'created_at':{'$gte':ISODate("2014-01-29"), '$lt':ISODate("2014-01-30")}})

我认为有趣的是,您选择了一个适合在嵌入式设备上运行大数据的数据库。我很好奇事情会怎样发展。我有一个类似的小工具,用BerkeleyDB存储读数。不要忘记,32位操作系统上的MongoDB对于整个数据库的最大大小为2GB。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21416150

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档