PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Was ist der schnellste Weg Daten in eine Datenbank einzufügen?



craano
25.12.11, 14:46
Hallo,

ich frage mich gerade, auf welche Art und Weise man viele Datensätze am schnellsten in eine (MySQL-)Datenbank einfügen kann?

Irgendwie klemmt dort bei mir die Performance.

Als Beispiel erstelle ich einmal einen (Mini-)Rainbow-Table und füge die Werte in eine Datenbank ein.

Versuch 1:
1. Ich erstelle die Wertepaare und schreibe diese in eine Datei.


#!/usr/bin/python



import string
import hashlib
import MySQLdb as mdb

l1 = string.lowercase[:26]
n = 0
t = '\t'
z = '\n'
ns = ''
row = ''
md5 = ''

gen = ((a,b,c) for a in l1 for b in l1 for c in l1)
print "use p_db;"
for i in gen:
i = tuple(i)
i = ''.join(i)
n = n + 1
md5 = hashlib.md5(i).hexdigest()
ins = "insert into p_md5(num,pattern,md5) values (%d,\'%s\',\'%s\');" % (n,i,md5)
print ins


2. Dann lese ich die Dateien mit msql ein


$ time python many.mysql.gen_lst.py > test

real 0m0.440s
user 0m0.412s
sys 0m0.016s

$ ls -lh test i
-rw-r--r-- 1 craano users 1,6M 25. Dez 15:29 test

$ time mysql -u p_user -p < test
Enter password:

Das breche ich dann irgendwann ab, weil die einzelnen Zeilen einfach sehr langsam in die Datenbank eingefügt werden.

2. Versuch
Ich füge aus dem kleinem Python Skript mittels MySQLdb direkt in die DB ein:


import string
import hashlib
import MySQLdb as mdb

l1 = string.lowercase[:26]
n = 0
t = '\t'
z = '\n'
ns = ''
row = ''
md5 = ''

gen = ((a,b,c) for a in l1 for b in l1 for c in l1)

con = None
con = mdb.connect('localhost', 'p_user', 'p_pwd', 'p_db')
cur = con.cursor()
for i in gen:
i = tuple(i)
i = ''.join(i)
n = n + 1
md5 = hashlib.md5(i).hexdigest()
with con:
ins = "insert into p_md5(num,pattern,md5) values (%d,\"%s\",\"%s\");" % (n,i,md5)
cur.execute(ins)
con.close()
print n


Das dauert mehr oder weniger genauso lange wie das einlesen aus der Datei.
Igendwie steht aber der zeitliche Aufwand des Einfügens in keinem Verhältnis zum Erzeugen der Hashes.
Als sehr unerfahrener und nur privater Datenbankuser frage ich mich nun, ob die Datenbank immer der Flaschenhals ist oder gibt es eine schnellere Möglichkeiten vielen Zeilen in eine DB einzulesen (vieleicht aus einer csv - Datei)?

Übrigens ich habe das auch mit Postgresql versucht, ist auch nicht wirklich besser.

Ein Rat, nimm stärkere Hardware, nutzt mir nicht, weil das ganze nur ein privater Versuch auch meinem Lappi ist und ich einfach nur wissen möchte, wie man eine Datenbank optimal ansprechen kann.

Wenn ich nun Hashes aus vier oder fünf Stellen (26**4, 26 **5) erstelle, sind diese in sehr kurzer Zeit erstellt. Das Datenbank einfügen dauert dann eeeeeewwwwwwwiiiiiiig!

Gesegnetes Fest Euch allen
craano

Roger Wilco
25.12.11, 15:48
In deinem konkreten Fall könnte es ein wenig helfen, ein Prepared Statement zu verwenden, statt jedesmal ein neues SQL-Statement durch String-Manipulation zu erstellen. Dadurch muss der Datenbankserver das Statement nur einmal parsen, statt statt n-mal.

Desweiteren hängt viel Performanz von dem Datenbankschema und der Konfiguration des Datenbankservers ab. Wenn eines davon schlecht ist, dauern Operationen auf die Daten eben länger. Ebenso erhöhen Indizes die Dauer, die zum Einfügen in die Datenbank benötigt wird, da diese bei jeder Datenmanipulation aktualisiert werden müssen.

craano
25.12.11, 16:54
Prepared statements werde ich mit anschauen, kenne ich bislang noch nicht.

Ich habe jetzt

time mysql -u p_user -p < test
einmal durchlaufen lassen. Es braucht knapp 31 Minuten. Das erzeugen der Textdatei nur kanpp eine Sekunde.

Zum MySQL - Setup kann ich nicht viel sagen. Es ist eine Standardinstallation von OS 12.1 auf einem kleinem Laptop.

Die Datenbanktabelle sieht so aus:


mysql> describe p_md5;
+---------+-----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| num | int(11) | YES | | NULL | |
| pattern | char(128) | YES | | NULL | |
| md5 | char(128) | YES | | NULL | |
+---------+-----------+------+-----+---------+----------------+
4 rows in set (0.00 sec)



Werde es nochmal ohne (auto_increment) Spalte probieren.
Grüße
craano

ThorstenHirsch
25.12.11, 17:21
Man kann doch auch irgendwie das Journal (kann sein, dass der Begriff falsch ist) ausschalten, oder? Also dann hast du zwar keine Möglichkeit zum rollback am Ende, aber das ist dir ja offensichtlich egal.

Davon abgesehen finde ich es jetzt nicht sehr überraschend, dass das Schreiben länger dauert als das Lesen + Erzeugen der Hashes. Das Erzeugen der Hahes braucht nicht viel CPU-Zeit. RAM und CPU sind außerdem um Welten schneller aneinander angebunden als die Festplatte.

craano
26.12.11, 09:13
Davon abgesehen finde ich es jetzt nicht sehr überraschend, dass das Schreiben länger dauert als das Lesen + Erzeugen der Hashes. Das Erzeugen der Hahes braucht nicht viel CPU-Zeit. RAM und CPU sind außerdem um Welten schneller aneinander angebunden als die Festplatte.
Natürlich sind RAM und CPU sehr schnell aneinander angebunden. Allerdings finde ich überraschend, dass das Erzeugen und Schreiben Ausgabe in eine Textdatei gemeinsam um den Faktor 3600 schneller ist, als das Einfügen der selben Datensätze in die Datenbank.

Grüße
craano

Roger Wilco
26.12.11, 09:32
Ein RDBMS wie MySQL macht auch noch wesentlich mehr, als nur rohe Daten in eine Textdatei zuschreiben, Stichwort ACID.

marce
26.12.11, 12:08
für'S flotte inserten hilft meist,
- nicht für jede Zeile ein ded. insert, sondern mehrere Zeilen mit einem Insert einfügen
- indices abschalten und erst hinterher erstellen lassen
- DB entsprechend optimieren (ob sich das lohnt hängt davon ab, ob es eine einmalige Aktion ist oder regelmäßíg / öfters gemacht werden soll)
- evtl. passende DB-Engine wählen
- ...

Wenn es die Standard-my.cnf von OS ist dürfte da das meiste Optimierungspotential liegen. Performancesprünge von ein paar 100% sind da durchaus machbar...