2014-01-28 3 views
0

У меня есть монитор энергии с открытым исходным кодом (http://openenergymonitor.org), который регистрирует использование мощности моего дома каждые пять секунд, поэтому я подумал, что это будет отличное приложение для игры с MongoDB. У меня есть приложение Flask Python, работающее в Apache с использованием MongoEngine для взаимодействия с MongoDB.MongoDB + 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'] 
    } 

я в настоящее время есть около 36000 показаний, хранящихся в последние пару дней. Следующий код работает супер быстрый:

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?

Монго 2.1.1 на Debian свистящих

Обновление 29/1/2014:

В ответ на ответ ниже, вот результаты getIndexes() и объяснить():

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

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

Это только 16177 записей в базе данных, и только один индекс. Там около 111 МБ свободной памяти, поэтому не должно быть проблем с установкой индекса в памяти. Думаю, мне придется написать это, поскольку Пи не будет достаточно сильным для этой работы.

ответ

0

возможно, что-то делать с вами, сохраняя дату 5 раз сохранить его один раз (т. Е. Сохранить created_at), а затем, если вы хотите, чтобы месяц, день и т. Д. В вашем представлении, просто преобразуйте значение created_at, чтобы просто отображать месяц, день etc

+0

Я разбиваю дату и время на его составные части, потому что планирую собирать данные различными способами с помощью mapreduce, поэтому вместо того, чтобы извлекать день или час в каждой функции карты, он уже используется для меня. – littlecharva

+0

попробуйте использовать только created_at и напишите функции, чтобы получить дату, месяц и т. Д. Только от этого значения, делая это таким образом, чтобы повысить производительность, вы могли бы разывать два подхода и посмотреть, какой из них лучше, вы можете наткнуться на оптимальный баланс .Я также должен добавить, делая это так, как упомянутый ive потребует меньше обращений к базам данных, чем то, как вы это сделали. – Aesthete

1

Вы уверены, что ваш индекс будет создан? Вы могли бы обеспечить выпуск 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
  • year и month
  • year и month и day
  • year и month и day и hour

Это сделает ваши запросы (с помощью четырех клавиш) гораздо быстрее, потому что все ваши критерии будут выполнены в индексных данных!

Обратите внимание, что порядок ключей в 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 })

будет использоваться только индекс, который является максимальной производительностью!

+0

Спасибо за ответ, я обновил сообщение с результатами. – littlecharva

+0

@littlecharva проверить мое обновление. – xlembouras

+0

Еще раз спасибо - я еще не добавил составной индекс, но повторно запустил запрос объяснения, снова нацелив только один индекс: db.reading.find ({created_at_day: 28}). Explain(), и это все еще выполняется 13 секунд. Это лучшая работа, на которую я могу надеяться? – littlecharva

0

Интересно, не указали ли индексы в памяти вашего малинового пи. Поскольку MongoDB может использовать только один индекс для каждого запроса, и он, похоже, использует только запрос created_by_day, вы можете попробовать сбросить индексы и заменить их индексом на отметке времени created_at. Затем вы можете уменьшить размер своих документов, избавившись от полей created_at_*.

Вы можете легко извлечь день, месяц, год и т. Д. Из даты ISO в функции уменьшения карты или с помощью структуры агрегации date operators.

Запрос для today становится чем-то вроде этого:

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

Я думаю, что это интересно, что вы выбрали базу данных рекламируются как подходящие для BIG данных для запуска на встроенном устройстве. Мне любопытно, как это сработает. У меня есть аналогичный гаджет, и я использовал BerkeleyDB для хранения показаний. Не забывайте, что MongoDB на 32-битной ОС имеет максимальный размер 2 ГБ для всей базы данных.

+0

См. Мое обновление к исходному сообщению для результатов использования одного индекса. Я решил использовать базу данных BIG DATA, поскольку я хотел поиграть с ней, и мне казалось, что каждый раз, когда я читаю датчик, каждые пять секунд, а использование устройства с низким энергопотреблением почти похоже на проект с мини-большими данными. Спасибо, BerkeleyDB, спасибо. – littlecharva

Смежные вопросы