 |
12.11.2005, 23:24
| Nach oben
#1 | | Benjamin Klaile
Registriert seit: 02.12.2004 Ort: Remagen
Beiträge: 4.516
| Cachen von SQL-Queries. Sinnvoll?
Hi,
ich habe soeben auf Zend.com dieses Tutorial hier gefunden: SQL Query Caching
Ich wollte jetzt mal fragen, ob da jemand Erfahrung mit hat. Mir geht es da vor allem darum, ob sich das Caching überhaupt lohnt.
ist es günstiger "eine" Datenbankabfrage durchzuführen oder eine Caching-Technik anzuwenden?
Freu mich auf Antworten  .
Grüße Ben.
PS:
Bitte auch die Kommentare beachten |
| |
13.11.2005, 07:27
| Nach oben
#2 | | Goldman.de
Registriert seit: 09.10.2005 Ort: Frankfurt am Main
Beiträge: 190
|
warum steht denn bei bsp. ebay das eine verzögerung von 3min sein kann
( da deren cacheengine auf 3 min(max) steht
wenn nur 10 querys pro tag dann sicher nicht ansonsten durchaus
ich nutze dazu die Cacheengine von PEAR wobei ich das Statement getrennt vom result cache
da ich ja eh mit einer dbklasse arbeite ist das cachen hier sehr einfach
kleines beispiel PHP-Code: <?php
// }}} // {{{ getNums()
/** * - Execute mysql_num_rows with parameters * * @param string $table Tablename * @param string $para whereclausel * @param string $col selected column * @return integer COUNT * @access public */ function getNums($table, $para, $field=false) { global $objCACHE; $err = false; $field = false==$field ? '*' : $field; $return = false; $cacheId=$objCACHE->generateID("SELECT COUNT($field) as return FROM ".PRE."$table $para"); if (true==$objCACHE->get($cacheId, SQL_STATEMENTS)) { return $objCACHE->get($cacheId, SQL_RESULTS); } $objCACHE->save($cacheId,"SELECT COUNT($field) as return FROM ".PRE."$table $para", EXP_CACHE_TIME, SQL_STATEMENTS); $sql=$this->query("SELECT COUNT($field) as num FROM ".PRE."$table $para"); if (true==mysql_error()) { $err=Error_class::raiseError('getNums:<br />'.mysql_error(),2,'',__FILE__,get_class($this),__LINE__); Error_class::errorHandling($err); } else { $return=$this->fetch_assoc($sql); } $objCACHE->save($cacheId,$return['num'], EXP_CACHE_TIME, SQL_RESULTS); return $return['num']; } ?>
hier ein einfaches sql-cache beispiel PHP-Code: <?php
class sqlCache
{
// Initialisieren der benoetigten Werte
var $server="localhost";
var $user="xx";
var $password="xx";
var $database="xx";
var $data=array(); // Speichert die Daten aus der Abfrage
var $filename=''; // Enthaelt den Namen der Cache-Datei
// Konstruktor der Klasse
// Initialisiert das Objekt
// $expire ist die Gueltigkeitsdauer des Caches in Sekunden
// $query_data ist ein Array mit Suchbegriffen
function sqlCache($expire, $query_data)
{
// Sortiert Suchbegriffe alphabetisch fuer den Dateinamen
sort($query_data);
// erstellt den Dateinamen
$this->filename=implode("_",$query_data).".dat";
$this->filename=strtolower($this->filename);
// Cache loeschen, damit filemtime() korrekt arbeitet
clearstatcache();
// Existiert Cache-Datei und ist sie gueltig? => Cache-Hit
if ((true==file_exists($this->filename)) &&
(time()-$expire) <= filemtime($this->filename)
)
{
// Ja, wir haben einen Hit => Datei einlesen
$raw_data=file_get_contents($this->filename);
// Die ersten 32 Byte sind der md5-Hash
$md5=substr($raw_data,0,32);
// Restliche Daten separieren
$ser_data=substr($raw_data,32);
// Gelesene Daten OK?
if ($md5 != md5($ser_data))
{
// Daten nicht OK, Datenbank abfragen
$this->_queryData($query_data);
}
else
{
// Daten sind OK => entpacken
$this->data=unserialize($ser_data);
}
}
else
{
//Cache-Miss => Cache-File war nicht aktuell
// oder nicht angelegt => Daten abfragen
$this->_queryData($query_data);
// Cache-Datei schreiben
$this->_makeFile();
}
}
function _queryData($query_data)
{
// Verbindung zur Datenbank oeffnen
$db=mysql_connect($this->server,
$this->user,
$this->password);
mysql_select_db($this->database,$db);
// SQL-Befehl konstruieren
$bed1=implode("%'OR titel LIKE '%",$query_data)."%' ";
$bed2=implode("%'OR beschreibung LIKE '%",
$query_data)."%' ";
$sql="SELECT titel,beschreibung,bidder FROM auktionen
WHERE (
titel LIKE '%$bed1
OR beschreibung LIKE '%$bed2)";
// Abfrage zur Datenbank
$query_result=mysql_query($sql,$db);
if (false===$query_result)
{
die ("Fehler in Abfrage<br />".mysql_error());
}
mysql_close($db);
// Alle Daten auslesen und in Array speichern
while ($zeile=mysql_fetch_row($query_result))
{
$this->data[]=$zeile;
}
}
function _makeFile ()
{
// Daten packen, um sie speichern zu koennen
$ser_data=serialize($this->data);
// md5-Hash berechnen
$md5=md5($ser_data);
// In PHP5 koennen Sie alternativ
// file_put_contents($this->filename, “$md5$ser_data”-)
// nutzen
$fp=fopen($this->filename,"w");
if (false==$fp)
{
die ("Konnte Cache-Datei nicht anlegen");
}
// md5-Hash speichern
fwrite ($fp,$md5);
// Serialisierte Daten speichern
fwrite ($fp,$ser_data);
fclose($fp);
}
}
// Cache-Datei soll 60 Sekunden gueltig sein
$expire=60;
// Suchbegriffe, die der Benutzer eingegeben hat
$daten=array("corrado","vw");
// Neues Cache-Objekt für diese Suchbegriffe erzeugen
$sql_result=new sqlCache($expire,$daten);
// Daten aus $sql_result->data weiterverarbeiten.
?>
mfg
Geändert von J33d3X (13.11.2005 um 07:30 Uhr)
|
| |
13.11.2005, 10:03
| Nach oben
#3 | | Corvin Gröning
Registriert seit: 19.03.2005 Ort: S-H | Flensburg
Beiträge: 459
| Zitat: |
Zitat von Ben ist es günstiger "eine" Datenbankabfrage durchzuführen oder eine Caching-Technik anzuwenden? | Wie J33d3X schon schreibt, bei 10 Querys pro Tag lohnt sich ein Query-Caching sicher nicht. Bei vielleicht 1000 und mehr Querys pro Tag lohnt sich das schon eher. Wenn nur alle 5 Minuten eine Query ausgeführt wird und ansonsten die Daten aus einer Datei gelesen werden, entlastet das den Server.
__________________ |
| |
14.11.2005, 16:27
| Nach oben
#4 | | Gast | http://dev.mysql.com/doc/refman/4.1/en/query-cache.html
mysql optimiert also, korrekt eingerichtet und eine aktuelle version vorausgesetzt, bereits ganz gut - besser als alles, was man auf den 'zend'-seiten als sog. knowledge vermittelt bekommt - ich wage es zu bezweifeln, dass unserialize() eines größeren datensatzes schneller ist als das query cache von mysql.
meine erfahrung ist:
es sind nicht unbedingt die mysql-abfragen, die 'langsam' sind, sondern eher das parsen der template-engines und das erzeugen des html-outputs. langsame mysql-abfragen lassen sich bereits mit leichten index-spielen oft optimieren, die 'geschwindigkeit' von php aber nicht.
ergo:
nicht die abfrage selbst cachen, sondern den kompletten html-output... d.h. bei einem 'cache miss' die seite erzeugen und die ausgabe zwischenspeichern, bei einem 'cache hit' einfach die bereits fertige html-seite aufrufen - ob per include oder sonstwie ist dann auch egal.
hätte im prinzip auch den vorteil, dass man die statischen oder cache-seiten ziemlich trivial mit proxies implementieren kann - und damit z.b. einen oder mehrere cache-server vor den php-server zu schalten, die sich nur um die ausgabe der 'cache'-seiten kümmern.
damit ist die skalierbarkeit mit hardware gewährleistet und die ist, aktuellen zahlen zufolge, billiger als software-optimierungen.
Geändert von axo (14.11.2005 um 16:33 Uhr)
| |
| |
14.11.2005, 16:39
| Nach oben
#5 | | Benjamin Klaile
Registriert seit: 02.12.2004 Ort: Remagen
Beiträge: 4.516
| Zitat: |
Zitat von axo nicht die abfrage selbst cachen, sondern den kompletten html-output | Yeah. Genauso mach ich das derzeit auch. |
| |
19.11.2005, 14:05
| Nach oben
#6 | | Gast |
Ich habe mir meinen eigene kleinen Cache geschrieben: PHP-Code: <?php class Cache { /** * Location where cache files are stored * @var storeFiles string */ private static $storeFiles = './cache/'; /** * Stores the cached data in a file * @param group string - A group to which the cached file belongs to * @param uniqueId int - A unique id for each cached file * @param data string - Th data to save in the file */ public static function writeFile($group,$uniqueId,$data) { $filename = self::getFilename($group,$uniqueId);
if ($fp = fopen($filename,'xb')) { if (flock($fp,LOCK_EX)) { fwrite($fp,$data); } fclose($fp); touch($filename); // Set time } } /** * Creates the filename. Not the file! * @param group string - A group to which the cached file belongs to * @param uniqueId int - A unique id for each cached file */ protected static function getFilename($group,$uniqueId) { return self::$storeFiles.$group.'_'.md5($uniqueId); } /** * Gets the content of a cached file * @param group string - A group to which the cached file belongs to * @param uniqueId int - A unique id for each cached file * * @return string */ public static function readFile($group,$uniqueId) { $filename = self::getFilename($group,$uniqueId); $data = file_get_contents($filename); // Get's the data of the file return unserialize($data); // return the data } /** * Checks if a file is cached or not * @param group string - A group to which the cached file belongs to * @param uniqueId int - A unique id for each cached file * @param cacheTime int - The caching time in seconds * * @return boolean */ protected static function isCached($group,$uniqueId,$cacheTime) { $filename = self::getFilename($group,$uniqueId); // if the file is cached
if (file_exists($filename) && filemtime($filename)+$cacheTime >= time()) { return true; } // if the file is not cached or antiquated @unlink($filename); return false; } }
class Caching extends Cache { /** * The group name * @var gr string */ protected static $gr; /** * The uniqueId * @var int */ protected static $id; /** * This method starts the cache * @param group string - A group to which the cached file belongs to * @param uniqueId int - A unique id for each cached file * @param cacheTime int - The caching time in seconds * * @return boolean */ public static function start($group,$uniqueId,$cacheTime = 7200) { // if the file is cached if (self::isCached($group,$uniqueId,$cacheTime)) { echo self::readFile($group,$uniqueId); return false; } else { ob_start(); self::$gr = $group; self::$id = $uniqueId; return true; } } /** * Stops caching */ public static function stop() { // Get the puffered data $data = ob_get_contents(); // write the data in the file
self::writeFile(self::$gr,self::$id,serialize($data)); } } ?> Beispiel: PHP-Code: if (Caching::start('sample',01,7200)) { // Hier die SQL queries oder Funktionsaufrufe Cacheing::stop(); }
Funktioniert super und ist schnell. Wenn man am Anfang eines Scripts ob_start() oder ob_start('gz_handler') aufruft. Wird das Script nochmal deutlich schneller. Dann noch einen Cache.
Dann z.B. noch Code optimieren: PHP-Code: // Würde man mit einer while Schleife machen for ($i = 0; $i<mysqli_num_rows($result); ++$i) { // Hier wird bei jedem durchlauf die Funktion mysqli_num_rows() aufgerufen }
// Optimierte Version $numRows = mysqli_num_rows($result); for ($i = 0; $i<$numRows; ++$i) { // Hier werden einfach zwei Variablen verglichen => deutlich schneller }
Man kann auch OPCODE Caches verwenden oder sich einen Query Cache in C schreiben.
| |
| | |
Aktive Benutzer in diesem Thema: 1 (Registrierte Benutzer: 0, Gäste: 1) | | | | Themen-Optionen | Thema durchsuchen | | | |
Forumregeln
| Es ist dir nicht erlaubt, neue Themen zu verfassen. Es ist dir nicht erlaubt, auf Beiträge zu antworten. Es ist dir nicht erlaubt, Anhänge hochzuladen. Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten. HTML-Code ist aus. | | | Alle Zeitangaben in WEZ +1. Es ist jetzt 09:58 Uhr.
|