21Μαρ

Εύκολο caching με χρήση PHP και mySQL

, 21 Μαρτίου 2011 | WEB DEVELOPMENT | 0 Σχόλια

Σκοπός αυτού του οδηγού είναι να παρουσιάσουμε ένα απλό τρόπο με τον οποίο μπορούμε να υλοποιήσουμε ένα σύστημα caching, χρησιμοποιώντας php και mysql. Ο οδηγός απευθύνεται κυρίως σε developers με όχι και τόσο μεγάλη εμπειρία και στόχος είναι το αποτέλεσμα να είναι εύκολο στη χρήση και αρκετά ευέλικτο.

Η βασική ιδέα είναι να υλοποιήσουμε μία κλάση η οποίο θα αναλαμβάνει να κάνει αυτόματα το caching για όλες τις μεθόδους που περιέχει. Στο τελικό αποτέλεσμα, κάθε μέθοδος θα περιέχει όλες τις ενέργειες που θέλουμε να κάνουμε και στις παραμέτρους, θα ορίζουμε και το χρόνο για τον οποίο θα ισχύει η cache, πριν χρειαστεί ανανέωση.

Σημείωση: Σε μερικά από τα παρακάτω παραδείγματα δεν φαίνεται όλος ο κώδικάς. Αν τον επιλέξτε και τον επικολήστε στον text editor της επιλογής σας θα μπορέσετε να τον δείτε.

Cache table

Ας δούμε πρώτα το table που χρειάζεται να δημιουργήσουμε στη βάση. Θα του δώσουμε το πολύ πρωτότυπο όνομα ‘cache’. Αυτό είναι το αντίστοιχο CREATE TABLE.

