×Закрыть

Изобретение колеса, или как была разработана библиотека кэширования Ybc

Wheel image via Shutterstock.

Что такое ybc?

Ybc — это легковесная низкоуровневая библиотека на C, реализующая API для кэширования произвольных blob’ов. На текущий момент работает только под Linux, но может быть легко портирована на любую платформу благодаря тому, что весь платформозависимый код спрятан за платформонезависимым интерфейсом, реализация которого находится в строго отведенном месте.

С чего все началось?

Пару лет назад мне попалась на глаза статья Notes from the Architect, где автор программы Varnish рассказывает о том, как нужно писать современный софт под современные платформы (операционные системы + железо). После этого я прочел еще одну статью того же автора — You’re Doing It Wrong, где он рассказывает про то, насколько важно понимание locality of reference при создании высокопроизводительного кода под современные платформы. Для тех, кому влом читать оригиналы статей, привожу две основные мысли:

  • Современное железо напичкано кучей многоуровневых кэшей. Поэтому циклическое обращение к одной и той же области памяти может работать в 100500 раз быстрее, чем циклическое обращение к случайным областям памяти.
  • Память, с которой работают ваши программы на современных операционных системах, имеет мало общего с физической RAM. Скорость доступа по разным адресам этой памяти может отличаться в миллионы раз. Причем скорость доступа по одному и тому же адресу памяти в разные интервалы времени может также отличаться в миллионы раз.

Эти «сокровенные знания» могут сильно помочь при оптимизации алгоритмов по производительности. Для любителей матана есть замечательная серия статей — «What every programmer should know about memory», а также Cache-oblivious algroithm.

Начитавшись вышеупомянутой «ереси», я решил испытать свои силы. Вначале покопался в исходниках Varnish’а и обнаружил там следующие «фатальные недостатки»:

  • Добавление новой записи в переполненный кэш либо устаревание уже существующей записи может приводить к большому количеству операций ввода-вывода по случайным адресам памяти. Как известно любителям файлов подкачки на HDD, такие операции могут сильно тормозить.
  • Обращение к существующей записи также может приводить к нескольким операциям ввода-вывода, если все записи в кэше не умещаются в RAM.
  • Реализация кэша в Varnish была подвержена DoS-атаке на хэш-коллизии.
  • При крэше Varnish’а все закэшированные записи терялись.
  • Код написан не мной.

Для «решения» этих недостатков вначале были написано несколько патчей для Varnish, затем была созадана реализация B-heap и D-heap в одном флаконе под названием gheap. Затем было решено, что лучше создать собственную реализацию кэша, лишенную вышеупомянутых недостатков. Так зародилась ybc.

Дизайн ybc

Отслеживание и удаление записей из кэша в Varnish’а реализовано с помощью двух механизмов, присутствующих в большинстве классических реализаций кэша:

  • LRU-списка, в котором хранятся указатели на все записи, отсортированные по времени последнего обращения к этим записям. При каждом обращении к записи ее указатель переносится в начало LRU-списка. При переполнении кэша Varnish проходится по этому списку с конца, удаляя записи, к которым очень давно не было обращений.
  • Heap’а, в котором хранятся указатели на все записи, упорядоченные по expiration time. В «голове» heap’а всегда находится запись, которая должна устареть раньше всех. При каждом добавлении новой записи ее указатель заносится в heap. Периодически Varnish удаляет устаревшие записи с помощью этого heap’а.

Как можно видеть, основной недостаток классической реализации кэша — повышенного количество обращений к памяти по произвольным адресам, которое выливается в операции ввода-вывода при больших объемах данных.
Главной целью при проектировании ybc было избавление от этого недостатка. Эта цель была достигнута с помощью радикального способа — полного отказа от LRU с heap’ом в пользу «принципиально нового» механизма — циклического буфера наподобие того, который используется в Log-structured filesystem. Кэшируемые данные записываются по очереди в циклический буфер. При переполнении буфера старые данные затираются новыми. При этом структура кэша реализована таким образом, что отслеживать затираемые записи или инициализировать циклический буфер при создании нет необходимости. При попытке обращения к затертой или поврежденной записи ybc легко обнаружит это и вернет результат «запись не найдена». Хранение данных в виде циклического буфера, спроецированного в mmap’ed файл, дало следующие «бесплатные» фичи:

  • записи не теряются при крэше или принудительном рестарте программы, использующей библиотеку ybc.
  • Размер кэша может превышать объем физической RAM в сотни раз.
  • Закэшированные данные никогда не попадут в файл подкачки. Это хорошо с точки зрения безопасности и производительности — при нехвтке памяти операционная система просто «забудет» про cold cache страницы памяти из за-mmap’ленного файла вместо того, чтобы выгружать их в файл подкачки.
  • Не нужно городить огород с балансировкой закэшированных данных между оперативной памятью и файлом — об этом позаботится механизм виртуальной памяти в операционной системе.

