Что происходит с Redis после нехватки памяти

Redis
Что происходит с Redis после нехватки памяти

предисловие

Как сервер, память не безгранична, поэтому всегда будет исчерпание памяти, поэтому, когдаRedisПосле исчерпания памяти сервера, если вы продолжите выполнять команду запроса,RedisКак это будет обрабатываться?

восстановление памяти

использоватьRedisВо многих случаях при обслуживании некоторые пары ключ-значение действительны только в течение определенного периода времени.Чтобы данные этого типа не занимали память все время, мы можем установить период действия для пар ключ-значение.Redisсквозь4Отдельная команда для установки срока действия ключа:

  • expire key ttl:будетkeyСрок действия значения установлен наttl второй.
  • pexpire key ttl:будетkeyСрок действия значения установлен наttl миллисекунда.
  • expireat key timestamp:будетkeyСрок действия значения устанавливается равным указанномуtimestamp секунды.
  • pexpireat key timestamp:будетkeyСрок действия значения устанавливается равным указанномуtimestamp миллисекунды.

PS: Независимо от того, какая команда используется, в концеRedisНижний слой используетсяpexpireatкоманда для достижения. Кроме того,setКоманды также могут быть установленыkeyВ то же время добавьте время истечения, что может обеспечить атомарность установки значения и установки времени истечения.

После установки срока действия можно пройтиttlиpttlДве команды для запроса оставшегося времени истечения срока действия (если срок действия не установлен, следующие две команды возвращают-1, если установлено недопустимое время истечения, оба возвращают-2):

  • ttl keyвозвращениеkeyОставшиеся истекшие секунды.
  • pttl keyвозвращениеkeyКоличество миллисекунд, оставшихся до истечения срока действия.

Политика истечения срока действия

Если ключ с истекшим сроком действия удален, у нас обычно есть три стратегии:

  • Удаление по времени: установите таймер для каждого ключа, и по истечении срока действия ключ будет удален. Эта стратегия удобна для памяти, ноCPUНе дружелюбно, потому что каждый таймер занимает определенное количествоCPUресурс.
  • Ленивое удаление: независимо от того, истек срок действия ключа или нет, он не будет активно удален.Он будет ждать, пока каждый раз не получит ключ, чтобы определить, истек ли срок его действия.Если срок его действия истек, удалите ключ, в противном случае верните соответствующее значение к ключу. Эта стратегия не является дружественной к памяти и может привести к потере большого количества памяти.
  • Периодическое сканирование: система регулярно сканирует через равные промежутки времени и удаляет все ключи с истекшим сроком действия, если они найдены. Эта стратегия представляет собой относительно компромисс между двумя вышеуказанными стратегиями.Следует отметить, что регулярная частота должна контролироваться в соответствии с реальной ситуацией.Одним из недостатков использования этой стратегии является то, что ключи с истекшим сроком действия также могут быть возвращены.

существуетRedisСреди них он выбирает стратегию2и стратегия3комплексное использование. ноRedisПри периодическом сканировании будут сканироваться только ключи с установленным сроком действия, поскольку ключи с установленным сроком действияRedisхранятся отдельно, поэтому нет возможности отсканировать все ключи:

typedef struct redisDb {
    dict *dict; //所有的键值对
    dict *expires; //设置了过期时间的键值对
   dict *blocking_keys; //被阻塞的key,如客户端执行BLPOP等阻塞指令时
   dict *watched_keys; //WATCHED keys
   int id; //Database ID
   //... 省略了其他属性
} redisDb;

8 стратегий устранения

еслиRedisВсе ключи не просрочены, и память в это время заполнена, тогда клиент продолжает выполнениеsetПока жду заказRedisКак это будет обрабатываться?RedisДля этого сценария предусмотрены различные стратегии устранения.

во-первыхRedisпредоставил параметрmaxmemoryнастроитьRedisМаксимально используемая память:

maxmemory <bytes>

Или вы также можете использовать командуconfig set maxmemory 1GBДинамически изменен.

Если этот параметр не задан, то32разрядная операционная системаRedisнаиболее используемый3GBпамяти, находясь в64Ограничений в операционной системе бит нет.

Redisпредоставлено в8Тип вне политики, параметры могут бытьmaxmemory-policyЧтобы настроить:

стратегия устранения инструкция
volatile-lru Удаляйте ключи со сроком действия, установленным в соответствии с алгоритмом LRU, пока не освободится место. Если ключевой объект для удаления отсутствует, а памяти по-прежнему недостаточно, сообщается об ошибке
allkeys-lru Удаляйте все ключи по алгоритму LRU, пока не освободится место. Если ключевой объект для удаления отсутствует, а памяти по-прежнему недостаточно, сообщается об ошибке
volatile-lfu Ключи с установленным сроком действия удаляются по алгоритму LFU до тех пор, пока не освободится место. Если ключевой объект для удаления отсутствует, а памяти по-прежнему недостаточно, сообщается об ошибке
allkeys-lfu Все ключи удаляются по алгоритму LFU, пока не освободится место. Если ключевой объект для удаления отсутствует, а памяти по-прежнему недостаточно, сообщается об ошибке
volatile-random 随机删除设置了过期时间的键,直到腾出可用空间。 Если ключевой объект для удаления отсутствует, а памяти по-прежнему недостаточно, сообщается об ошибке
allkeys-random Случайным образом удаляйте все ключи, пока не освободится место. Если ключевой объект для удаления отсутствует, а памяти по-прежнему недостаточно, сообщается об ошибке
volatile-ttl В соответствии со свойством ttl объекта «ключ-значение» удалите данные, срок действия которых истекает недавно. Если нет, сообщите об ошибке напрямую
noeviction Стратегия по умолчанию без какой-либо обработки сообщает об ошибке напрямую

PS: стратегия исключения также может использовать команду напрямуюconfig set maxmemory-policy <策略>для динамической конфигурации.

Алгоритм LRU

LRUПолное имя:Least Recently Used. То есть: в последнее время он давно не использовался. Это в основном для времени использования.

Redis улучшил алгоритм LRU

существуетRedisСреди них традиционныеLRUалгоритм, так как традиционныйLRUАлгоритмы существуют2вопросы:

  • Для хранения требуется дополнительное место.
  • могут быть некоторыеkeyЗначение используется часто, но в последнее время не использовалось, поэтомуLRUАлгоритм удаления.

Чтобы избежать вышеперечисленного2Проблема,Redisсреди традиционныхLRUАлгоритм изменен,удалить путем выборки.

Свойство предоставляется в файле конфигурацииmaxmemory_samples 5, значение по умолчанию5, указывает на случайное извлечение5Кусокkeyзначение, то5Кусокkeyстоимость согласноLRUалгоритм удаления, так что очевидно,keyЧем больше значение, тем точнее удаление.

ВыборкаLRUАлгоритм и традиционныйLRUалгоритм,RedisНа официальном сайте есть сравнительная таблица:

  • Светло-серые полосы — это удаленные объекты.

  • Серые полосы — это объекты, которые не были удалены.

  • Зеленый — добавленный объект.

Первая картинка в верхнем левом углу представляет собой традиционнуюLRUалгоритма видно, что когда количество выборок достигает10(вверху справа), уже и традиционныеLRUАлгоритм очень близкий.

Как Redis управляет данными о тепле

Когда мы говорили о строковых объектах ранее, мы упомянулиredisObjectобъект существует вlruАтрибуты:

typedef struct redisObject {
    unsigned type:4;//对象类型(4位=0.5字节)
    unsigned encoding:4;//编码(4位=0.5字节)
    unsigned lru:LRU_BITS;//记录对象最后一次被应用程序访问的时间(24位=3字节)
    int refcount;//引用计数。等于0时表示可以被垃圾回收(32位=4字节)
    void *ptr;//指向底层实际的数据存储结构,如:SDS等(8字节)
} robj;

lruСвойство создается при записи объекта, оно будет обновляться при доступе к объекту. Нормальная идея заключается в том, что окончательное решение о том, следует ли удалять ключ, должно быть вычтено из текущей временной метки.lru, тот, у которого разница больше, будет удален первым. ноRedisЭто не то, что он делает там,RedisГлобальное свойство поддерживается вlru_clock, это свойство передается через глобальную функциюserverCronлюбой другой100Он обновляется один раз в миллисекунду, и запись является текущей.unixметка времени.

Окончательное решение об удалении данных принимается черезlru_clockминус объектlruпроизводные от свойств. так почемуRedisСделать это? Не более ли точно брать глобальное время напрямую?

Это связано с тем, что это позволяет избежать обновления объекта каждый раз.lruКогда атрибут используется, глобальный атрибут может быть получен напрямую без вызова системной функции для получения системного времени, тем самым повышая эффективность (RedisЕсть много из этой детализации для повышения производительности, что можно сказать, что можно оптимизировать до максимально возможной).

Но тут есть проблема, мы видим,redisObjectв объектеlruтолько собственность24немного,24биты могут храниться только194Размер временной метки в днях после превышения194Через несколько дней он перезапустится с0начать считать, так что это время может появитьсяredisObjectв объектеlruСвойства больше, чем глобальныеlru_clockситуация с недвижимостью.

