[英]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
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 })
那么只会使用索引,这是最高的性能!
可能与您保存日期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.