php/mysql: geschwindigkeitsproblem
kleinerChemiker 14.01.2005 - 16:24 524 8
kleinerChemiker
Here to stay
|
db1: Feld Typ Null Standard Verweise Kommentare MIME
id mediumint(8) Nein
uid int(10) Nein 0
name varchar(50) Nein
account varchar(15) Nein
gilde varchar(50) Ja NULL
klasse varchar(15) Ja NULL
rasse varchar(10) Ja NULL
lehen varchar(50) Ja NULL
last_online int(10) Nein 0
sum_online mediumint(8) Nein 1
Indizes :
Name Typ Kardinalität Feld
id UNIQUE 660 id
uid INDEX 660 uid
db2 Feld Typ Null Standard Verweise Kommentare MIME
charid mediumint(8) Nein 0 chars -> id
zeit int(10) Nein 0
x smallint(5) Nein 0
y smallint(5) Nein 0
Indizes :
Name Typ Kardinalität Feld
zeit INDEX 15317 zeit
charid INDEX 850 charid
ich habe nun 2 abfragen, die nahezu das selbe ergebnis liefern. obwohl eine der beiden weniger info liefert und eigentlich komplett von mysql bearbeitet wird, ist sie etwa 5mal langsamer. ist in meinem sql was falsch, oder ist so was durchaus üblich? abfrage1 (die schnellere): $time_start = getmicrotime();
$query = 'SELECT gilde AS name, count(*) AS mitglieder FROM chars WHERE last_online>' . $temp . ' AND klasse<>\'\' GROUP BY gilde ORDER BY gilde ASC';
$result = mysql_query($query) OR die(mysql_error());
for ($gild_summe = $i = 0; $i < mysql_num_rows($result); $i++) {
$gilden[$i] = mysql_fetch_assoc($result);
$gild_summe += $gilden[$i]['mitglieder'];
if (strlen($gilden[$i]['name']) < 2) {
$gilden[$i]['name'] = 'gildenlos';
}
}
for ($i = 0; $i < count($gilden); $i++) {
$temp = ($gilden[$i]['mitglieder']/$gild_summe)*100;
$gilden[$i]['proz'] = round($temp, '2');
unset($temp);
}
for ($i=0; isset($gilden[$i]['name']);$i++) {
if ($gilden[$i]['name'] != 'gildenlos') {
$query1 = 'SELECT id FROM chars WHERE gilde=\'' . mysql_escape_string($gilden[$i]['name']) . '\'';
$result1 = mysql_query($query1);
$temp = $time-60*60*24*30;
$zeile1 = mysql_fetch_assoc($result1);
$query2 = 'SELECT charid, count(*) AS online FROM cstatus WHERE zeit>' . $temp . ' AND (charid=\'' . $zeile1['id'] . '\'';
while ($zeile1 = mysql_fetch_assoc($result1)) {
$query2 .= ' OR charid=\'' . $zeile1['id'] .'\'';
}
unset($temp);
$query2 .= ') GROUP BY charid';
$result2 = mysql_query($query2);
$gilden[$i]['online'] = 0;
while ($zeile2 = mysql_fetch_assoc($result2)) {
$gilden[$i]['online'] += $zeile2['online'];
}
$gilden[$i]['onlineprochar'] = $gilden[$i]['online'] / $gilden[$i]['mitglieder'];
$gilden[$i]['onlineprochar'] = round($gilden[$i]['onlineprochar']);
unset($temp);
}
else {
$gilden[$i]['onlineprochar'] = $gilden[$i]['online'] = '-';
}
}
$time_end = getmicrotime();
$time_used = $time_end - $time_start;
echo "$time_used<br>\n\n";
die 2.abfrage (die langsamere): $time_start = getmicrotime();
$query = 'SELECT chars.gilde AS name, COUNT(DISTINCT chars.name) AS mitglieder, COUNT(cstatus.charid) AS online FROM chars LEFT JOIN cstatus ON chars.id=cstatus.charid WHERE cstatus.zeit>' . $temp . ' AND chars.klasse<>\'\' GROUP BY chars.gilde ORDER BY chars.gilde ASC';
$result = mysql_query($query) OR die(mysql_error());
for ($i=0;$i<mysql_num_rows($result); $i++) {
$zeile[$i] = mysql_fetch_assoc($result);
$zeile[$i][onlineprochar] = $zeile[$i][online]/$zeile[$i][mitglieder];
$zeile[$i][onlineprochar] = round($zeile[$i][onlineprochar]);
}
$time_end = getmicrotime();
$time_used = $time_end - $time_start;
echo "$time_used\n\n";
tia MIK
Bearbeitet von kleinerChemiker am 16.01.2005, 15:31 (solved)
|
mat
AdministratorLegends never die
|
zeit sollte kein index sein - man beachte die kardinalität
ich denke zwar nicht, dass es daran liegt, allerdings sollte 1 query meistens (wenn nicht immer) schneller sein, alles mehrere.
btw: ich habe mir den oberen code nicht wirklich angesehen, der ist imo unlesbar und hat einige no-nos (zB deutsch-englisch mischen, keine temporären variablen verwendet.. lieber immer asdfasdf[$i] usw).
|
watchout
Legendundead
|
chars.gilde -> INDEX! chars.klasse -> INDEX! ausserdem verwendest du beim langsameren einen join und verbindest somit 2 tables miteinander (wohlgemerkt tables, nich datenbanken...), ausserdem verwendest du ja im 1. 3 queries - und 2 davon sogar in einer doppelten for-schleife - kann mir nicht vorstellen, dass du die geschwindigkeit des ganzen scripts gemessen hast... edit: probier' mal so: // Query: (Hoffe, ich hab mich mit den funktionen nicht vertan...)
SELECT
chars.gilde AS name,
COUNT(DISTINCT chars.name) AS mitglieder,
COUNT(cstatus.charid) AS online
ROUND(online/mitglieder) as onlineprochar
FROM chars
LEFT JOIN cstatus
ON chars.id=cstatus.charid
WHERE
cstatus.zeit>' . $temp . '
AND chars.klasse<>''
GROUP BY chars.gilde
ORDER BY chars.gilde ASC
// Loop:
while($zeile[] = mysql_fetch_assoc($result)) {
;
}
kann man sich das auch in laufendem zusatand ansehen? vor allem würd mich interessieren, um was für zeiten es sich da handelt...
Bearbeitet von watchout am 14.01.2005, 19:40
|
kleinerChemiker
Here to stay
|
@mat: was sagt die kardinalität aus? sicher kein index? denn es wird bei jedem query, das cstatus verwendet nach der zeit sortiert und in der where-clausel kommts auch vor. cstatus hat derzeit knapp 350.000 einträge. englisch - deutsch mischen passiert mir immer wieder. wußte aber nicht, daß das ein pfui-pfui ist. und das temp-variablen auch pfui sind, wußte ich ebenso nicht. weshalb eigentlich? dadurch daß ich sie immer wieder lösche spare ich doch speicher, oder? @watchout: doch, ich messe die geschwindigkeit des ganzen scripts. drum hab ich die zeilen sogar noch dazugenommen, wo er die zeit ausliest (getmicrotime()). ich werds mal mit index austesten, wobei da müßten ja eigentlich beide scripts schneller werden. thx fürs erste MIK
|
watchout
Legendundead
|
Darf man fragen, wozu du die Bedingung chars.klasse<>'' hast? edit: mpf, ich sollte mir angewöhnen gleich alles rein zu schreiben in JOINS profitiert MySQL besonders von Indezes - aber auch nicht immer, ein lebendbeispiel wär mal ganz gut, und in welchem bereich sich die runtimes bewegen ausserdem wieviel % der datenbank du mit einer solchen abfrage ausliest, denn je mehr %, desto weniger bedingungen sind sinnvoll
Bearbeitet von watchout am 14.01.2005, 20:07
|
kleinerChemiker
Here to stay
|
wenn ein char creiert wird, muß er eine klasse wählen. hat er die (noch) nicht gewählt, dann spielt er (noch) nicht und daher uninteressant. ich hab das ganze skript noch angehängt, das die unterschiedliche geschwindigkeit testen soll: click here ich hab es ein paar mal laufen lassen, und werde es noch einige male heute abends anstoßen. akutelle ergebnisse gibts unter: http://infow.kleinerchemiker.net/test.txthier die vorläufigen ergebnisse: lang: 0.2281551361084<br>
kurz: 1.0547528266907<br>
lang: 0.42916989326477<br>
kurz: 0.88980007171631<br>
lang: 0.38035011291504<br>
kurz: 0.93206810951233<br>
lang: 0.32431507110596<br>
kurz: 1.2448098659515<br>
lang: 0.24889516830444<br>
kurz: 0.83910298347473<br>
lang ist die version, mit den vielen queries, kurz die version mit dem join. index hab ich nun auch vergeben, was aber nicht wirklich was gebracht hat MIK edit: von chars wird nahezu der ganze table ausgelesen. in einigen monaten wird sich das vermutlich etwas reduzieren, aber ich schätze es wird sicher über 50% bleiben, eher sogar über 75%. von cstatus wird jetzt noch der ganze table ausgelesen, aber das wird sich schneller ändern. es werden meist die letzten 30tage ausgelesen, ich werde aber voraussichtlich rund 6monate im table lassen. (dannach muß ich löschen, weil ich nicht so viel db-platz hab) was genau verstehst du unter lebendbeispiel? ein print_r vom array? falls ja: http://infow.kleinerchemiker.net/test.php
Bearbeitet von kleinerChemiker am 14.01.2005, 20:19
|
watchout
Legendundead
|
mess' auch mal die reine mysql-zeit der zeitunterschied könnte auch an einem unterschiedlich grossen resultset liegen...
|
kleinerChemiker
Here to stay
|
|
kleinerChemiker
Here to stay
|
|