Из-за этого расчет также необходимо разделить на2кейс:

  • Когда глобалlruclock > lru, затем используйтеlruclock - lruПолучите свободное время.
  • когда глобальныйlruclock < lru, затем используйтеlruclock_max(который194небо) -lru + lruclockПолучите свободное время.

Следует отметить, что этот метод расчета не гарантирует, что самое длинное время простоя можно удалить из выборочных данных. Это потому, что в первую очередь194Дни используются пока редко, опять же толькоlruclockпервое2раунды продолжают превышатьlruАтрибуты, расчет будет проблемой.

например объектAзаписаноlruда1дней, покаlruclockВторой раунд10Боже, в это время результат расчета будет только10-1=9Боже, это должно быть на самом деле194+10-1=203небо. Однако такая ситуация, можно сказать, встречается реже, поэтому данный метод обработки может иметь неточные удаления, но сам по себе этот алгоритм является приблизительным алгоритмом, поэтому большого влияния он не окажет.

LFU-алгоритм

LFUПолное имя:Least Frequently Used. То есть: наименее частое использование в последнее время, это в основном для частоты использования. Это свойство также задокументировано вredisObjectсерединаlruСобственность внутри.

когда мы принимаемLFUПри стратегии утилизацииlruвысокий атрибут16Бит используется для записи времени доступа (время последнего уменьшения: ldt, в минутах), низкий8Биты используются для записи частоты доступа (логистический счетчик: LOGC), упомянутой кcounter.

Частота доступа увеличивается

LFUСчетчик только на ключ8бит, максимальное значение, которое он может представлять, равно255,такRedisреализуется с использованием вероятностного логарифмаcounter的递增。 р

Учитывая старую частоту доступа, при доступе к ключуcounterУвеличение следующим образом:

  1. извлекать0и1случайное число междуR.
  2. counter- начальное значение (по умолчанию5), получить базовую разницу, если разница меньше0, то сразу взять0, для удобства расчета эта разница записывается какbaseval.
  3. вероятностьPФормула расчета:1/(baseval * lfu_log_factor + 1).
  4. еслиR < P, приращение частоты (counter++).

в формулеlfu_log_factorназывается логарифмическим коэффициентом, значение по умолчанию равно10, которым можно управлять с помощью параметров:

lfu_log_factor 10

На следующем рисунке показан логарифмический коэффициентlfu_log_factorИ частотаcounterГрафик роста:

Видно, что при логарифмическом множителеlfu_log_factorза100когда, наверное10M(1000万)посещения будутcounterувеличить до255, а по умолчанию10также может поддерживать1M(100万)посещенияcounterдостигать255Верхний предел, достаточный для большинства сценариев.

снижение частоты

Если частота посещенияcounterПросто были повышения, так поздно, я получу все.255, ЭтоcounterОн увеличивается и не может полностью реагировать на одинkeyжара, поэтому, когда определеннаяkeyПосле периода бездействия,counterТакже требуется соответствующее сокращение.

counterСкорость снижения определяется параметромlfu-decay-timeуправления, по умолчанию1, в минутах. По умолчанию1Выражать:NНет доступа в минутах,counterвот-вот уменьшитсяN.

lfu-decay-time 1

Конкретный алгоритм выглядит следующим образом:

  1. Получите текущий отметку времени вминутаПринять низко после16бит (для удобства последующих вычислений это значение записывается какnow).
  2. удалить объектlruвысокие атрибуты16бит (для удобства последующих вычислений это значение записывается какldt).
  3. когдаlru > now, по умолчанию истек один цикл (16бит, макс.65535), то возьмите разницу65535-ldt+now:когдаlru <= now, возьми разницуnow-ldt(Для удобства последующих расчетов эта разница записывается какidle_time ).
  4. Выньте файл конфигурацииlfu_decay_timeзначение, затем рассчитать:idle_time / lfu_decay_time(Для удобства последующих расчетов это значение записывается какnum_periods).
  5. наконецcounterуменьшать:counter - num_periods.

Это выглядит так сложно, но формула расчета на самом деле состоит из одного предложения: выньте текущую временную метку иlruСравните атрибуты и подсчитайте, как долго к ним не обращались.Например, вычисленный результат100минут не было доступа до удаления параметра конфигурацииlfu_decay_timeЕсли эта конфигурация по умолчанию1это100/1=100,представлять100минут без доступа, поэтомуcounterпросто уменьшить100.

Суммировать

Эта статья в основном знакомитRedisСрок действия ключа политики обработки истек, и когда памяти сервера недостаточноRedisиз8стратегия устранения, и, наконец, введенаRedisДва основных алгоритма исключения вLRUиLFU.