Langsame django Datenbank Operationen auf großen (ish) Datensatz.

Ich habe ein System eingerichtet, um die twitter Echtzeit Stream Probe zu filtern. Offensichtlich ist die Datenbank schreibt zu langsam, um mit etwas komplexer als ein paar Low-Volume-Keywords zu halten. Ich habe django-rq als ein einfaches Warteschlangen-System implementiert, um die Tweets in eine redisbasierte Warteschlange zu schieben, als sie hereinkamen, und das funktioniert super. Mein Problem ist auf der anderen Seite. Der Kontext zu dieser Frage ist, ich habe ein System, das gerade läuft, mit 1,5m Tweets für die Analyse, und weitere 375.000 in der Warteschlange durch redis. Bei aktuellen Raten, wird es mich nehmen ~ 3 Tage nachholen, wenn ich die Ströme ausschalte, was ich nicht will. Wenn ich die Ströme pflege, dann dauert es etwa einen Monat, auf meine letzten Schätzungen.

Die Datenbank hat ein paar Millionen Zeilen über zwei Haupttabellen, und die Schreibvorgänge sind sehr langsam. Die optimale Anzahl von rq-Arbeiter scheint vier zu sein, und das ist durchschnittlich bei 1,6 Warteschlangenaufgaben pro Sekunde. (Code dessen, was unten genannt wird). Ich dachte, dass vielleicht das Problem war die Öffnung der DB-Verbindungen für jede neue Warteschlange Aufgabe, so setzen CONN_MAX_AGE auf 60, aber das hat nichts verbessert.

Nachdem ich dies gerade auf localhost getestet habe, habe ich über 13 Schriften / Sekunde, auf einem Macbook 2011, mit Chrome, etc etc ausgeführt, aber es gibt nur ein paar tausend Zeilen in dieser Datenbank, die mich zu glauben, es ist Größe verwandt. Es gibt ein paar get_or_create Befehle, die ich benutze (siehe unten), was könnte verlangsamen Dinge, aber kann nicht sehen, andere Weg durch die Verwendung von ihnen – ich muss überprüfen, ob der Benutzer existiert, und ich muss überprüfen, ob Der tweet existiert bereits (ich könnte vermutlich, vermute ich, den letzteren zu einem Versuch machen, außer auf der Basis, dass Tweets, die aus dem Live-Stream kommen, aus offensichtlichen Gründen nicht existieren sollten.) Würde ich viel Leistung gewinnen Von dem Da dies noch läuft, bin ich sehr daran interessiert, den Code etwas zu optimieren und bekomme schnellere und effizientere Arbeiter dort, damit ich aufholen kann! Würde ein Pre-Vetting-Arbeiter laufen, um die Arbeit zu bauen? (Dh so kann ich Batch erstellen Benutzer, die nicht existieren, oder etwas ähnliches?)

Ich laufe ein 4 Core / 8Gb Ram Tröpfchen auf digitalem Ozean, also das Gefühl, das ist eine ziemlich schreckliche Leistung und vermutlich Code verwandt. Wo gehe ich hier falsch?
(Ich habe das hier noch als Code-Review gepostet, da ich denke, das ist für das Q & A-Format für SO relevant, da ich versuche, ein spezielles Codeproblem zu lösen, anstatt "wie kann ich das im Allgemeinen besser machen?" )

Anmerkung: Ich arbeite in django 1.6, da dies Code ist, den ich schon seit einiger Zeit herumgeschwommen habe und war nicht zuversichtlich, das Upgrade zu der Zeit zu machen – es ist nicht öffentlich zugänglich, also wenn es jetzt keinen zwingenden Grund gibt (so Performance-Problem), wurde ich nicht zu aktualisieren (für dieses Projekt).

Stream Zuhörer:

 class StdOutListener(tweepy.StreamListener): def on_data(self, data): # Twitter returns data in JSON format - we need to decode it first decoded = json.loads(data) #print type(decoded), decoded # Also, we convert UTF-8 to ASCII ignoring all bad characters sent by users try: if decoded['lang'] == 'en': django_rq.enqueue(read_both, decoded) else: pass except KeyError,e: print "Error on Key", e except DataError, e: print "DataError", e return True def on_error(self, status): print status 

