Почему срок действия ключей Redis не истекает?

Я проверил эти вопросы введите здесь описание изображения, но они не помогли мне решить мою проблему. Я использую Redis в качестве хранилища ключевых значений для ограничения скорости в моем приложении Spring REST с использованием библиотеки spring-data-redis. Тестирую с огромной нагрузкой. В этом я использую следующий код для хранения ключа, а также устанавливаю время истечения срока действия. В большинстве случаев срок действия ключа истекает, как и ожидалось. Но иногда срок действия ключа не истекает!

фрагмент кода

RedisAtomicInteger counter = counter = new RedisAtomicInteger("mykey");
counter.expire(1, TimeUnit.MINUTES);

Я проверил доступность ключей с помощью инструмента redis-cli

ключи *

а также

имя ключа ttl

redis.conf со значениями по умолчанию.

Какие-либо предложения ?

Редактировать 1:

Полный код:

Функция находится в аспекте

public synchronized Object checkLimit(ProceedingJoinPoint joinPoint) throws Exception, Throwable {

        boolean isKeyAvailable = false;
        List<String> keysList = new ArrayList<>();

        Object[] obj = joinPoint.getArgs();
        String randomKey = (String) obj[1];
        int randomLimit = (Integer) obj[2];

        // for RedisTemplate it is already loaded as 

        // @Autowired
        // private RedisTemplate template; 

        // in this class
        Set<String> redisKeys = template.keys(randomKey+"_"randomLimit+"*");
        Iterator<String> it = redisKeys.iterator();
        while (it.hasNext()) {
               String data = it.next();
               keysList.add(data);
        }

        if (keysList.size() > 0) {
            isKeyAvailable = keysList.get(0).contains(randomKey + "_" + randomLimit);
        }

        RedisAtomicInteger counter = null;
        // if the key is not there
        if (!isKeyAvailable) {
              long expiryTimeStamp = 0;
              int timePeriodInMintes = 1;
              expiryTimeStamp = new Date(System.currentTimeMillis() + timePeriodInMintes * 60 * 1000).getTime();
              counter = new RedisAtomicInteger(randomKey+ "_"+ randomLimit + "_" + expiryTimeStamp,template.getConnectionFactory());
              counter.incrementAndGet();
              counter.expire(timePeriodInMintes, TimeUnit.MINUTES);
              break;

        } else {

              String[] keys = keysList.get(0).split("_");
              String rLimit = keys[1];

              counter = new RedisAtomicInteger(keysList.get(0), template.getConnectionFactory());
              int count = counter.get();
              // If count exceeds throw error
              if (count != 0 && count >= Integer.parseInt(rLimit)) {
                    throw new Exception("Error");
               }  
               else {
                    counter.incrementAndGet();
              }
      }
        return joinPoint.proceed();
    }

когда эти строки работают

RedisAtomicInteger counter = counter = new RedisAtomicInteger("mykey"); counter.expire(1, TimeUnit.MINUTES);

Я вижу

75672562.380127 [0 10.0.3.133:65462] "KEYS" "mykey_1000*"
75672562.384267 [0 10.0.3.133:65462] "GET" "mykey_1000_1475672621787"
75672562.388856 [0 10.0.3.133:65462] "SET" "mykey_1000_1475672621787" "0"
75672562.391867 [0 10.0.3.133:65462] "INCRBY" "mykey_1000_1475672621787" "1"
75672562.395922 [0 10.0.3.133:65462] "PEXPIRE" "mykey_1000_1475672621787" "60000"
...
75672562.691723 [0 10.0.3.133:65462] "KEYS" "mykey_1000*"
75672562.695562 [0 10.0.3.133:65462] "GET" "mykey_1000_1475672621787"
75672562.695855 [0 10.0.3.133:65462] "GET" "mykey_1000_1475672621787"
75672562.696139 [0 10.0.3.133:65462] "INCRBY" "mykey_1000_1475672621787" "1" 

в журнале Redis, когда я "МОНИТОРЮ" его в


person prashanth-g    schedule 04.10.2016    source источник
comment
Что показывает ttl для ключа?   -  person Chris Tanner    schedule 04.10.2016
comment
@ChrisTanner показывает -1   -  person prashanth-g    schedule 04.10.2016
comment
в redis-cli введите команду MONITOR и посмотрите, идет ли какая-либо команда на сервер redis между этими двумя строками.   -  person Karthikeyan Gopall    schedule 04.10.2016
comment
@KarthikeyanGopall две строки находятся внутри потокобезопасной функции. И я тестирую с огромной нагрузкой.   -  person prashanth-g    schedule 04.10.2016
comment
Какая команда выполняется на сервере для RedisAtomicInteger counter = counter = new RedisAtomicInteger(mykey); эта строка кода? Если важно понять, как RedisAtomicInteger работает внутри   -  person Karthikeyan Gopall    schedule 05.10.2016
comment
В приведенном выше коде нет ничего плохого. Любые вызовы RedisAtomicInteger.set(…) будут очищать TTL. Еще немного кода/воспроизводимый тестовый пример поможет отследить проблему.   -  person mp911de    schedule 05.10.2016
comment
@ mp911de Я отредактировал вопрос, указав дополнительные данные. Не могли бы вы проверить?   -  person prashanth-g    schedule 05.10.2016


Ответы (1)


Изменить: теперь, с обновленным кодом, я считаю, что ваша методология в корне ошибочна, помимо того, что вы сообщаете.

