簡體   English   中英

MongoDB + Python-非常慢的簡單查詢

[英]MongoDB + Python - very slow simple query

我有一個開源能源監控器( http://openenergymonitor.org ),它每五秒鍾記錄一次我家的用電情況,因此我認為這是與MongoDB一起使用的理想應用程序。 我有一個在MongoEngine中使用MongoEngine與MongoDB交互的Flask Python應用程序。

現在,我在RaspberryPi上運行所有這些功能,因此我並不期望獲得令人難以置信的性能,但是一個簡單的查詢大約需要20秒,即使對於這種有限的硬件,這似乎也很慢。

我有以下模型:

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個讀數。 以下代碼運行超級快:

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

但是做一個簡單的過濾器:

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獲得更高的性能?

Debian Wheezy上的Mongo 2.1.1

2014年2月1日更新:

為了回答以下問題,以下是getIndexes()和explain()的結果:

> 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秒):

> 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"
}

這樣數據庫中只有16,177條記錄,只有一個索引。 大約有111MB的可用內存,因此索引適合內存應該不會有問題。 我想我將不得不撇掉它,因為Pi的功能不足以完成這項工作。

您確定您的索引已創建? 您能否提供集合的getIndexes()的輸出

例如: db.my_collection.getIndexes()

以及查詢的解釋

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

PS:當然,我必須同意@Aesthete的事實,即您存儲的存儲量遠遠超過了您的需要...

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 })

那么只會使用索引,這是最高的性能!

可能與您保存日期5次有關,請保存一次(即保留created_at),然后,如果您希望在視圖中顯示月份,日期等,只需將created_at值轉換為僅顯示月份,日期等即可。

我想知道索引是否不適合您的樹莓派的內存。 由於MongoDB每個查詢只能使用一個索引,並且似乎只使用created_by_day查詢,因此您可以嘗試刪除索引,並在created_at時間戳上將其替換為索引。 然后,可以通過擺脫created_at_*字段來減小文檔的大小。

您可以在map reduce函數中或使用聚合框架日期運算符輕松地從ISO日期中提取日,月,年等。

today的查詢將變成這樣:

db.reading.find({'created_at':{'$gte':ISODate("2014-01-29"), '$lt':ISODate("2014-01-30")}})

我認為您選擇了一個宣傳為適合BIG數據在嵌入式設備上運行的數據庫很有趣。 我很好奇它會如何工作。 我有一個類似的小工具,並使用BerkeleyDB來存儲讀數。 不要忘記,在32位操作系統上,整個數據庫的MongoDB的最大大小為2GB。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM