Seite 2 von 2

Re: cPickle Datenbank + threading

Verfasst: Samstag 31. Dezember 2011, 03:52
von Brainsucker
BlackJack hat geschrieben:Das kann ich mir eigentlich nur vorstellen wenn ständig alle Werte für eine Berechnung benötigt werden und dabei auch immer alle Werte geändert werden müssen.

So ist es auch. Ich will eine statistik erstellen, die aus den gesamte Daten der Datenbank errechnet wird.

Wie schon gesagt, besteht die Datenbank im wesentlichen aus einem datetime.datetime objekt für jeden Spieler der auf dem Gameserver ist/war.

Eventuell kommen später noch ein paar andere Keys hinzu. Aber ich würde mal sagen 95% des Scripts besteht im wesentlichen aus einem Key, der den Spieler eindeutig identifiziert und einem *subkey*, mit dem datetime objekt als wert.

Edit:

Um den grundsätzlichen *aufbau* der "Datenbank" als Pythoncode ein kleinwenig zu verdeutlichen:

Code: Alles auswählen

visitors = {}
today = datetime.datetime.today()
if not visitors.has_key('creationdate'):
	# Wann wurde die Datenbank erstellt?
	visitors['creationdate'] = today
	
if not visitors.has_key('players'):
	# In diesem Key befinden sich die Daten der Spieler
	visitors['players'] = {}
	
if not visitors['players'].has_key('unique_ip'):
	# Dieser Key enthält die IPs der Spieler
	visitors['players']['unique_ip'] = {}
	
if not visitors['players'].has_key('unique_id'):
	# Dieser Key enthält einen Wert für jeden Spieler, der den Spieler exakt identifiziert.
	visitors['players']['unique_id'] = {}

Re: cPickle Datenbank + threading

Verfasst: Samstag 31. Dezember 2011, 10:22
von BlackJack
@Brainsucker: Ich weiss es ist nur Pseudocode, aber `dict.has_key()` ist „deprecated“. In Python 3 gibt es die Methode nicht mehr, aber auch bei Python 2 macht es Sinn stattdessen den ``in``- beziehungsweise den ``not in``-Operator zu verwenden. Es ist kürzer, mindestens im Falle der Negierung (IMHO) auch leichter lesbar, und last but not least (in CPython) geringfügig schneller, da statt eines dynamischen Methodenaufrufs ein einzelner Bytecode für den Operator tritt, der eine von mehreren „magischen“ Methoden aufruft (z.B. `__contains__()`), die schneller erreichbar und aufrufbar sind als die `has_key()`-Methode. Also statt ``if not a_dict.has_key(key):`` besser ``if key not in a_dict:``.

Aus der Beschreibung wird mir nicht ersichtlich wo die Daten alle geändert werden? Denn nur dann macht es Sinn immer alle wieder zu schreiben. Es sieht eher so aus, als wenn da immer neue Daten hinzu kommen. Dann sollte man auch nur diese in eine DB wegschreiben müssen.

Der Aufbau ist mir auch noch nicht ganz klar. Was ist in den innersten Wörterbuchern enthalten?

Code: Alles auswählen

{
    'creationdate': datetime.datetime(2011, 12, 31, 10, 5, 38, 833585),
    'players': {
        'unique_ip': {}, # ?
        'unique_id': {}, # ?
    }
}
Das sieht bis jetzt so aus, als wenn man den grossteil der Daten in eine relationale Datenbank packen könnte. Oder in ein `shelve`. Oder eine Objektdatenbank.

Kannst Du die Berechnung der Statistiken nicht vielleicht so formulieren, dass sie nicht immer komplett neu berechnet werden müssen, sondern dass man ein altes Ergebnis mit neuen Daten aktualisieren kann? Dann bräuchtest Du sowohl beim Lesen als auch beim Schreiben nicht mehr alle Daten, sondern nur noch die aktuellen noch nicht verarbeiteten beziehungsweise gespeicherten Daten.

Bei einer „richtigen“ Datenbank mit Transaktionen könnte man die Statistikberechnung vielleicht sogar komplett aus dem Gameserver heraus nehmen und nebenher regelmässig machen. Das im Spiel eingebundene Skript muss dann nur neue „Rohdaten“ in die DB schreiben und von dort die fertige(n) Statistik(en) auslesen.

