"Christmas - the time to fix the computers of your loved ones" « Lord Wyrm

php/mysql: geschwindigkeitsproblem

kleinerChemiker 14.01.2005 - 16:24 524 8
Posts

kleinerChemiker

Here to stay
Avatar
Registered: Feb 2002
Location: Wien
Posts: 4282
db1:
Code:
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
Code:
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):
Code: PHP
$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):
Code: PHP
$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

Administrator
Legends never die
Avatar
Registered: Aug 2003
Location: nö
Posts: 25423
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

Legend
undead
Avatar
Registered: Nov 2000
Location: Off the grid.
Posts: 6845
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:
Code: PHP
// 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
Avatar
Registered: Feb 2002
Location: Wien
Posts: 4282
@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

Legend
undead
Avatar
Registered: Nov 2000
Location: Off the grid.
Posts: 6845
Darf man fragen, wozu du die Bedingung chars.klasse<>'' hast?

edit: mpf, ich sollte mir angewöhnen gleich alles rein zu schreiben :D
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
Avatar
Registered: Feb 2002
Location: Wien
Posts: 4282
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.txt
hier die vorläufigen ergebnisse:
Code:
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

Legend
undead
Avatar
Registered: Nov 2000
Location: Off the grid.
Posts: 6845
mess' auch mal die reine mysql-zeit
der zeitunterschied könnte auch an einem unterschiedlich grossen resultset liegen...

kleinerChemiker

Here to stay
Avatar
Registered: Feb 2002
Location: Wien
Posts: 4282
nur mysql-zeiten gibts unter: http://infow.kleinerchemiker.net/test2.php (inklusive array)
und unter: http://infow.kleinerchemiker.net/test2.txt (speichert alle bisherigen durchläufe)

auf alle fälle schon mal danke für die hilfe :)

kleinerChemiker

Here to stay
Avatar
Registered: Feb 2002
Location: Wien
Posts: 4282
:bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash: :bash:

ich bin einfach nur unfähig :(
irgendwie hab ich einen teil verloren, wie ich die queries der langen version (die mit vielen queries) in die testfiles kopiert habe. und natürlich war genau das der teil, der so ewig braucht. mit dem braucht die lange version nicht mehr unter einer sekunde, sondern knapp 30 sekunden und damit ist die kurze version mit einem query natürlich wesneltich schneller.
:bash: :bash: :bash:

aber thx für die hilfe :)
Kontakt | Unser Forum | Über overclockers.at | Impressum | Datenschutz