Каким же образом ybc находит необходимую запись по ключу? Для этого служит модифицированная open addressing хэш-таблица, адаптированная под нужды кэша. Модификация заключается в том, что при переполнении хэш-таблицы ybc может спокойно затирать старые записи новыми, не беспокоясь о memory leak’ах. Также модифицированная хэш-таблица «бесплатно» обеспечила защиту от DoS-атаки на хэш-коллизии вследствие ограниченного размера bucket’ов.

Подсчитаем количество необходимых обращений к произвольным участкам памяти (которые, как известно, на больших объемах данных превращаются в очень медленные операции ввода-вывода) при чтении чтении и записи для классической реализации кэша и для кэша ybc.

  • Чтение
    • Для классического кэша — минимум две при кэш-промахе (одна для обращения к хэш-бакету по ключу, последующие — для просмотра каждой записи в хэш-бакете), минимум пять при кэш-попадании (плюс две для переноса указателя в начало LRU-списка и одна — для чтения закэшированных данных).
    • Для ybc — одна при кэш-промахе (для обнаружения факта того, что в хэш-таблице отсутствует соответствующая запись), две при кэш-попадании — одна для обращения в хэш-таблицу, вторая — для чтения данных из циклического буфера.
  • Запись в переполненный кэш
    • Для классического хэша — много (динамическое выделение памяти для данных и для хэш-записи, вставка указателя на данные в хэш-бакет, произвольное количество обращений к элементам LRU-списка для освобождения необходимой памяти под новую запись, произвольное количество обращений к элементам expiration heap’а для удаления устаревших записей, произвольное количество вызовов функции освобождения динамически выделенной памяти под удаляемые записи и вспомогательные элементы в LRU-списке, expiration heap’е и хэш-таблице).
    • Для ybc — одна (для добавления записи в хэш-таблицу). Обращение к памяти при добавлении данных в циклический буфер не учитывается, т.к. эта область памяти с большой долей вероятности будет находиться в кэшах процессора, если недавно другой элемент был записан в кэш.

Полезные и не очень оптимизации, используемые в ybc

В ybc также было применено множество мелких оптимизаций, нацеленных на минимизацию working set size. Вот некоторые из них:

  • Мелкие объекты, размер которых не превышает размера страницы памяти, могут перемещаться с определенной долей вероятности в начало циклического буфера. Это необходимо для того, чтобы «запаковать» мелкие часто запрашиваемые объекты в минимальную область памяти. Также это позволяет продлить время жизни таких объектов — ведь при переполнении циклического буфера они не будут затерты.
  • Кэш хэш-таблицы для хранения часто запрашиваемых записей. При обнаружении записи в этом кэше удается избежать потенциально медленного обращения в основную хэш-таблицу, которая может не умещаться в RAM.
  • Минимизация динамического выделения памяти. Обычно алгоритмы динамического выделения памяти приводят к произвольному количеству обращений по произвольным адресам памяти. В ybc динамическое выделение памяти используется только в одном очень специфическом месте — для реализации dogpile effect handling.
  • «Упаковка» структур друг в друга вместо повсеместно используемой практики — использования указателей на вложенные структуры. Это минимизирует количество разыменований указателей (которые могут быть медленными на определенных платформах) при работе с данными структурами.
  • Минимизация копирования больших объемов данных из одной области памяти в другую. Ybc API реализовано таким образом, чтобы пользователи могли избегать большинства операций копирования данных. Это может дать существенный выигрыш в скорости при работе с большими blob’ами наподобие многогигабайтных фильмов.
  • Использование skiplist-ов для ускорения отслеживания, какие записи в данный момент читаются/пишутся. Данное отслеживание необходимо для того, чтобы при переполнении цилклического буфера не произошло нарушения целостности читающейся/пишущейся в данный момент записи. Skiplist’ы оказались наилучшим решением, позволившим избежать динамического выделения памяти в наиболее часто исполняемом коде.
  • Минимизация количества и длительности блокировок в часто исполняемом коде. Это необходимо для того, чтобы несколько процессоров могли параллельно выполнять полезную работу, а не ожидать, пока другие процессоры освободят блокировку. В ybc даже есть специальный режим, отключающий все блокировки. Он позволяет линейно наращивать производительность ybc для операций чтения путем увеличения количества задействованных CPU в системе. Но за скорость нужно платить — в этом режиме сильно возрастает нагрузка на CPU для больших blob’ов, поэтому он подходит только для работы со сравнительно маленькими записями.

