Datenbankwerte am schnellsten ausgeben

Jeronnymoe

Mitglied
Hallo,

ich baue zur Zeit mein eigenes kleines CMS in PHP. Dabei mache ich mir jetzt schon Gedanken
über die Perfomance. Deshalb wollte ich mal fragen, ob meine Wege die schnellsten sind oder
ob es da noch schnellere gibt.


Aktuelle Besucherzahl ausgeben:

$timestamp = time();
$select = mysql_query("SELECT COUNT(ip_address) FROM data_visitors_online WHERE online_till > '$timestamp'");
$select_done = mysql_result($select, 0, 0);
$user_online = $select_done;


Um's besser nachvollziehen zu können, die dazugehörige Datenbankstruktur:

CREATE TABLE data_visitors_online (
ip_address varchar(15) collate latin1_german2_ci NOT NULL default '',
online_till varchar(10) collate latin1_german2_ci NOT NULL default ''
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_german2_ci;


Weitere Teile folgen noch...

Vielen Dank für eure Hilfe im voraus!

Jeronnymoe
 
Wenn du dir Gedanken über Performance machst, solltest du konstrukte wie "Variablen in Strings" also zB.: $x="$y"; sein lassen, das kostet mächtig Zeit. Im allgemeinen empfiehlt sich, nur das einfache Hochkomma bei strings zu verwenden, da diese Strings dann nicht intern nochmal geparst werden.

zu deine Frage mit der Query kann ich nicht viel sagen, ich verwende seit ewigkeiten Wrapper-Klassen und habe daher kaum noch Ahnung von den Grundfunktionen
biggrin.gif
Aber mysql_result habe ich glaube noch nie verwendet.

Zur Query:
SELECT COUNT(ip_address) FROM

kann man in deinem Fall durch das ersetzen:

SELECT COUNT(*) FROM

das ist insgesamt schneller und funktioniert auch wenn man was an der Tabelle ändert.

Hier stehen dazu weitere Infos:
http://www.mysqlperformanceblog.com/2007/0...nt-vs-countcol/
 
Grundsätzlich kann es sinnvoll sein, die Zahl der Datenbankabfragen zu minimieren, sprich: Sie zusammenzufassen.

Ich nutze zwar kein PHP/mySql, aber jeder Kontakt zur Datenbank stellt einen Kontext/Prozeßwechsel dar.

Sprich: Mehrere Einzelabfragen zu einer Abfrage kombinieren, falls das DB-System Unterabfragen unterstützt:

QUOTE Select (Select Count(*) From data_visitors_online WHERE online_till > '$timestamp') As ersterWert,
(Select irgendetwas anderes From andere_Tabelle) As zweiterWert
 
Zum Thema Double Quote und PHP gibt es jede Menge Artikel im Web.
Meiner Meinung nach gibt es da meist drei Grundprobleme:
- Unterschiedliche PHP Versionen führen zu unterschiedlichen Ergebnissen.
Allgemein je neuer desto flotter => bei PHP 5 ist es wirklich egal
- Der Performanceunterschied ist in absoluten Werten im Vergleich zur Gesamtlaufzeit eines Scripts extrem gering
- Der Einsatz eines PHP Code Cache hebt den Unterschied vollkommen auf
- Auch der String im Single Quote wird Zeichen für Zeichen abgearbeitet (und das ist wohl der Hauptfaktor der mit Double Quote übereinstimmt)

Und trotz der genannten Punkte schreibe ich auch keine Variablen in Double Quotes ;-)

Es gibt aber einen wesentliche Unterschied zwischen Double und Single Quote und das ist der Slash
Im Single Quote:
\’ für ein single-quote
\\ für ein \

Achtung:
’\r\n’ != “\r\n“


http://www.mysqlperformanceblog.com/ ist ein cooler Blog, danke für den Tipp Maik!
cool.gif
 
Vielen Dank für eure Hilfe! Dank eurer Tipps und ein bisschen Eigenleistung hab ich den
Quelltext um ca. 50% halbiert. Hier mal ein kleiner Auszug.

Neu