Re: cPickle Datenbank + threading

Verfasst: Samstag 31. Dezember 2011, 11:39
von Dav1d
@Brainsucker, das habe ich gelesen:

Code: Alles auswählen

from datetime import datetime
from cPickle import dump, load
from time import time

stimes = 800*360 # 800 = mittel aus 600 und 1000, 360 für ein Jahr

example = {'foo' : datetime.now(), # ein etwas größeres als dein dict
           'bar' : {'dunno' : dict((i,i) for i in range(15)),
                    'dunno2' : dict((i,i) for i in range(10))},
            'baz' : 123}

example_big = [example for _ in range(stimes)] 
            
dump_start = time()
with open('/tmp/data.pkl', 'w') as f:
    dump(example_big, f)

dump_end = time()
    
with open('/tmp/data.pkl', 'r') as f:
    foo = load(f)
  
load_end = time()

print 'Dump:', dump_end - dump_start
print 'Load:', load_end - dump_end
print 'Data:', len(foo)

Code: Alles auswählen

─[ArchBox][/tmp]╼ time python2 setst.py             
Dump: 0.178967952728
Load: 0.127691984177
Data: 288000
python2 setst.py  0,37s user 0,02s system 99% cpu 0,387 total
Nicht gerade lange…

Re: cPickle Datenbank + threading

Verfasst: Samstag 31. Dezember 2011, 12:05
von Brainsucker
Dav1d hat geschrieben:

Code: Alles auswählen

─[ArchBox][/tmp]╼ time python2 setst.py             
Dump: 0.178967952728
Load: 0.127691984177
Data: 288000
python2 setst.py  0,37s user 0,02s system 99% cpu 0,387 total
Nicht gerade lange…

Danke für deine bemühungen, aber dein beispiel ist keinesfalls wirklich praxisnahe, da der Gameserver während dem Betrieb noch mit vielen anderen Berechnungen u.s.w. beschäftigt ist und außerdem in der Regel noch weitere Scripts auf dem Server laufen, kann der Gameserver der cPickle.dump funktion nur einen Teil der CPU zuweisen. Somit vordoppelt oder verdreifacht sich die von dir ausgerechnete Zeit ganz schnell mal. Und das würde ohne Threading ein lagg von 1-1,5 Sekunden verursachen, was nicht gerade schön ist auf einem Gameserver.

Re: cPickle Datenbank + threading

Verfasst: Samstag 31. Dezember 2011, 12:14
von Brainsucker
@BlackJack:

Hey, danke das mit dem ´in´ werd ich mir merken, wusste nicht dass das schneller ist als die ´has_key´-Funktion.


So nun Back2Topic:
Die vollständige Datenbank sieht in etwa so aus:

Code: Alles auswählen

{
    'creationdate': datetime.datetime(2011, 12, 31, 10, 5, 38, 833585),
    'players': {
        'unique_id': {'spieler1': {'lastonline': datetime.datetime.today()
                                    }
                    'spieler2': 'lastonline': datetime.datetime.today()}, # !
        'unique_ip': {'IP1': {'lastonline': datetime.datetime.today()}
                    'IP2': {'lastonline': datetime.datetime.today()}}, # !
    }
}
Heißt also, dass jede IP und jeder Spieler, die auf dem Server war eine eigenes datetime objekt bekommt, wann diese(r) zuletzt auf dem Server gesehen wurde.

Das stimmt durchaus, dass es eher sinnvoll wäre ausschließlich nur die Daten zu schreiben, die auch tatsächlich geändert wurden. Allerdings ist da cPickle in diesem Falle sehr begrenzt.

Also wäre wohl tatsächlich eine andere Datenbankart eher geeignet, die nur die Daten schreibt, die die auch wirklich geändert wurden.

Hmm, dann ist wohl cPickle nicht wirklich die Optimale lösung.

Re: cPickle Datenbank + threading

Verfasst: Samstag 31. Dezember 2011, 12:19
von Dav1d
Mir ging es darum, dir zu zeigen, dass du versuchst zu optimieren, wenn es nicht sicher ist, ob es überhaupt nötig ist. Außerdem wie BlackJack meinte, mit den falschen Mitteln, da threading dir in Verbindung mit pickle überhaupt nichts nützen könnte.