Что дальше?

После завершения основных работ над ybc я начал думать, где бы его применить. Первое, что пришло на ум, — реализация memcached сервера. Программировать что-нибудь сложнее hello world на C мне не очень хотелось, поэтому решил создать binding’и для ybc на каком-нибудь более дружелюбном для application programming языке. Выбор пал на Go. В итоге на Go с помощью ybc были реализованы два PoC-приложения:

  • go-memcached — memcached сервер с «фишками» вроде cache persistence, произвольного объема кэшируемых данных (может превышать объем RAM), произвольного размера отдельно кэшируемых записей.
  • go-cdn-booster — простой кэширующий http-прокси для статического контента. В отличие от nginx, хранит весь закэшированный контент в двух, а не в миллионе файлов. Также не нуждается в cleaner-процессе, ответственным за удаление файлов с устаревшим контентом.

В планах есть желание использовать ybc для создания squid killer’а — прозрачного кэширующего http-прокси с возможностью кэширования range request’ов от основных видеохостингов вроде ютуба и иксхамстера. Такой прокси мог бы экономить входящий трафик для интернет-провайдеров, а также отдавать юзерам закэшированный контент на скорости 1Гбит/с. Пока до него не дошли руки.

Заключение

Несколько советов:

  • Изучайте детали реализации современных платформ (железо, операционная система, программное окружение). Это позволит вам писать более эффективный код на любом языке программирования.
  • Пишите больше разного кода (желательно применимого на практике) на разных языках программирования, не стесняйтесь изобретать «велосипеды». Это позволит вам делать осознанный выбор в пользу программных архитектур и языков программирования, ориентированных на практическое, а не теоретическое применение.

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

  1. Портирование ybc на другие платформы.
  2. Создание binding’ов для ваших любимых языков программирования.
  3. Создание высокоуровневых API, более удобных для использования по сравнению с API ybc.
  4. Написание различных приложений, использующих ybc.

С удовольствием отвечу на любые ваши вопросы к комментариям к этой статье.

👍НравитсяПонравилось0
В избранноеВ избранном0
Подписаться на автора
LinkedIn

Похожие статьи




Підписуйтесь: SoundCloud | Google Podcast | YouTube

18 комментариев

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

Как насчет кеширования небольших файлов 100Kb-2Mb через ybc или linux сам знает какие файлы закешировать в ram? И ybc здесь будет оверхедом?

ybc изначально проектировался для кэширования большого количества файлов, так что можете спокойно использовать его для кэширования файлов изображений.

Линукс, как и другая современная ОС, умеет сам кэшировать содержимое часто запрашиваемых файлов. Но если полагаться на файлы, то придется иметь дело с файловой системой, работа с которой вносит дополнительные задержки и усложняет код. Если кэшировать большое количество файлов, то столкнетесь с возможным замедлением доступа к этим файлам, если они будут храниться в одном каталоге. Также нужно будет самим реализовывать систему удаления устаревших файлов и ограничения суммарного объема кэшируемых файлов. Все это уже реализовано в ybc.

Еще раз прочитал ваш вопрос. Т.е. вы хотите использовать ybc для кэширования содержимого файлов, хранящися на той же файловой системе, где находятся файлы ybc? В этом особого смысла нет — это лишь приведет к увеличению использования дискового пространства в два раза и не даст существенного выигрыша в производительности.

ybc может увеличить производительность в данной конфигурации, если кэшируемые файлы находятся на другой, медленной файловой системе (например, nfs, cifs или WebDAV), а файлы ybc находятся на локальной быстрой файловой системе.

Да об этом и задавался вопрос, а вот по поводу кеширования файлов на распределенных файловых системах — интересная идея )

Не претендуя на экспертную точку зрения, и совершенно не умаляя заслуг автора, добавлю свои «5 копеек» об mmap.

Аналогичный метод дискового кеширования «для бедных» (mmap-file), используется в совсем не бедной MongoDB.

В результате «всплывают» неприятности:

— Latency доступа данным очень неконсистентна и прыгает от 4-5мс до 700мс.

— Для более или менее predictable времени ответа, весь датасет требует весьма тяжелого warm-up, а также желательно garbage collection процесса на уровне логики самой БД

— Перекладывание IO-операций посредством mmap-вызова на ОС — это как «стрельба из пушки по воробьям». В моем опыте с Linux, на датасетах 30-60Gb+, IO-операции попросту зашкаливают «убивая» производительность системы. (Иногда очень забавно наблюдать как на stand-by базе половина ресурсов современного сервера занято на mmap-syscall)

Когда в прошлом году, на какой-то очередной NoSQL-конференции мы спросили докладчиков из 10gen что-то вроде:

«Чуваки, ну как же так, вот вы взяли и переложили весь IO-процесс на mmap+ОС, а вон ребята из MySQL, Percona, Oracle, TokuTek годами парятся чтоб оптимизировать работу с дисковым IO»

в ответ получили:

«Нее, ну это же крутые ребята, у них там теория, реальные алгоритмы, оптимизации, сборы статистик, „умный“ read-ahead, вообщем hardcore — а нам все это не надо — у нас дешевый горизонтальный scaling, потому если что — серверов на амазоне докупите и не парьтесь».

:)

ну тонкости работы mmap() понимает не так много людей. Вот например лично до меня очччень долго доходило что page-fault существенно дороже чем тик шедуллера + переключение контекстов.

Хороший комментарий.

Есть пара замечаний

Причина «тормозов» некоторых современных систем связана не с использованием mmap’а, а с уделением недостаточного внимания минимизации количества обращений к произвольным участкам памяти в условиях, когда часто запрашиваемые данные не помещаются в RAM. Если часто запрашиваемые данные хранились не в mmap’ленном файле, а в heap’е, то в условиях нехватки памяти они бы выгружались бы в файл подкачки, что приводило бы к еще большим тормозам. Ведь в случае mmap-файла операционная система может просто «забыть» о странице, замэпленной на файл, если содержимое этой страницы не менялось с момента последнего доступа к ней. В случае данных на heap’е операционная система вынуждена выгружать все страницы с данными в файл подкачки.

В ybc количество обращений к произвольным участкам памяти мининизировано на нескольких уровнях — об этом написано в статье. Поэтому в случае, когда страницы с часто запрашиваемыми данными не помещаются в RAM, ybc будет тормозить меньше по сравнению с другими системами, где этому вопросу не уделялось должного внимания.

Latency доступа данным очень неконсистентна и прыгает от 4-5мс до 700мс.

Время доступа к данным может прыгать и на системах, хранящих все данные в оперативной памяти (например, memcached), если они явно не указали операционной системе, что страницы с данными не могут быть выгружены в файл подкачки. Также время доступа к редко запрашиваемым данным будет прыгать на любой БД, если эти данные не находились в RAM при попытке обращения к ним.

Для более или менее predictable времени ответа, весь датасет требует весьма тяжелого warm-up, а также желательно garbage collection процесса на уровне логики самой БД

Warm-up нужен в любой системе, чтобы гарантировать консистентность времени доступа к часто запрашиваемым данным (т.е. чтобы они были закэшированы в RAM).
Garbage collection в ybc не нужен благодаря его архитектуре. Также он избавлен от проблем фрагментации данных в памяти типа slab calcification, свойственным традиционным системам кэширования типа memcached.

— Перекладывание IO-операций посредством mmap-вызова на ОС — это как «стрельба из пушки по воробьям». В моем опыте с Linux, на датасетах 30-60Gb+, IO-операции попросту зашкаливают «убивая» производительность системы. (Иногда очень забавно наблюдать как на stand-by базе половина ресурсов современного сервера занято на mmap-syscall)

Напротив, mmap позволяет избежать создания кучи вспомогательных алгоритмов управления памятью, тем самым упрощая систему.
Тормоза происходят не из-за mmap’а, а из-за того, что dataset не помещается в оперативную память. В этом случае любая система будет тормозить. Просто некоторые (где не уделяли внимания на количество обращений к произвольным участкам памяти) будут тормозить больше, а некоторые (типа ybc) — меньше.

Также забыл упомянуть в статье, что при разработки ybc я придерживался принципа KISS, стараясь использовать наиболее простые решения (иногда в ущерб полезным и не очень «фичам»).

На самом деле очень интересно. Поддерживаю, что классно заниматься PoC exploratory programming и велосипедизмом. А то когда думаешь о текущем рынке, «какую проблему оно решает» (всегда можно накидать альтернативных решений в конкретных примерах использования), то умирает программирование в чистом виде. По итогу может быть это будет положено «в стол», но понимание и опыт, и кайф от разработки получены. А может и не положено в стол, редис ведь изначально точно такой же велосипед от Сальватора.

Бенчмарки нужны в контексте конкретных приложений, а не просто так. Так что пока на них можно забить. Возможно, заменять мемкешд и редис этим везде и не стоит, но зато когда нужно будет оптимизировать узкое место, есть отличный набор велосипедиков.

ПС. на самом деле есть чуйка, что отказ от LRU будет вылезать боком, в зависимости от характера задачи кеширования, по причине исчезнование в одночасье, как пишет Алексей Васильев, и это может свети на нет все оптимизации в каком-то приложении. Можно баловаться разными хеш-функциями, конечно.

Мені здається, чи автор забув про тести продуктивності і порівняння їх із існуючими конкурентами?

Не совсем — просто не упомянул про это в статье. Тесты производительности есть:
— низкоуровневые ( make perftests ) - тестируют скорость основных функций библиотеки ybc.
— среднего уровня ( make go-perftests-bindings ) - тестируют скорость binding’ов ybc на Go.
— уровня приложений ( make go-perftests-memcache ) - тестируют скорость клиент-серверной связки реализации memcached .

Для сравнения производительности с конкурентами написаны два бенчмарка:
— go-memcached-bench — может быть использован для тестирования производительности различных memcached-серверов.
— go-cdn-booster-bench — аналог апачевского ab. Может быть использован для тестирования производительности http-серверов и проксей.

А результати?

  • Низкоуровневые тесты производительности на моем компе с 4-х ядерным процессором Intel® Core™ i5-2450M ( make perftests ):
    • Один поток:
      — 8.8М обращений по несуществующим ключам в секунду (кэш-промахи).
      — 6.5М обращений по существующим ключам (плюс чтение данных) в секунду (кэш-попадания).
      — 3.2М операций записи в секунду.
    • 16 потоков:
      — 25М кэш-промахов в секунду
      — 5.3М кэш-попаданий в секнду в обычном режиме (тут производительность упирается в глобальную блокировку, используемую для защиты от перезаписи читаемых данных). Если вместо этой защиты при каждом обращении к данным копировать их из кэша и после этого проверять их целостность, то скорость возрастает до 24М кэш-попаданий в секунду.
      — 1.4М операций записи в секунду (аналогично, скорость упирается в ту же блокировку). При отключении блокировки скорость возрастает до 5М операций записи в секунду.
  • Тесты производительности binding’ов для Go ( make go-perftests-bindings ):
    • Один поток:
      — 3.3М кэш-промахов в секунду
      — 1.9М кэш-попаданий в секнду
      — 2.5М операций записи в секунду
    • 16 потоков:
      — 3.5М кэш-промахов в секунду при включенной глобальной блокировке в ybc и 6М кэш-промахов в секнду при отключенной глобальной блокировке.
      — 2М кэш-попаданий в секнду при включенной блокировке и 4М кэш-попаданий в секунду при отключенной блокировке.
      — 1.8М операций записи при включенной блокировке и 4М операций записи в секунду при отключенной блокировке.
    Очевидно, что в Go производительность упирается в какую-то глобальную блокировку в текущей реализации binding’ов для C-функций.
  • Тесты производительности взаимодействия клиента с сервером по протоколу memcached ( make go-perftests-memcache ). При интерпретации этих тестов следует учитывать, что клиент и сервер находятся на одном компе и общаются через локальные TCP-соединения.
    • Один поток:
      — 10К кэш-попаданий в секунду при запросах по одному ключу на один запрос.
      — 60К кэш-попаданий в секунду при batch-запросах по 16 ключей на один запрос.
      — 12К блокирующих операций записи в секунду.
      — 750К неблокирующих операций записи в секунду.
    • 128 потоков (на самом деле это не совсем потоки, а goroutine’ы).
      — 300К кэш-попаданий в секунду для запросов по одному ключу на запрос.
      — 450К блокирующих операций записи в секунду.
  • Сравнительные тесты производительности оригинального memcached-сервера с memcached сервером на основе ybc с помощью go-memcached-bench (при условии использования клиента, оптимизированного на отправку максимального количества запросов по каждому TCP-соединению — см. github.com/...cache/client.go ):
    • Кэш-промахи (workerMode=GetMiss): 170К vs 440К запросов в секунду
    • Кэш-попадания (workerMode=GetHit): 100K vs 200К запросов в секунду
    • Операции записи (workerMode=Set): 260K vs 350K запросов в секунду
    • Одновременно: 10% операций записи и 90% операций чтения (workerMode=GetSet): 110K vs 205K запросов в секунду

