Расширенное программирование: работа с кэшем.
Давайте рассмотрим следующий пример. У нас есть две таблицы, страны и города, и они связаны следующим образом
select * from countries
inner join cities on countries.idcountry=cities.idcities
И мы хотим кэшировать результат.
Какова ваша мотивация?
Наша мотивация заключается в том, что самый быстрый запрос к базе данных – это запрос, который не выполняется .
В этом случае мы хотим кэшировать информацию один раз, хранить ее в памяти и использовать много раз. Таким образом, мы могли бы использовать одни и те же данные много раз, но мы запрашиваем базу данных один раз. Однако мы также должны учитывать, что база данных также имеет свой собственный кэш, но мы им не управляем, поэтому мы не знаем, что кэшируется, когда и где, и обычно он очищается автоматически.
Аннулирующий кэш.
Основная проблема с кэшем – это недействительность.
Например, что, если мы закэшируем запрос?
select * from countries; -- 100 countries
А позже мы решаем добавить новую страну (например, Южный Судан). Однако наш кэш не знает, что существует новая страна, поэтому мы должны аннулировать весь кэш.
select * from countries; -- 100 countries (from cache) -- we invalidate the cache select * from countries; -- 101 countries (from the database)
Аннулирование с помощью идентификатора
Эта форма признания недействительным делает недействительным конкретный запрос. Однако нам нужно (в некотором роде) знать, какой кэш является недействительным, поэтому непрактично, если мы хотим сделать его недействительным вручную. Обычно он используется внутри страны.
Как это делается?
select * from countries;
Мы могли бы использовать запрос в качестве идентификатора, но это практично. Вместо этого мы могли бы сгенерировать хэш (например, sha256).
и это наше удостоверение личности
286c960d1c9c21e84df5a263bddcd2771da976fa306cfdb3fdea60e174a680a8
Хотя он не короче, но всегда имеет одинаковую длину, даже если запрос длинный или более сложный, поэтому его можно использовать в качестве ключа.
Но что, если в запросе есть аргументы?.
select * from countries where idcountry=?;
Затем мы также сохраняем аргументы и генерируем хэш, включая аргументы. Итак, наш хэш содержит всю информацию для запроса: запрос и аргументы (если таковые имеются).
Так затем
select * from countries; -- it reads from the database and store in the cache -- here we invalidate the cache id 286c960d1c9c21e84df5a263bddcd2771da976fa306cfdb3fdea60e174a680a8 select * from countries; -- since the cache is gone, then it reads again from the database and store in the cache
Аннулирование через TTL Время жизни
Одним из способов аннулирования кэша является использование TTL. Итак, у нашего кэша есть срок годности. В этом есть свои плюсы и минусы.
- Плюсом является то, что с признанием недействительным легко справиться, потому что оно работает автоматически.
- Минусы: если мы дадим короткий срок службы, нам следует чаще пользоваться базой данных. Например, если мы запрашиваем данные каждую минуту, а срок действия часового TTL истекает каждые 2 минуты, наша эффективность составляет 50% (1 из 2 вызовов использует кэш).
- Другие минусы: если мы дадим ему долгую жизнь, то сможем прочитать устаревшую информацию.
select * from countries; -- we store it for (example) 5 seconds -- (we wait 3 seconds) select * from countries; -- it reads from the cache that it is still alive -- (we wait another 3 seconds) select * from countries; -- it reads from the database because the cache expired.
Инвалид по линии семьи или группы
Другой способ аннулировать кэш – это использовать семейство или группу кэша.
select * from countries; -- stored in the family called "countries" -- countries=['select * from countries']
и
select * from countries where idcountry=56; -- also stored in the family called "countries" -- countries=['select * from countries','select * from countries where idcountry=56']
Затем мы решаем добавить новую страну, чтобы мы могли аннулировать все семейство “страны”, и это семейство страны аннулирует оба запроса.
Однако
Это наш первоначальный запрос.
select * from countries
inner join cities on countries.idcountry=cities.idcities
Этот запрос может зависеть от двух семейств: “страны” и “города”. Таким образом, этот запрос может быть признан недействительным двумя семьями, если мы добавим новую страну или если мы добавим новый город.
Реализации
Мы будем использовать две библиотеки.
ЭФФЕКТ/Сделано: К Одному (github.com )
EFTEC/Кэшедон: Кэш Один – это кэш сервиса для php (github.com )
Вы можете добавить его вручную или с помощью композитора.
composer require eftec/pdoone composer require eftec/cacheone
Cache One работает с Redis (рекомендуется), расширением APC PHP, Memcache или файловой системой.
Подключение к базе данных и система кэширования
// You must add the libraries prior to be used
$cache=new CacheOne('redis',127.0.0.1,'redistest',6379); // if you don't have redis, then you could use any other.
$cache->setSerializer('json-object'); // the information is stored as json-object.
$pdoOne=new PdoOne('mysql',127.0.0.1,'root','abc.123','dbtest');
$pdoOne->setCacheService($cache); // di
$pdoOne->open(); // open database
Кэширование информации
$countries=$pdoOne->useCache(3600)
->select('*')
->from('countries')
->toList(); // cache lasts 1 hour
$countries=$pdoOne->useCache(3600,'countries')
->select('*')
->from('countries')
->toList(); // cache lasts 1 hour or until the family of cache "countries" is purged.
$countries=$pdoOne->useCache(3600,'*') // indicates to use as family the table indicated in methods from() and joins().
->select('*')
->from('countries')
->toList(); // cache lasts 1 hour or until the family of cache "countries" is purged.
$countries=$pdoOne->useCache(3600,'*') // indicates to use as family the table indicated in methods from() and joins().
->select('*')
->from('countries')
->innerjoin('cities on countries.idcountry=cities.idcountry')
->toList(); // cache lasts 1 hour or until the family of cache "countries" or "cities" is purged.
Аннулирование кэша
Мы могли бы аннулировать кэш, используя два метода, аннулируя с помощью метода invalidate Cache(), где мы могли бы аннулировать с помощью идентификатора или с помощью семейства кешей.
Второй метод заключается в использовании кэша и выполнении одной из операций DML (вставка, обновление и удаление). Он автоматически очищает семейство кэша.
$pdoOne->invalidateGroup('','countries'); // we invalidate all the cache of the family countries
// or we could invalidate for insert,update or delete
$pdoOne->useCache(3600,'*')->from("countries")->set(['idcountry,name'],[101,'South Sudan'])->insert();
ПСР-6
Почему бы не следовать стандарту PSR-6?
Во-первых, это не официальный стандарт. Официальный стандарт – это стандарт, написанный и соблюдаемый PHP. Но это не главная проблема.
Во-вторых, в спецификации не хватает деталей, она добавляет статистику, которая может быть полезной, но она на 100% работоспособна.
Например, наше начальное упражнение.
select * from countries
inner join cities on countries.idcountry=cities.idcities
PSR-6 позволяет сохранять кэш в виде
$result=[..]; // storing the result of the query
$item=new CacheItemClass('KEYCACHE',result,500); // implements CacheItemInterface
// configure item
CacheService::save(CacheItemInterface $item);
Но что, если мы изменим страны таблицы или города таблицы? Может быть, это не влияет на запрос но на самом деле мы этого не знаем.
Мы могли бы аннулировать кэш, если бы знали идентификатор
CacheService::deleteItem('KEYCACHE');
Но как мы связали “вставить в страны” или “вставить в города” с КЭШЕМ КЛЮЧЕЙ?
Мы могли бы использовать кэш напрямую через Pdo One (сохранение) или с помощью библиотечного кэша One
Хранение кэша
$result=[..]; // storing the result of the query $cache->set(['countries','cities],"KEYCACHE",result,500);
Аннулирование кэша
$cache->invalidate('KEYCACHE');
// or
$cache->invalidateGroup('countries'); // it invalidates all the cache stores in the group of family countries
// or
$cache->invalidateGroup('cities'); // it invalidates all the cache stores in the group of family cities
Оригинал: “https://dev.to/jorgecc/advanced-programming-working-with-cache-5d93”