CODE <?php
include("db_connection.php");
$vo_ip_address = $_SERVER['REMOTE_ADDR'];
$vo_timestamp = time();
$vo_update_intervall = 180;
$db_vo_timestamp = $vo_timestamp + $vo_update_intervall;
if($_COOKIE['tms02_user'])
{
$cookie = explode("#", $_COOKIE['user']);
$update_done = mysql_query("UPDATE data_users SET online_till = '$db_vo_timestamp' WHERE name LIKE '$cookie[0]'");
}
$select_done = mysql_query("SELECT (SELECT COUNT(*) FROM data_visitors_online WHERE ip_address LIKE '$vo_ip_address') AS o_w_s_i,
(SELECT COUNT(*) FROM data_visitors_online WHERE online_till > '$vo_timestamp') AS online_visitors");
$db_o_w_s_i = mysql_result($select_done, 0, "o_w_s_i");
$db_online_visitors = mysql_result($select_done, 0, "online_visitors");
if($db_o_w_s_i == 0)
{
$insert_done = mysql_query("INSERT INTO data_visitors_online (ip_address, online_till) VALUES ('$vo_ip_address', '$db_vo_timestamp')");
$db_online_visitors++;
}
else
{
$update_done = mysql_query("UPDATE data_visitors_online SET online_till = '$db_vo_timestamp' WHERE ip_address LIKE '$vo_ip_address'");
}
echo "<b>".$db_online_visitors."</b> Besucher online.";
?>



Alt


CODE <?php
include("db_connection.php");
$vo_ip_address = $_SERVER['REMOTE_ADDR'];
$vo_timestamp = time();
$vo_update_intervall = 180;
if($_COOKIE['tms02_user'])
{
$cookie = explode("#", $_COOKIE['tms02_user']);
$db_vo_timestamp = $vo_timestamp + $vo_update_intervall;
$update = "UPDATE data_users SET online_till = '$db_vo_timestamp' WHERE name LIKE '$cookie[0]'";
$update_done = mysql_query($update);
}
$select = mysql_query("SELECT COUNT(ip_address) FROM data_visitors_online WHERE ip_address LIKE '$vo_ip_address'");
$select_done = mysql_result($select, 0, 0);
if($select_done == 0)
{
$db_vo_timestamp = $vo_timestamp + $vo_update_intervall;
$insert = "INSERT INTO data_visitors_online (ip_address, online_till) VALUES ('$vo_ip_address', '$db_vo_timestamp')";
$insert_done = mysql_query($insert);
}
else
{
$db_vo_timestamp = $vo_timestamp + $vo_update_intervall;
$update = "UPDATE data_visitors_online SET online_till = '$db_vo_timestamp' WHERE ip_address LIKE '$vo_ip_address'";
$update_done = mysql_query($update);
}
$select = mysql_query("SELECT COUNT(ip_address) FROM data_visitors_online WHERE online_till > '$vo_timestamp'");
$select_done = mysql_result($select, 0, 0);
mysql_free_result($select);
$user_online = $select_done;
switch($user_online)
{
case "0":
echo "<b>Kein</b> Besucher online.";
break;
case "1":
echo "<b>1</b> Besucher online.";
break;
default;
echo "<b>".$user_online."</b> Besucher online.";
break;
}
?>


Ihr könnt ja noch mal drüberschauen, falls ihr noch was findet!

Nochmals vielen Dank!

Jeronnymoe
 
Bedenke, dass auch Cookies gefälscht werden können! Die Variable $cookie sollte bevor du sie in eine Query packst vorher Query-sicher gemacht werden. Das einfachste ist mit mysql_escape_string also:

CODE $cookie = mysql_escape_string($cookie);


direkt vor deiner ersten Query.

Dann noch 2 Dinge:

1.

CODE if($_COOKIE['tms02_user'])...

Da solltest du mit isset oder empty prüfen, ob der Array-index überhaupt existiert. Wahrscheinlich hast du die Warnungen von PHP abgeschaltet, ansonsten würdest du eine "undefined index" Warnung bekommen.

2.


CODE if($_COOKIE['tms02_user'])
{
$cookie = explode("#", $_COOKIE['user']);...


Du prüfst ob $_COOKIE['tms02_user'] gesetzt ist und gehst damit davon aus, dass auch $_COOKIE['user'] gesetzt ist. Das muss nicht sein! Auch hier solltest du $_COOKIE['user'] mit isset oder empty prüfen.
 
Danke Maik für deine Hilfe!

Ich hab' den Code jetzt noch mal umgeschrieben...

CODE <?php
include("db_connection.php");
$vo_ip_address = $_SERVER['REMOTE_ADDR'];
$vo_timestamp = time();
$vo_update_intervall = 180;
$db_vo_timestamp = $vo_timestamp + $vo_update_intervall;
if(isset($_COOKIE['tms02_user']))
{
$cookie = mysql_escape_string($_COOKIE['tms02_user']);
$cookie = explode("#", $cookie);
$update_done = mysql_query("UPDATE data_users SET online_till = '$db_vo_timestamp' WHERE name LIKE '$cookie[0]'");
}
$select_done = mysql_query("SELECT COUNT(*) AS o_w_s_i FROM data_visitors_online WHERE ip_address LIKE '$vo_ip_address'");
$db_o_w_s_i = mysql_result($select_done, 0, "o_w_s_i");
if($db_o_w_s_i == 0)
{
$insert_done = mysql_query("INSERT INTO data_visitors_online (ip_address, online_till) VALUES ('$vo_ip_address', '$db_vo_timestamp')");
}
else
{
$update_done = mysql_query("UPDATE data_visitors_online SET online_till = '$db_vo_timestamp' WHERE ip_address LIKE '$vo_ip_address'");
}
$select_done = mysql_query("SELECT COUNT(*) AS online_visitors FROM data_visitors_online WHERE online_till > '$vo_timestamp'");
$db_online_visitors = mysql_result($select_done, 0, "online_visitors");
echo "<b>".$db_online_visitors."</b> Besucher online.";
?>
 
Ich habe übrigens einen lustigen Counter gebaut:

'Besucherstatistik: 50 heute, 38 gestern, 104 insgesamt.'

Tabellenstruktur

CODE CREATE TABLE data_counter (
visits int(10) NOT NULL default '0',
ip_address varchar(15) collate latin1_german2_ci NOT NULL default '',
visit_at varchar(10) collate latin1_german2_ci NOT NULL default ''
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_german2_ci;



Code (wird auf jeder Seite included)


CODE <?php
include("db_connection.php");
$counter_ip_address = $_SERVER['REMOTE_ADDR'];
$counter_timestamp = time();
$counter_update_intervall = 900;
$select_done = mysql_query("SELECT (SELECT COUNT(*) FROM data_counter WHERE ip_address LIKE '$counter_ip_address') AS visited_before,
(SELECT MAX(visits) FROM data_counter) AS max_visits");
$visited_before = mysql_result($select_done, 0, "visited_before");
$db_max_visits = mysql_result($select_done, 0, "max_visits");

if($visited_before == 0)
{
$db_max_visits++;
$insert_done = mysql_query("INSERT INTO data_counter (visits, ip_address, visit_at) VALUES ('$db_max_visits', '$counter_ip_address', '$counter_timestamp')");
}
else
{
$select_done = mysql_query("SELECT ip_address, visit_at FROM data_counter WHERE ip_address LIKE '$counter_ip_address'");
$db_visit_at = mysql_result($select_done, 0, "visit_at");
if($db_visit_at < ($counter_timestamp - $counter_update_intervall))
{
$db_max_visits++;
$update_done = mysql_query("UPDATE data_counter SET visits = '$db_max_visits', visit_at = '$counter_timestamp' WHERE ip_address = '$counter_ip_address'");
}
}
?>



Zur Auswertung


CODE <?php
include("db_connection.php");
$date_yesterday = date('n+j+Y', time() - 86400);
$mktime = explode("+", $date_yesterday);
$yesterday_morning = mktime(0,0,0,$mktime[0],$mktime[1],$mktime[2]);
$yesterday_evening = mktime(23,59,0,$mktime[0],$mktime[1],$mktime[2]);
$select_done = mysql_query("SELECT (SELECT visits FROM data_counter WHERE visit_at BETWEEN '$yesterday_morning' AND '$yesterday_evening' ORDER BY visit_at ASC LIMIT 0,1) AS y_morning,
(SELECT visits FROM data_counter WHERE visit_at BETWEEN '$yesterday_morning' AND '$yesterday_evening' ORDER BY visit_at DESC LIMIT 0,1) AS y_evening,
(SELECT visits FROM data_counter ORDER BY visits DESC LIMIT 0, 1) AS max_visits");
$visits_yesterday = mysql_result($select_done, 0, "y_evening") - mysql_result($select_done, 0, "y_morning");
$visits_today = mysql_result($select_done, 0, "max_visits") - mysql_result($select_done, 0, "y_evening");
$visits_overall = mysql_result($select_done, 0, "max_visits");
?>


Ist der was oder eher zu belächeln?
wink.gif


Jeronnymoe
 
Ich wuerde gern noch anmerken, dass es durchaus sinnvoll sein kann, "natuerlichere" Datentypen zu benutzen. MySQL hat einen timestamp-Spaltentyp und diverse eingebaute Funktionen oder Definitionen, wie z.B. NOW(), CURRENT usw. Die Benutzung derselben erhoeht oft nicht nur den Komfort, sondern auch die Performance.

den
 
Daran habe ich auch gedacht, allerdings kann ich damit nachher nicht mehr so gut rechnen oder Vergleiche
erstellen, wie mit dem Timestamp als String.
 
Ja, das ist schon klar ... der UNIX-Timestamp (den auch PHP verwendet) und der MySQL-Timestamp sind verschieden. Man koennte aus Komfortgruenden auch einen INT-Typen fuer die Spalte in der MSQL verwenden und unix_timestamp() darauf benutzen. Das waere dann gleich das passende Format fuer PHP. Bei groesseren Datenmengen wuerde der Unterschied von INT zu VARCHAR mit unicode sicher zu bemerken sein.

den
 
QUOTE (Jeronnymoe @ Mi 16.05.2007, 16:14) Daran habe ich auch gedacht, allerdings kann ich damit nachher nicht mehr so gut rechnen oder Vergleiche
erstellen, wie mit dem Timestamp als String.

Why not?
 
Ich kann es mir nicht vorstellen, hab's aber ehrlich gesagt noch nicht ausprobiert. Wie
will man denn z.B. 900 Sek. Unterschied zwischen dem jetzigen Zeitpunkt und dem in
der Datenbank (im Format YYYY-MM-DD 00:00:00) festellen?
blink.gif
 
Wenn Du die vorgeschlagene Variante INT-Spalte und unix_timestamp() benutzt, ist das einfach.

Der UNIX-Timestamp ist ein Wert, der die Anzahl der Sekunden seit dem 1.1.1970 bis zum entsprechenden Datum enthaelt. Mit diesem Wissen kannst Du einfach die entsprechenden Timestamps (oder auch 900 Sekunden direkt) addieren/subtrahieren usw.

Beste Gruesse,
den.
 
Jo, hab ich ja vorher auch schon gemacht, nur halt mit VARCHAR. Meine Frage war jetzt
eher auf die Aussage von Sascha Ahlers bezogen
wink.gif
 
Du kannst das Format umwandeln, indem Du Dir die Funktion UNIX_TIMESTAMP() zu nutze machst. Beispiel:

Tabelle:
SQL CREATE TABLE `test` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(30) collate utf8_unicode_ci NOT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=2 ;

--
-- Daten für Tabelle `test`
--

INSERT INTO `test` (`id`, `name`, `time`) VALUES
(1, 'datum', '2007-05-16 21:13:49');


Abfrage:

SQL SELECT UNIX_TIMESTAMP( time ) AS time FROM `test`;


Ausgabe:

CODE +------------+
| time       |
+------------+
| 1179342829 |
+------------+



Der Vergleich ist also fast genauso zu vollführen, wie vorher, alternativ kannst Du natürlich auch die speziellen Funktionen für Zeitabweichungen benutzen.
 
Ja, oder wie ich schon anmerkte:

CODE
CREATE TABLE `test` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(30),
`time` int(10) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `test` (`name`, `time`) VALUES ('test', unix_timestamp());
SELECT `time` from `test`;
+------------+
| time |
+------------+
| 1179342829 |
+------------+



wink.gif
, den.
 
QUOTE (Jeronnymoe @ Di 15.05.2007, 14:03)Vielen Dank für eure Hilfe! Dank eurer Tipps und ein bisschen Eigenleistung hab ich den
Quelltext um ca. 50% halbiert. Hier mal ein kleiner Auszug.

Neu

...

Alt

...

Na, die eigentlich spannende Frage wäre doch nun:

Bau in beide Codes einen Zeitmesser ein - und vergleiche die Verarbeitungszeit.

Dann siehst Du, ob sich die Arbeit angesichts deiner ursprünglichen Fragestellung


QUOTE Datenbankwerte am schnellsten ausgeben


gelohnt hat.

PS: In NET ist das einfach. Da muß ich nur Trace einschalten ... und kriege dann ganz genaue Auflistungen, wann welche Rahmenprozeduren aufgerufen wurden.
 
Zurück
Oben