CREATE TABLE IF NOT EXISTS `cache` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`_key` varchar(255) DEFAULT NULL,
`_call` text,
`value` longtext,
`ts` int(11) DEFAULT NULL,
`locktime` int(11) DEFAULT NULL,
`expirationtime` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `_key_2` (`_key`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8
  • _key είναι το μοναδικό αναγνωριστικό για κάθε εγγραφή στην cache. Είναι ένα string που παράγεται εφαρμόζοντας md5 στο όνομα και τις παραμέτρους τις συνάρτησης (περισσότερα γι αυτό στη συνέχεια).
  • _call είναι το όνομα της συνάρτησης και οι παράμετροι.
  • value είναι το αποτέλεσμα που είναι cached.
  • ts είναι το timestamp που δείχνει πότε έγινε η εγγραφή στην cache.
  • locktime είναι το timestamp που δείχνει πότε κλειδώθηκε η εγγραφή στην cache (για να γίνει update).
  • expirationtime είναι το timestamp που δείχνει πότε θα καταστεί άκυρη η εγγραφή στην cache.

Πριν προχωρήσουμε στον κώδικα, ας δούμε με ένα απλό παράδειγμα, πώς θα γίνεται η κλήση των συναρτήσεων. Ας υποθέσουμε ότι έχουμε τη βάση δεδομένων ενός βιβλιοπωλείου και θέλουμε να ψάξουμε βιβλία με βάση το όνομα του συγγραφέα. Σκοπός μας είναι τελικά να έχουμε κάτι τέτοιο:

$cache->searchBooksByAuthor(300, "nikos");

Όπου 300 είναι ο χρόνος σε δευτερόλεπτα που η εγγραφή στην cache θα πάψει να ισχύει.

Ας ξεκινήσουμε με τον ορισμό της κλάσης και μιας μεταβλητής που θα κρατάει το μέγιστο χρόνο που μπορεί μια εγγραφή στην cache να είναι κλειδωμένη. Θα περιλάβουμε και τον ορισμό της προηγούμενης συνάρτησης για αναζήτηση βιβλίων.

class Cache {
var $lock_timeout = 10;
function __call($name, $arguments) {
}
function _searchBooksByAuthor($author) {
// query the database like you would normally do and return the result
}
}

Προσοχή στο underscore (_) που προηγείται του ονόματος της συνάρτησης και επίσης στο ότι το timeout δεν περιλαμβάνεται στα arguments. Θα γίνει σαφές το γιατί συμβαίνει αυτό, μόλις αρχίζουμε να υλοποιούμε την λειτουργικότητα της συνάρτησης __call().

H __call() είναι μία από τις λεγόμενες magic methods της php. Καλείται αυτόματα όταν ζητήσουμε μέθοδο της κλάσης που δεν υπάρχει στην πραγματητότητα. Οπότε, αν στην παραπάνω κλάση ζητήσουμε την συνάρτηση “searchBooksByAuthor” που δεν υπάρχει, τότε θα κληθεί η __call. Τώρα αρχίζει να γίνεται σαφές γιατί χρησιμοποιήσαμε το _ στο όνομα της συνάρτησης μέσα στην κλάση. Θέλουμε να αναγκάσουμε όλες τις μεθόδουμε να περνάνε μέσα από την __call, όπου και θα υλοποίήσουμε όλη την λειτουργικότητα της cache.

Ας ξεκινήσουμε να γράφουμε κώδικα μέσα στην __call. Μία υποσημείωση. Μιας και ο καθένας χρησιμοποιεί το δικό του database wrapper (έτσι τουλάχιστον οφείλουμε να κάνουμε), δεν θα χρησιμοποιήσουμε κάποια πραγματική συνάρτηση για τα queries, αλλά μια φανταστική, την myquery().

// first think we must check that the function actually exists
if ( !method_exists($this, '_'.$name) ) {
// handle the error anyway you like
}
$current_timestamp = time();
$timeout = $arguments[0]; // we said that the first argument will be the time till the cache record invalidates
$expiration_timestamp = $current_timestamp + $timeout;
// lets create the key for the cached row
$arguments[0] = $name;
$call = serialize(array_merge((array)$name, array_slice($arguments, 1)));
$key = md5($call);

Φτιάξαμε λοιπόν ένα serialized array που αποτελείται από το όνομα της συνάρτησης και τις παραμέτρους. Περνώντας αυτό από md5() προκύπτει το τελικό κλειδί της εγγραφής μας στην cache. Η μεταβλητή $call δεν χρησιμοποιείται πουθενά ουσιαστικά, αλλά είναι πολύ χρήσιμη για debugging, αν χρειαστεί κάποια στιγμή να βρείτε συγκεκριμένη συνάρτηση στην cache.

Στη συνέχεια, ελέγχουμε τη βάση και αν δεν υπάρχει εγγραφή με το συγκεκριμένο κλειδί, δημιουργούμε μια νέα στον πίνακα της cache.

// searching for the cached row
$cached = myquery("SELECT * FROM cache WHERE _key = '$key'");
if ( !$cached['id'] ) {
// row doesn't exist in the cache table, we just need to create it
// but first, run the original function and get the result
$data = call_user_func_array(array($this, '_'.$name), array_diff_key($arguments, array($timeout)));
$value = serialize($data);
myquery("
INSERT INTO `cache`
(`_key`, `_call`, `ts`, `locktime`, `expirationtime`, `value`)
VALUES
('$key', '$call', $current_timestamp, 0, $expiration_timestamp, $value)
");
return $data;
}

Αν όμως η εγγραφή υπάρχει, τότε υπάρχουν πολλές περιπτώσεις. Μπορεί να είναι έγκυρη, μπορεί να έχει λήξει, μπορεί να είναι κλειδωμένη ή ακόμα μπορεί και να να είναι προβληματική (πχ κλειδωμένη περισσότερη ώρα από το όριο)

/ if valid
if ( $cached['ts'] + timeout > $current_timestamp ) return unserialize($cached['value']);
// locked, so we'll serve the outdated content
if  ( $cached['locktime'] && $cached['locktime'] +  $this->lock_timeout < $current_timestamp ) return  unserialize($cached['value']);
// if we reach here, either the cache is invalid, or it is locked, and past the locked timeout
// in either case we lock and update the cache row
myquery("UPDATE `cache` SET locktime = $current_timestamp WHERE `_key`='$key'");
$data = call_user_func_array(array($this, '_'.$name), array_diff_key($arguments, array(' ')));
$value = serialize($data);
myquery("
UPDATE `cache` SET
`value` = '$value',
`ts` = $current_timestamp,
`locktime` = 0,
`expirationtime` = $expiration_timestamp
WHERE `_key` = '$key'
");
return $data;

Και με αυτό η κλάση ολοκληρώνεται.

Προφανώς μία τέτοια κλάση είναι χρήσιμη σε περιπτώσεις όπου έχουμε μεγάλα και αργά queries ή και ολόκληρες php functions που είναι πολύ αργές. Έχει μεγάλο πλεονέκτημα ότι είναι αρκετά ευέλικτη και σίγουρα είναι ένα καλό έναυσμα για κάποιο με όχι και τόσο μεγάλη εμπειρία στην php.

Εσείς χρησιμοποιείται κάποιο caching σύστημα στις εφαρμογές σας;

Το παραπάνω άρθρο ήταν Guestpost

Το παραπάνω άρθρο ήταν Guestpost και γράφτηκε από τον Νίκο Ζηνά.
Περισσότερα για τον Νίκο Ζηνά μπορείτε να δείτε στους παρακάτω συνδέσμους:

Εάν ενδιαφέρεστε και εσείς να γράψετε άρθρο για το Web Design Blog, μπορείτε να διαβάσετε περισσότερα στο άρθρο Το Web Design Blog ανοίγει τις πόρτες του!

ΧΡΗΣΙΜΗ ΠΛΗΡΟΦΟΡΙΑ: Το άρθρο Εύκολο caching με χρήση PHP και mySQL γράφτηκε από το WebDesignBlog. Η ομάδα μας σας υπενθυμίζει πως αν θέλετε να ενημερώνεστε για τα νέα του διαδικτύου και για επιλεγμένα άρθρα μας, μπορείτε να γραφτείτε εύκολα στο Newsletter μας ή στο RSS Feed μας.

Σχολιάστε

Back to top

Recent comments

  • Πολυ καλο αθρο πραγματικα!

    Η σωστή αρχιτεκτονική ενός website και τα οφέλη της
  • Πολύ χρήσιμη η λίστα με τα online tools. Ο επιτυχημένος σχεδιασμός μιας ιστοσελίδας βοηθά σε δύο κατευθύνσεις. Η πρώτη είναι ότι βελτιώνει την εμπειρία του επισκέπτη της ιστοσελίδας με συνέπεια ο τελευταίος να βλέπει περισσότερες σελίδες και να μένει περισσότερο χρόνο σε αυτή. Η δεύτερη είναι ότι επειδή ακριβώς βελτιώνονται τα στατιστικά στοιχεία της ιστοσελίδας, όπως το bounce rate, ο μέσος χρόνος παραμονής στην ιστοσελίδα και ο αριθμός των σελίδων ανά επίσκεψη, βελτιώνεται και η κατάταξη της ιστοσελίδας στα οργανικά αποτελέσματα της Google.

    10+1 Χρήσιμα website και on line tools

Latest From Twitter