Конвейер Redis, работающий с ошибками кэша
Я пытаюсь найти лучший способ реализовать конвейерную обработку Redis. Мы используем Redis в качестве кэша поверх MySQL для хранения пользовательских данных, списков продуктов и т. Д. Я использую это как отправную точку: https://joshtronic.com/2014/06/08/how-to-pipeline-with-phpredis/
Мой вопрос, если у вас есть массив идентификаторов, правильно отсортированных. Вы перебираете конвейер redis следующим образом:
$redis = new Redis();
// Opens up the pipeline
$pipe = $redis->multi(Redis::PIPELINE);
// Loops through the data and performs actions
foreach ($users as $user_id => $username)
{
// Increment the number of times the user record has been accessed
$pipe->incr('accessed:' . $user_id);
// Pulls the user record
$pipe->get('user:' . $user_id);
}
// Executes all of the commands in one shot
$users = $pipe->exec();
Что происходит, когда $pipe->get('user:' . $user_id);
недоступен, потому что он не был запрошен ранее или был удален Redis и т. д.? Предполагая, что это результат № 13 из 50, как нам а) узнать, что мы не смогли извлечь этот объект и б) правильно отсортировать массив пользователей?
Спасибо
1 ответ
Я отвечу на вопрос, касающийся протокола Redis. Как это работает на конкретном языке, более или менее одинаково в этом случае.
Прежде всего, давайте проверим, как работает конвейер Redis: это просто способ отправить несколько команд на сервер, выполнить их и получить несколько ответов. В этом нет ничего особенного, вы просто получаете массив с ответами на каждую команду в конвейере.
Почему конвейеры работают намного быстрее, потому что время для каждой команды сохраняется, т. Е. Для 100 команд существует только одно время туда и обратно вместо 100. Кроме того, Redis выполняет каждую команду синхронно. Выполнение 100 команд может потребовать битвы 100 раз, чтобы Redis мог выбрать эту единственную команду, конвейер обрабатывается как одна длинная команда, поэтому требуется только один раз, чтобы ждать, когда он будет выбран синхронно.
Вы можете прочитать больше о конвейерной обработке здесь: https://redis.io/topics/pipelining. Еще одно замечание: поскольку каждый конвейерный пакет работает бесперебойно (с точки зрения Redis), имеет смысл отправлять эти команды в виде фрагментов с возможностью просмотра, то есть не отправлять команды по 100 тыс. В одном конвейере, которые могут блокировать Redis на длительный период времени, разбить их на куски по 1к или 10к команд.
В вашем случае вы запускаете в цикле следующий фрагмент:
// Increment the number of times the user record has been accessed
$pipe->incr('accessed:' . $user_id);
// Pulls the user record
$pipe->get('user:' . $user_id);
Вопрос в том, что вкладывается в трубопровод? Допустим, вы обновите данные для u1
, u2
, u3
, u4
как идентификаторы пользователей. Таким образом, конвейер с командами Redis будет выглядеть так:
INCR accessed:u1
GET user:u1
INCR accessed:u2
GET user:u2
INCR accessed:u3
GET user:u3
INCR accessed:u4
GET user:u4
Скажем так:
- к u1 обращались 100 раз,
- к u2 обращались 5 раз,
- U3 не был доступен раньше и
- u4 и сопутствующих данных не существует.
Результатом будет в этом случае массив ответов Redis, имеющих:
101
u1 string data stored at user:u1
6
u2 string data stored at user:u2
1
u3 string data stored at user:u3
1
NIL
Как видите, Redis будет обрабатывать пропущенные значения INCR как 0
и выполнить incr(0)
, Наконец, Redis ничего не сортирует, и результаты будут отображаться в запросе.
Привязка к языку, например, драйвер Redis, просто проанализирует для вас этот протокол и предоставит представление для проанализированных данных. Не соблюдая порядок команд, драйвер Redis не сможет работать правильно, а вы, как программист, сможете определить что-то. Просто имейте в виду, что запрос не дублируется в ответе, т.е. вы не получите ключ для u1
или же u2
когда делаешь GET
, но только данные для этого ключа. Таким образом, ваша реализация должна помнить, что на позиции 1
(индекс с нуля) приходит результат GET
за u1
,