Lesen Sie User / Tweet / Beide

 def read_user(tweet): from harvester.models import User from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned #We might get weird results where user has changed their details"], so first we check the UID. #print "MULTIPLE USER DEBUG", tweet["user"]["id_str"] try: current_user = User.objects.get(id_str=tweet["user"]["id_str"]) created=False return current_user, created except ObjectDoesNotExist: pass except MultipleObjectsReturned: current_user = User.objects.filter(id_str=tweet["user"]["id_str"])[0] return current_user, False if not tweet["user"]["follow_request_sent"]: tweet["user"]["follow_request_sent"] = False if not tweet["user"]["following"]: tweet["user"]["following"] = False if not tweet["user"]["description"]: tweet["user"]["description"] = " " if not tweet["user"]["notifications"]: tweet["user"]["notifications"] = False #If that doesn't work"], then we'll use get_or_create (as a failback rather than save()) from dateutil.parser import parse if not tweet["user"]["contributors_enabled"]: current_user, created = User.objects.get_or_create( follow_request_sent=tweet["user"]["follow_request_sent"], _json = {}, verified = tweet["user"]["verified"], followers_count = tweet["user"]["followers_count"], profile_image_url_https = tweet["user"]["profile_image_url_https"], id_str = tweet["user"]["id_str"], listed_count = tweet["user"]["listed_count"], utc_offset = tweet["user"]["utc_offset"], statuses_count = tweet["user"]["statuses_count"], description = tweet["user"]["description"], friends_count = tweet["user"]["friends_count"], location = tweet["user"]["location"], profile_image_url= tweet["user"]["profile_image_url"], following = tweet["user"]["following"], geo_enabled = tweet["user"]["geo_enabled"], profile_background_image_url =tweet["user"]["profile_background_image_url"], screen_name = tweet["user"]["screen_name"], lang = tweet["user"]["lang"], profile_background_tile = tweet["user"]["profile_background_tile"], favourites_count = tweet["user"]["favourites_count"], name = tweet["user"]["name"], notifications = tweet["user"]["notifications"], url = tweet["user"]["url"], created_at = parse(tweet["user"]["created_at"]), contributors_enabled = False, time_zone = tweet["user"]["time_zone"], protected = tweet["user"]["protected"], default_profile = tweet["user"]["default_profile"], is_translator = tweet["user"]["is_translator"] ) else: current_user, created = User.objects.get_or_create( follow_request_sent=tweet["user"]["follow_request_sent"], _json = {}, verified = tweet["user"]["verified"], followers_count = tweet["user"]["followers_count"], profile_image_url_https = tweet["user"]["profile_image_url_https"], id_str = tweet["user"]["id_str"], listed_count = tweet["user"]["listed_count"], utc_offset = tweet["user"]["utc_offset"], statuses_count = tweet["user"]["statuses_count"], description = tweet["user"]["description"], friends_count = tweet["user"]["friends_count"], location = tweet["user"]["location"], profile_image_url= tweet["user"]["profile_image_url"], following = tweet["user"]["following"], geo_enabled = tweet["user"]["geo_enabled"], profile_background_image_url =tweet["user"]["profile_background_image_url"], screen_name = tweet["user"]["screen_name"], lang = tweet["user"]["lang"], profile_background_tile = tweet["user"]["profile_background_tile"], favourites_count = tweet["user"]["favourites_count"], name = tweet["user"]["name"], notifications = tweet["user"]["notifications"], url = tweet["user"]["url"], created_at = parse(tweet["user"]["created_at"]), contributors_enabled = tweet["user"]["contributers_enabled"], time_zone = tweet["user"]["time_zone"], protected = tweet["user"]["protected"], default_profile = tweet["user"]["default_profile"], is_translator = tweet["user"]["is_translator"] ) #print "CURRENT USER:""], type(current_user)"], current_user #current_user"], created = User.objects.get_or_create(current_user) return current_user, created def read_tweet(tweet, current_user): import logging logger = logging.getLogger('django') from datetime import date, datetime #print "Inside read_Tweet" from harvester.models import Tweet from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.db import DataError #We might get weird results where user has changed their details"], so first we check the UID. #print tweet_data["created_at"] from dateutil.parser import parse tweet["created_at"] = parse(tweet["created_at"]) try: #print "trying tweet_data["id" current_tweet =Tweet.objects.get(id_str=tweet["id_str"]) created=False return current_user, created except ObjectDoesNotExist: pass except MultipleObjectsReturned: current_tweet =Tweet.objects.filter(id_str=tweet["id_str"])[0] try: current_tweet, created = Tweet.objects.get_or_create( truncated=tweet["truncated"], text=tweet["text"], favorite_count=tweet["favorite_count"], author = current_user, _json = {}, source=tweet["source"], retweeted=tweet["retweeted"], coordinates = tweet["coordinates"], entities = tweet["entities"], in_reply_to_screen_name = tweet["in_reply_to_screen_name"], id_str = tweet["id_str"], retweet_count = tweet["retweet_count"], favorited = tweet["favorited"], user = tweet["user"], geo = tweet["geo"], in_reply_to_user_id_str = tweet["in_reply_to_user_id_str"], lang = tweet["lang"], created_at = tweet["created_at"], place = tweet["place"]) print "DEBUG", current_user, current_tweet return current_tweet, created except DataError, e: #Catchall to pick up non-parsed tweets print "DEBUG ERROR", e, tweet return None, False def read_both(tweet): current_user, created = read_user(tweet) current_tweet, created = read_tweet(tweet, current_user) 

