Кеширане с файлове в PHP

от Стоян Кьосев

Кеширането е често срещана тема, що се отнася до уеб програмирането. Интересно е, че аз лично съм попадал често на хора, занимаващи се от доста време с php, за които тази тема е мъглива, затова ще се опитам да хвърля малко светлина по въпроса.

Общо взето страниците представят видимо статична дата, която обаче е с динамичен контекст. Например – страницата за обява в сайт за недвижими имоти. Отделните елементи на обявата (град, описание и т.н.) се вземат от базата данни, което става при всеки request (при всяко отваряне на страницата). Като информация обявата се променя сравнително рядко, т.е. заявката към базата данни и обработката на върнатия резултат си остават същите. Голяма част от времето за изпълнение на скрипта се поглъща именно от заявки за бази данни. Тук е най-подходящо да се приложи кеширане на ниво файл, което следва следните стъпки:

  • - Проверка дали резултата с данните не е вече в кеша.
  • - Ако е и валидността не е изтекла (изминал е 1 час от записа) – ползваме него
  • - Ако не е или валидността е изтекла- изтегляме го от базата данни, обработваме го и го записваме в кеша за 1 час (примерно)

Това означава, че в рамките на 1 час след записа, няма да има никакви обръщения към базата данни, а ще се ползва директно готовия резултат.

Из нета съществуват доста решения за кеширане, като се започне от решения за цялостно кеширане, който не се ограничават само до файловата система и се стигне до един от най-популярните пакети за файлово кеширане. Аз лично съм ползвал доста често PEAR::Cache_Lite, докато един от проектите ми не почна да бави значително, а причината се оказа именно Cache_Lite. Най-вероятно съществува начин да се коригира, а и грешката може да е била в моя код или в настройките за класа. Отдавна обаче се канех да си създам кеширащ клас. Първо защото настоящите решения са прекалено тежки, тъй като се опитват да бъдат all-in-one, второ защото кешът не е голяма философия и не ми отне много време да си направя собствен клас.

Пример А:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
require_once 'TinyCache.php';
 
function dbGetComplexMultiJoinQuery()
{
    return array(
            0 => 'item - ' . rand(1, 21),
            1 => 'item - ' . rand(1, 21)
        );
}
 
$options = array('path' => dirname(__FILE__), 'ttl' => 2);
$varcache = TinyCache::getInstance('var', $options);
 
if (!$data = $varcache->getVar('a')) {
    $data = dbGetComplexMultiJoinQuery();
    $varcache->putVar('a', $data);
}
 
var_dump($data);

Масивът $options приема следните ключове:

‘path’ | @string | default: ‘/tmp/’
пълен път до директорията, където ще се съхранява кеш файловете
‘ttl’ | @integer | default: 3600
време в секунди, за което ще се прази кеша преди да се обнови
‘prefix’ | @string | default: ‘cache_’
представката на файловете с кеша
‘auto_serialize’ | @boolead | default: true
(важи само за varCache) – флаг дали автоматично да сериализира/десериализира записваната променлива (default: true)

Пример Б:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
require_once 'TinyCache.php';
 
$options = array('path' => dirname(__FILE__), 'ttl' => 2);
$flowcache = TinyCache::getInstance('flow', $options);
 
if (!$flowcache->start('b')) {
    $result = rand(100, 200);
    $row_num = $result / 10
    echo 'The result from mega complex calculations is ' . $result;
    for ($i = 1; $i < $row_num; $i++) {
        echo 'Row data: ' . $i;
    } 
    $flowcache->stop();
}

Да обощя – кешът е подходят за облекчаване на системата от ресурсоемък процес, генериращ данни, които не се сменят често, като най-честото му приложение е записването на резултат от заявка към база данни (VarCache) или записването на генериран html за дадена страница (FlowCache).