То, как вы это реализовали, требует запуска KEYS в производстве - это плохо. По мере масштабирования вы будете вызывать растущую и ненужную системную блокировку нагрузки на сервер. Как говорится в каждой части документации, не используйте keys в рабочей среде. Обратите внимание, что кодирование срока действия в имени ключа не дает вам никаких преимуществ. Если вы сделаете эту часть имени ключа меткой времени создания или даже случайным числом, ничего не изменится. Действительно, если убрать этот бит, ничего не изменится.

Вместо этого более разумным путем было бы использовать ключевое имя, которое не зависит от времени. Использование истечения срока действия выполняет эту функцию за вас. Давайте назовем вашу вещь с ограниченной скоростью "сеансом". Имя вашего ключа без метки времени — это «идентификатор сеанса». Установив для него срок действия 60 с, он больше не будет доступен на отметке 61 с. Таким образом, вы можете безопасно увеличивать и сравнивать результат с вашим лимитом, не зная текущего времени или срока действия. Все, что вам нужно, это имя статического ключа и соответствующий срок действия, установленный для него.

Если вы INCR используете несуществующий ключ, Redis вернет «1», что означает, что он создал ключ и увеличил его за один шаг/вызов. так что в основном логика выглядит так:

  1. создать идентификатор сеанса
  2. приращение счетчика с использованием идентификатора
  3. compare result to limit
    1. if count == 1, set expiration to 60s
    2. количество идентификаторов > ограничение, отклонение

Шаг 3.1 важен. Число 1 означает, что это новый ключ в Redis, и вы хотите установить для него срок действия. Все остальное означает, что срок действия уже должен быть установлен. Если вы установите его в 3.2, вы прервете процесс, потому что он будет сохранять счетчик более 60 секунд.

При этом вам не нужно иметь динамические имена ключей, основанные на времени истечения срока действия, и, следовательно, не нужно использовать keys, чтобы узнать, существует ли существующий «сеанс» для объекта с ограниченной скоростью. Это также делает ваш код намного проще и предсказуемее, а также сокращает количество обращений к Redis, что означает, что он будет меньше нагружать Redis и работать лучше. Что касается того, как это сделать с клиентской библиотекой, которую вы используете, я не могу сказать, потому что я не очень хорошо с ней знаком. Но основная последовательность должна быть переведена на него, поскольку она довольно проста и проста.

Однако вы не показали ничего, что могло бы поддержать утверждение о том, что истечения срока действия не происходит. Все, что вы сделали, это показали, что Redis действительно велят и установили срок действия. Чтобы подтвердить свое требование, вам необходимо показать, что срок действия ключа не истекает. Это означает, что вам нужно показать получение ключа после истечения срока действия и что счетчик не был «сброшен» путем воссоздания после истечения срока действия. Один из способов узнать об истечении срока действия — использовать уведомления о пространстве ключей. При этом вы сможете увидеть, как Redis сообщает, что срок действия ключа истек.

Где этот процесс немного потерпит неудачу, так это в том случае, если вы делаете несколько окон для ограничения скорости или если у вас гораздо большее окно (например, 10 минут), и в этом случае отсортированные наборы могут быть более разумным вариантом для предотвращения предварительной загрузки запросов. - по желанию. Но, как написано в вашем примере, вышеприведенное будет работать нормально.

person The Real Bill    schedule 05.10.2016
comment
На самом деле я вижу PEXPIRE mykey_1000_1475672621787 60000. Отредактировал вопрос для получения дополнительной информации. - person prashanth-g; 05.10.2016
comment
Какую версию Redis вы используете? - person The Real Bill; 05.10.2016
comment
используя версию 3.2 в windows - person prashanth-g; 05.10.2016
comment
Вы все еще не показываете конечный результат. Нет никаких указаний на то, что не происходит истечения срока действия, просто есть один набор. Ваши журналы отображаются в течение одной секунды и не включают вызов TTL. Как долго вы ждете, чтобы проверить ключ? Как вы проверяете ключ? Что происходит после вызова создания счетчика, как/где он используется? Данные ясно показывают, что Redis должным образом приказали истечь сроком действия данных, но ничто не указывает на то, что он этого не сделал. За исключением того, что мы ничем не можем вам помочь. Кстати, прекратите использовать keys и вместо этого используйте TTL непосредственно на ключе. - person The Real Bill; 05.10.2016
comment
Как долго вы ждете, чтобы проверить ключ? - Тест длится 10 минут, поэтому я проверяю после этого. Между ними создается несколько ключей, срок действия которых истек. Но иногда срок действия ключа не истекает. / Как вы проверяете ключ? - Использование ttl keyname(показывает - 1) / Что происходит после вызова создания счетчика, как/где он используется? - При вызове счетчика в Redis выполняется команда SET с ключом / остановить использование ключей и вместо этого использовать TTL непосредственно для ключа - Не удалось найти ttl непосредственно в классе RedisTemplate, поэтому keys*. Предложите мне, если что-нибудь доступно. - person prashanth-g; 05.10.2016
comment
Также показанные здесь журналы Redis фиксируются во время выполнения кода. После окончания прогона видим как - 75672562.696139 [0 10.0.3.133:65462] INCRBY mykey_1000_1475672621787 1 в Redis MONITOR. - person prashanth-g; 05.10.2016
comment
Спасибо за ваш ответ @the-real-bill. Это действительно очень помогает. Я не использую ключи в prod. Это просто локальная/отладочная цель. Я использую EXISTS в prod. Временная метка, прикрепленная к ключу, предназначена для моей локальной справки. Наконец, ваши предложения помогли мне проверить некоторые ключевые вещи в Redis. Еще раз спасибо. Не работает нормально - person prashanth-g; 06.10.2016