One Solution collect form web for “Langsame django Datenbank Operationen auf großen (ish) Datensatz.”

Ich schaffte es schließlich, eine Antwort von einigen Redditoren und ein paar anderen Sachen zusammenzupassen.

Grundsätzlich, obwohl ich einen doppelten Lookup auf dem id_str Feld machte, das nicht indiziert wurde. Ich habe Indizes db_index=True zu diesem Feld auf read_tweet und read_user , und bewegte read_user zu einem try / außer Tweet.objects.create Ansatz, fallen zurück zu den get_or_create, wenn es ein Problem, und sah eine 50-60x Geschwindigkeit Verbesserung, Mit den Arbeitern jetzt skalierbar – wenn ich 10 Arbeiter hinzufüge, bekomme ich 10x Geschwindigkeit.

Ich habe derzeit einen Arbeiter, der glücklich Verarbeitung 6 oder so Tweets eine Sekunde ist. Als nächstes werde ich einen Monitoring-Daemon hinzufügen, um die Warteschlangengröße zu überprüfen und zusätzliche Arbeiter hinzuzufügen, wenn es noch zunimmt.

Tl; dr – ERINNERUNG INDEXING!

  • Twython OAuth1 Ausgaben, 401 Fehler mit Beispielcode
  • Veröffentlichen von Bildern zu Twitter über Python
  • Urllib.request in Python 2.7
  • Abrufen von Twitter-Daten mit Tweepy
  • Brauchen Sie, um Twitter-JSON-Daten zu einer Datei mit Python zu drucken
  • Verwenden von Python, um Wörterbuchschlüssel in einer Liste zu extrahieren
  • Tweepy Stream zu sqlite Datenbank - ungültige Synathe
  • Twitter retweet Benutzer id api python
  • Python verwenden, um verschachtelte Divs und Spannen in Twitter zu kratzen?
  • Twitter-Stream mit OAuth in Python verhält sich anders auf zwei gleich konfigurierten Maschinen
  • Fokussierung auf spezifische Ergebnisse beim Kratzen Twitter mit Python und schöne Suppe 4?
  • Python ist die beste Programmiersprache der Welt.