За порівняльні тести — дякую

Добрый день. Возникло пару воросов:

1) Как управлять размером буфера? Его можно задать через конфигурацию?
2) Я так понял из-за циклического буфера ключи с TTL могут исчезать из кеша раньше назначеного времени? То есть никакой гарантии нет, что записаное в кеш там не уберется в одночасье, поскольку други процессы уже написали еще данных в кеш?

Еще бы хорошо увидеть сравнение (бэнчмарк таблицы) с memcached.org, redis.io и gibson-db.in (последний сжимает хранимые данные и использует дерево вместо хешей — хеш-таблицы на больших объемах страдают или от коллизий или от не эффективности использования памяти), что бы понять, «стоит ли игра свеч». Особенно интересно увидеть результаты, когда данные влазят в RAM, и когда нет.

1) Как управлять размером буфера? Его можно задать через конфигурацию?

Размер буфера (файла с данными) задается через конфигурацию. Вернее, там два файла — один для циклического буфера, а второй — для индекса (хэш-таблицы). Размеры этих файлов можно задавать при создании/открытии кэша.

2) Я так понял из-за циклического буфера ключи с TTL могут исчезать из кеша раньше назначеного времени? То есть никакой гарантии нет, что записаное в кеш там не уберется в одночасье, поскольку други процессы уже написали еще данных в кеш?

Да, правильно поняли. Это же кэш, а не хранилище данных. Никакой кэш не может дать гарантии на то, что данные там будут храниться вплоть до окончания TTL. Иначе его размер был бы неконтролируемым.

Еще бы хорошо увидеть сравнение (бэнчмарк таблицы) с memcached.org, redis.io и gibson-db.in (последний сжимает хранимые данные и использует дерево вместо хешей — хеш-таблицы на больших объемах страдают или от коллизий или от не эффективности использования памяти), что бы понять, «стоит ли игра свеч». Особенно интересно увидеть результаты, когда данные влазят в RAM, и когда нет.

Постараюсь провести сравнительные тесты. Пока можете сами попробовать протестировать производительность разных решений, поддерживающих протокол memcached, с помощью go-memcached-bench.

Да, правильно поняли. Это же кэш, а не хранилище данных. Никакой кэш не может дать гарантии на то, что данные там будут храниться вплоть до окончания TTL. Иначе его размер был бы неконтролируемым.

Тут я с Вами не согласен. Если мне нужен кеш (кусок данных для быстрого доступа), то я ожидаю, что он будет существовать «расчетное» мной время. Размер кеша — эта отдельная тема (для таких случаев отказываются от хеш-таблиц и добавляют сжатие из коробки). Система, которая не может это гарантировать тяжело рассматривать как кеш систему.

ybc имеет право на жизнь, но мне тяжело придумать применение его в реальном проекте. Тот же redis гарантирует, что данные будут хранится в кеше до наступления TTL (+/- 1 миллисекунда).

Redis является не кэшем, а хранилищем данных, поэтому он гарантирует сохранность данных до окончания их TTL, в отличие от систем кэширования вроде memcached и его аналогов. В этом свойстве redis можно найти как преимущества (данные всегда доступны до окончания TTL), так и недостатки (размер записанных в redis данных может превысить объем оперативной памяти, что приведет к жутким тормозам, т.к. при гарантии на сохранность данных максимальный объем закэшированных данных нельзя никак ограничить).

Подписаться на комментарии