Re: cPickle Datenbank + threading

Verfasst: Samstag 31. Dezember 2011, 14:10
von Brainsucker
Ich weiß aus der Praxis, dass threading zum gewünschten ergebniss führt.

Einmal hatte ich selbes problem, dass beim speichern/laden der Daten der Server gelaggt hat. Kaum waren die threads drin, hats nicht mehr gelaggt.

Allerdings hatte dies andere nachteile, da ich damals noch kein Lock() verwendet hatte.

Und nun wollte ich die sache richtig angehen und eben die sache so schreiben, dass die Nachteile nicht mehr auftreten. Desshalb hab ich hier um hilfe angefragt.

Re: cPickle Datenbank + threading

Verfasst: Montag 2. Januar 2012, 00:46
von Brainsucker
Also Leute, ich hab mich nun umentschieden eine richtige sqlite3 Datenbank anstatt dem cPickle Serialisierungsmurks zu benutzen. Somit kann das Topic hier dicht gemacht werden und ich mach dann im passenden Unterforum ein neues Topic auf wenn ich fragen zu sqlite habe.


Danke euch allen für die hilfe :)

Re: cPickle Datenbank + threading

Verfasst: Dienstag 3. Januar 2012, 08:27
von noisefloor
Hallo,

gut. ;-)

Wenn du wirklich durchweg "nur" Schlüssel-Werte Paare schreibst / liest, dann sollte für dich ein Key-Value Store (wie z.B. Redis) auch interessant sein.

Zum einen sind die ziemlich schnell, zum anderen kann zumindest Redis die Daten asynchron im Hintergrund auf die HD schreiben. Und es gibt auch einen Object-Mapper für Redis.

Gruß, noisefloor

Re: cPickle Datenbank + threading

Verfasst: Mittwoch 4. Januar 2012, 07:02
von Brainsucker
Hey,

ja der größteil sind natürlich schon schlüssel/werte paare in der Datenbank.

Allerdings das was mich bisher am aller meißten über sqlite 'verärgert', ist die umständliche art abzufragen ob für einen bestimmten spieler bereits eine Reihe in der Datenbank existiert und man dann je nach dem die Reihe Updaten oder Inserten muss. Etwas recht umständlich wie ich finde.

Code: Alles auswählen

# Wir gehen davon aus, dass "cur" eine Variable ist, die eine sqlite3.Connect instanz beinhaltet.
cur.execute("SELECT * FROM datenbanke WHERE spielerid=?", (spielerid,))

Bei den dicts war das deutlich einfach mit has_key bzw. wie im verlauf des topics schon beschrieben den ´in´ operator zu benutzen.

Re: cPickle Datenbank + threading

Verfasst: Mittwoch 4. Januar 2012, 07:44
von noisefloor
Hallo,

na ja, was heißt umständlich. Wenn du wissen willst, ob etwas in einer (relationalen) Datenbank abfragen willst, du brauchst du einen Query. Gibt der "None" zurück, ist nichts drin.

Vom Prinzip ist das nicht anderes als das, was du mit dem Dict auch machst. :-)

Gruß, noisefloor

Re: cPickle Datenbank + threading

Verfasst: Mittwoch 4. Januar 2012, 07:59
von BlackJack
@Brainsucker: Statt ``*`` könntest Du vielleicht besser ``count(*)`` oder einfach nur ``1`` selektieren. Denn die Daten möchtest Du ja gar nicht haben.

Ausserdem könntest Du statt abzufragen auch einfach das machen was erwartungsgemäss häufiger klappen wird und testen ob es wirklich funkioniert hat, und falls nicht das andere machen.

Re: cPickle Datenbank + threading

Verfasst: Mittwoch 4. Januar 2012, 09:03
von Brainsucker
BlackJack hat geschrieben:@Brainsucker: Statt ``*`` könntest Du vielleicht besser ``count(*)`` oder einfach nur ``1`` selektieren. Denn die Daten möchtest Du ja gar nicht haben.
Hey, danke wusste gar nicht dass man auch irgendetwas x-beliebiges selektieren kann. Das macht die sache schon etwas einfacher um den datenfluss so gering wie möglich zu halten.