Дорога к кэшированию — принцип Caffeine

Java

как пользоваться

Caffeine Cache относится к API Guava Cache, и его использование в основном такое же.

Cache<String, Object> cache = Caffeine.newBuilder()
            .expireAfterWrite(30, TimeUnit.MINUTES)
            .maximumSize(1000)
            .build();

// cache.put("key1", "val");

Object val = cache.get("key1", key -> {
     return key + "_new";
});

System.out.println(val);

Что касается других применений Caffeine, я не буду их повторять. Давайте в основном рассмотрим преимущества Caffeine по сравнению с другими фреймворками локального кэширования. Некоторые алгоритмы и идеи могут нам сильно помочь в программировании.

Оптимизация кофеина

В дополнение к тем же богатым функциям, что и Guava Cache, Caffeine использует более эффективное устранение кеша.W-TinyLFUалгоритм.
Алгоритм W-TinyLFU объединяет алгоритмы LRU и LFU. Давайте сначала рассмотрим эти два алгоритма.

Алгоритм LRU

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

LFU-алгоритм

LFU (наименее часто используемый). Его основная идея заключается в«Если часть данных использовалась очень мало в последний период, вряд ли она будет использоваться в будущем»,Будет записано количество обращений к данным, и когда потребуется операция исключения, данные с наименьшим количеством посещений будут удалены.

Разница между алгоритмами LFU и LRU заключается в том, что правило исключения LRU основано на времени доступа, а LFU основано на количестве доступов.

Схема устранения алгоритма LFU:

image.png


недостаток:
Алгоритм LFU выполняет удаление кеша в зависимости от количества раз. Давайте возьмем в качестве примера горячие данные. Однажды звезда XXX обманула. Слово XXX искали 100 000 раз. Данные, связанные со сходом знаменитости с рельсов, все еще находятся в кеше, и удаление этих данных может занять много времени.
Кроме того, алгоритм LFU требует дополнительного места для записи количества обращений, а потребление памяти также очень велико, когда объем данных очень велик.

Алгоритм W-TinyLFU

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

Алгоритму LFU необходимо дополнительно записывать количество обращений.Проще всего использовать большую хеш-карту для хранения количества обращений к каждому данным, но когда количество данных очень велико, пространство, занимаемое хэш-картой, также очень большой.
В W-TinyLFU данные сначала поступают в Window LRU. После исключения из Window LRU они попадают в фильтр для фильтрации. Когда новые данные выше, чем данные, которые нужно исключить, данные будут приняты cache. , цель этого в основном состоит в том, чтобы заставить новые данные накапливать определенную частоту доступа, чтобы они могли пройти через фильтр и попасть в следующие сегменты кеша.

Использование W-TinyLFUCount-Min Sketchалгоритм как фильтр, алгоритмФильтр Блумавариант .


Вот краткий обзор.Идея фильтра Блума состоит в том, чтобы создать массив (аналогично, это может быть и байт), выполнить несколько хэшей для каждых данных и, наконец, установить позицию хешированного массива в 1 (array[hash% length] = 1) и не хранит данные напрямую, чтобы определить, могут ли данные повторяться. Алгоритм Count-Min Sketch также аналогичен: разные массивы создаются по разным алгоритмам хэширования, и каждые данные хешируются несколько раз, а к позиции хэш-индекса соответствующего массива алгоритма хеширования добавляется +1. алгоритм конфликтует, то при подсчете в конце просто берите наименьшее значение во всех массивах.

image.png

(алгоритм эскиза Count-Min)

В реализации Caffeine сначала будет создан массив типа Long. Размер массива равен 2, а размер массива равен количеству данных. Если размер вашего кеша равен 100, он сгенерирует длинный массив, чей размер ближе всего к 100. Число, являющееся степенью 2, то есть 128. Кроме того, Caffeine делит 64-битный тип Long на 4 сегмента, каждый сегмент составляет 16 бит, которые используются для хранения подсчета частоты доступа к данным, соответствующего 4 алгоритмам хеширования.

Сегментированный LRU (SLRU)

Для данных длительного хранения W-TinyLFU использует стратегию сегментированного LRU. Первоначально элемент данных хранится в пробационном сегменте (ProbationDeque), а при последующем доступе он будет перемещен в защищенный сегмент (ProtectedDeque) (на защищенный сегмент приходится 80% общей емкости). После того, как защитный сегмент заполнен, некоторые данные будут удалены обратно в пробный сегмент, что также может вызвать каскадное удаление пробного сегмента. Этот механизм обеспечивает сохранение «горячих» данных с малым интервалом доступа, а «холодные» данные с небольшим числом повторных обращений повторно используются.

Чтение и запись оптимизации

Операции чтения и записи кэша Guava смешиваются с операциями устранения кэша, поэтому часть производительности будет потрачена впустую во время операций чтения и записи. В Caffine эти операции с событиями асинхронны, и он отправляет эти события в очередь. Затем он будет использовать ForkJoinPool.commonPool() по умолчанию или самостоятельно настроить пул потоков, выполнить операцию извлечения из очереди, а затем выполнить последующие операции исключения и истечения срока действия. Каждая операция чтения и записи имеет свою собственную очередь.

readBuffer

Очередь чтения использует RingBuffer (ссылка:Высокопроизводительный деструктор очередей без блокировки, о котором вы должны знать), чтобы еще больше уменьшить параллелизм чтения, используются несколько кольцевых буферов (полосатых кольцевых буферов), а идентификатор потока хэшируется в соответствующий кольцевой буфер. Примечательной особенностью кольцевого кэша является то, что он не требует сборки мусора и напрямую перезаписывает просроченные данные.
Когда RingBuffer заполнен, будет запущено асинхронное выполнение, и последующие записи в кольцевой буфер будут отброшены до тех пор, пока кольцевой буфер не будет использован, поэтому транзакции чтения буфера записей readBuffer будут с потерями. Потому что чтение записи оптимизирует стратегию привода, позволяя ему быть с потерями.

writeBuffer

Очередь записи использует традиционную ограниченную очередь ArrayQueue.

Наконец

Наконец, изображение используется для описания процесса данных в кофеине от генерации до устранения:

image.png