2014-01-28 5 views
0

저는 5 초마다 우리 집의 전력 사용량을 기록하는 오픈 소스 에너지 모니터 (http://openenergymonitor.org)를 가지고 있습니다. 그래서 MongoDB와 함께 재생하는 완벽한 응용 프로그램이라고 생각했습니다. 아파치에서 MongoEngine을 사용하여 MongoDB와의 인터페이스를 수행하는 Flask Python 애플리케이션이 있습니다.MongoDB + Python - 매우 느린 간단한 쿼리

이제는 RaspberryPi에서이 모든 것을 실행하기 때문에 놀라운 성능을 기대하지는 않지만 간단한 쿼리는 약 20 초 정도 소요됩니다.이 제한된 하드웨어에도 느려집니다. 나는 현재 지난 며칠에서 저장 약 36,000 판독이

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'] 
    } 

:

나는 다음과 같은 모델을 가지고있다. 다음 코드는 슈퍼 빠른 실행 :

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 판독이 있습니다.

나는 파이를 더 이상 기대하지 않거나 MongoDB에서 더 많은 성능을 얻기 위해 할 수있는 조정이 있습니까?

몽고가

업데이트 29/1/2014 데비안 위지에 2.1.1 : 여기 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" 
} 

업데이트 4 이월

좋아, 그래서 삭제 한 인덱스는, 새로운 설정 하나는 created_at에, 모든 레코드를 삭제하고 새로운 데이터를 수집하기 위해 하루 남겨 두었습니다. 난 그냥 오늘의 데이터에 대한 쿼리를 실행했는데 시간이 더 오래 (48초)에 나섭니다 : 데이터베이스에서만 16,177 기록과 하나의 인덱스입니다

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

. 약 111MB의 여유 메모리가 있으므로 메모리에 맞는 색인에 문제가 있어서는 안됩니다. 나는이 작업을 위해 충분히 강력하지 않은 Pi로 이것을 써야 할 것 같아요.

답변

0

아마도번 저장하면 (예 : created_at 유지), 월, 일 등을보기에 원할 경우 created_at 값을 월, 일 표시로 변환하십시오. 등

+0

map.hdue를 사용하여 다양한 방식으로 데이터를 집계 할 계획이므로 일일 시간을 각지도 함수에서 추출해야하기 때문에 이미 사용하고 있습니다. – littlecharva

+0

그냥 created_at을 사용해보고, 그 값에서 날짜, 월 등을 얻는 함수를 작성하여 성능을 향상시킬 수 있습니다. 두 접근법에 시간을 할애하고 어느 것이 더 나은지를 볼 수 있습니다. 최적의 균형을 찾지 못할 수도 있습니다. .내가 언급 한 바와 같이, 필자가 언급 한 방식대로 데이터베이스 조회수를 줄이면됩니다. – 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 갱신

딱! 보시다시피 4 개의 서로 다른 색인을 만들 수있는 경우 하나의 복합 색인을 만들 수 있습니다. 정의

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

!

+0

응답 해 주셔서 감사 드리며 결과가있는 게시물을 업데이트했습니다. – littlecharva

+0

@littlecharva 내 업데이트를 확인하십시오. – xlembouras

+0

다시 한 번 감사드립니다 - 아직 복합 인덱스를 추가하지 않았지만 하나의 인덱스 만 다시 대상으로하는 Explain 쿼리를 다시 실행해야합니다 : db.reading.find ({created_at_day : 28}). explain() 이것은 여전히 ​​실행됩니다. 13 초. 그게 제가 기대할 수있는 최고의 성능입니까? – littlecharva

0

인덱스가 귀하의 나무 딸기 파이의 메모리에 맞지 않을지 궁금합니다. MongoDB는 쿼리 당 하나의 인덱스 만 사용할 수 있으며 created_by_day 쿼리 만 사용하는 것으로 보이므로 인덱스를 삭제하고 created_at 타임 스탬프의 인덱스로 대체 할 수 있습니다. 그런 다음 created_at_* 필드를 제거하여 문서의 크기를 줄일 수 있습니다.

지도 축소 기능이나 ISO을 사용하여 ISO 날짜에서 일, 월, 연도 등을 쉽게 추출 할 수 있습니다. 나는 당신이 큰 데이터가 임베디드 디바이스에서 실행하는 데이터베이스가 적합한 광고를 선택했다는 흥미로운 생각

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

:

today에 대한 질의 사항은 다음과 같이된다. 나는 그것이 어떻게 될지 궁금해. 비슷한 가제트를 사용하고 판독 값을 저장하는 데 BerkeleyDB을 사용했습니다. 32 비트 OS에서 MongoDB는 전체 데이터베이스에 대해 최대 크기가 2GB임을 잊지 마십시오.

+0

하나의 색인 사용 결과에 대한 원래 게시물을 업데이트하십시오. 나는 BIG DATA 데이터베이스를 사용하기를 원했고, 5 초마다 센서를 읽는 것과 같은 느낌이 들었고, 저전력 디바이스를 사용하는 것은 거의 Mini-Big Data 프로젝트와 같았습니다. BerkeleyDB를 살펴 보겠습니다. – littlecharva