В Redis есть очень удобная структура хранения данных под названием hashes. Она хороша, но есть у нее один весомый минус - нет возможности присваивать время жизни отдельному полю хэша. Иногда это очень нужно, и приходится искать способы, как реализовать такую возможность. Ниже рассмотрим один из таких способов.
Допустим, нам необходимо сохранить информацию об услуге (name), в частности ее стоимость (cost). Также допустим, что стоимость услуги действует 24 часа (время актуальности), а потом перерасчитывается и сохраняется вновь на 24 часа.
Если бы мы хранили стоимость услуги в структуре strings, то записали бы ее стоимость и установили бы время жизни для данного ключа. Сделали бы это примерно так:
$redis->set('services:name:cost', 1000);
$redis->expire('services:name:cost', 86400);
В итоге через 24 часа ключ удалится средствами Redis, а внутренняя логика вашего приложения увидит его отсутствие, перерасчитает заново стоимость и запишет ее вновь.
Но в некоторых случаях, возникает необходимость хранить свойства какого-либо объекта в отдельных полях хэш таблицы. Это удобнее во многих ситуациях, о которых речь в данной статье не пойдет. Итак, если мы услугу сохраняем в виде hashes, а ее стоимость в отдельном поле хэша, то выглядит это примерно так:
$redis->hset('services:name', 'cost', 1000);
Но для конкретного поля в хэше нельзя установить время жизни. Но следить за актуальностью стоимости и перерасчитывать ее после истечения времени актуальности все равно нужно. Для реализации этой возможности, будем хранить временную метку, а точнее просто time(). Хранить мы ее будем в структуре данных sorted sets - в упорядоченных множествах. Упорядоченные множества хороши тем, что позволяют легко доставать из них ранжированные данные.
Сохраним в sorted sets данные под ключом services:expire. Где в качестве значения будет название ключа hashes сложенное с названием поля, для которого мы сохраняем временную метку. В качестве ранга будет временная метка, при наступлении которой данные будут считаться устаревшими. Сохранение выглядеть будет примерно так:
$redis->zAdd('services:expire', time() + 86400, 'services:expire:cost');
В итоге мы сохранили значение стоимости в поле hashes. Сохранили время ее актуальности в sorted sets. Но как узнать, что время актуальности устарело? Для этого надо сохдать PHP скрипт, который будет запускаться по cron-у и выбирать из sorted sets те значения, у которых ранг в диапазоне от нуля до текущего времени. Тем самым в эту выборку попадут те значения, у которых временная метка устарела. В этом PHP скрипте будет по сути лишь одна команда, взаимодействующая с хранилищем актуальности данных, то есть с sorted sets:
$oldField = $redis->zRangeByScore('services:expire', 0, time());
В итоге в $oldField мы будем иметь массив значений вида
services:name:cost
где services:name
- это ключ в hashes (не забываем, что name - это название услуги), а cost
- это название поля в этом hashes. Т.е в итоге мы получим, что для услуги name устарела ее стоимость. Внутренняя логика приложения перерассчитает ее. Затем снова сохраним ее в hashes, обновим ее актуальность в sorted sets и так далее, до того момента, как вновь истечет время актуальности.
только авторизованные пользователи могут оставлять комментарии