O caminho para o armazenamento em cache - o princípio da cafeína

Como usar

Cache de cafeína refere-se à API do Guava Cache, e o uso é basicamente o mesmo.

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);
复制代码

Em relação a outros usos da cafeína, não vou entrar em detalhes. Vamos dar uma olhada nas vantagens da cafeína em comparação com outras estruturas de cache local. Alguns dos algoritmos e idéias podem nos trazer uma grande ajuda na programação.

Otimização da cafeína

Além de fornecer funções avançadas como o Guava Cache, a Caffeine usa um  algoritmo W-TinyLFU mais eficiente para a eliminação do cache  .
O algoritmo W-TinyLFU combina os algoritmos LRU e LFU. Vamos primeiro revisar esses dois algoritmos.

Algoritmo LRU

Conforme  mencionado no  LRUHashMap , o LRU (o menos usado recentemente) usa uma fila para armazenar itens de dados e cada vez que move os dados acessados ​​para o chefe da equipe e elimina diretamente os dados no final da equipe quando são eliminados. No entanto, operações esporádicas e periódicas em lotes farão com que a taxa de acertos da LRU caia acentuadamente.

Algoritmo LFU

LFU (menos usado com frequência). Sua idéia central é ** "Se um dado for usado algumas vezes no período mais recente, a possibilidade de ser usado em um período de tempo no futuro também será muito pequena", ** registrará o número de acesso a dados, quando precisar ser eliminado. Durante a operação, os dados com o menor número de acessos são eliminados.

A diferença entre o LFU e o algoritmo LRU é que a regra de eliminação do LRU é baseada no tempo de acesso, enquanto o LFU é baseado no número de visitas.
**
Ícone de eliminação do algoritmo LFU:

image.png


Desvantagens: o
algoritmo LFU realiza a eliminação do cache com base no número de vezes. Ainda usando dados quentes como exemplo, um dia uma estrela XXX foi descarrilada. A palavra XXX foi pesquisada 100.000 vezes. Após um mês, a popularidade passou e todos procuraram menos. Mas os dados relevantes do descarrilamento em estrela XXX ainda estão no cache; esses dados podem levar muito tempo para serem eliminados.
Além disso, o algoritmo LFU requer espaço de armazenamento extra para registrar o número de acessos e o consumo de armazenamento também é muito grande quando o volume de dados é muito grande.

Algoritmo W-TinyLFU

Estratégia de expiração

O algoritmo LFU precisa registrar adicionalmente o número de visitas. A maneira mais fácil é usar um hashmap grande para armazenar o número de visitas de cada dado. No entanto, quando a quantidade de dados é muito grande, o espaço ocupado pelo hashmap também é muito grande.
No W-TinyLFU, os dados entram primeiro no Window LRU. Depois de serem eliminados do Window LRU, entram no filtro para filtrar. Quando os novos dados forem maiores que os dados a serem despejados, esses dados serão aceitos pelo cache. O objetivo disso é principalmente acumular uma certa frequência de acesso para novos dados, para passar pelo filtro e entrar no segmento de cache subsequente.

W-TinyLFU usando o conde-Min Esboço algoritmo como o filtro, o algoritmo é o filtro de Bloom é uma variante de.

**
Aqui está uma breve revisão: a idéia do filtro Bloom é criar uma matriz (da mesma forma, também pode ser bytes), hash várias vezes para cada dado e, finalmente, definir a posição da matriz após o hash como 1 (matriz [ hash% length] = 1), os dados não são armazenados diretamente, para determinar se os dados podem ser duplicados. O algoritmo Count-Min Sketch também é semelhante: crie diferentes matrizes com base em diferentes algoritmos de hash, várias vezes para cada dado e adicione +1 à posição do índice de hash da matriz correspondente do algoritmo de hash. Quando a contagem é finalmente obtida, o menor valor em todas as matrizes pode ser obtido.

image.png

(Algoritmo de esboço Count-Min)

Na implementação da cafeína, uma matriz do tipo Long será criada primeiro, o tamanho da matriz é 2, o tamanho da matriz é o número de dados, se o tamanho do cache for 100, ele gerará um longo O tamanho da matriz é a potência de 2 mais próxima de 100, que é 128. Além disso, a Caffeine divide o tipo Long de 64 bits em 4 segmentos, cada segmento é de 16 bits e é usado para armazenar a contagem de frequências de acesso a dados correspondente a 4 algoritmos de hash.

LRU segmentada (SLRU)

Para retenção de dados a longo prazo, o W-TinyLFU usa uma estratégia de LRU segmentada. Inicialmente, um armazenamento de itens de dados é armazenado no segmento de avaliação (ProbationDeque) e, quando é acessado posteriormente, será promovido ao segmento protegido (ProtectedDeque) (o segmento protegido é responsável por 80% da capacidade total). Depois que a seção de proteção estiver cheia, alguns dados serão eliminados de volta para a seção de teste, que também poderá ser cascateada para acionar a eliminação da seção de teste. Esse mecanismo garante que dados quentes com pequenos intervalos de acesso sejam preservados e dados frios com poucas visitas repetidas sejam recuperados.

Otimização de leitura e gravação

O Guava Cache será misturado com as operações de eliminação de cache ao ler e gravar, portanto, perderá algum desempenho durante as operações de leitura e gravação. No Caffine, essas operações de eventos são assíncronas, ele os enviou à fila. Em seguida, o ForkJoinPool.commonPool () padrão ou configure você mesmo o conjunto de encadeamentos, execute a operação de extração de fila e execute as operações subsequentes de eliminação e expiração. Cada operação de leitura e gravação possui sua própria fila.

readBuffer

A fila de leitura usa o RingBuffer (referência: disruptor de alto desempenho, sem interrupção, que você deve conhecer ) Para reduzir ainda mais a simultaneidade da leitura, vários RingBuffers (buffer de anel listrado) são usados ​​para hash no RingBuffer correspondente por ID do encadeamento. Uma característica significativa do cache do anel é que ele não precisa executar o GC e substitui diretamente os dados expirados.
Quando um RingBuffer estiver cheio, ele acionará uma operação de execução assíncrona e as gravações subseqüentes no buffer de anel serão descartadas até que o buffer de anel possa ser usado; portanto, o readBuffer registra a transação do cache de leitura com perda. Porque ler o registro é otimizar a estratégia de direção, permitindo que ele perca.

writeBuffer

A fila de gravação usa a fila limitada tradicional ArrayQueue.

Ultimo

Finalmente, uma imagem é usada para descrever o processo desde a geração de dados até a eliminação na cafeína:

image.png

Acho que você gosta

Origin juejin.im/post/5e9c558e5188256bdf72c324
Recomendado
Clasificación