Análisis en profundidad del caché del futuro: cafeína

1. Introducción

Antes de leer este artículo, espero que pueda leerlo detenidamente: ¿Qué debe saber sobre la evolución del almacenamiento en caché y cómo utilizarlo con elegancia? . Estos dos artículos presentan principalmente cómo usar el caché de algún combate real. En estos dos artículos, recomiendo Caffeine como caché local para reemplazar su Guava Cache. En este artículo, presentaré las funciones específicas del caché de cafeína y los principios de implementación interna, para que todos sepan qué está sucediendo y por qué. Algunas personas preguntarán: no uso cafeína. Este artículo no debería ser de utilidad para mí. No te preocupes, el conocimiento de cafeína definitivamente te ayudará mucho en el diseño de otros códigos. Por supuesto, antes de la introducción, todavía tengo que publicar algunas fotos de comparación entre él y otros cachés:
Análisis en profundidad del caché del futuro: cafeína
se puede ver que la cafeína es básicamente más alta que otros cachés en todas las dimensiones. No es mucha tontería, primero veamos cómo usarlo.

1.1 Cómo utilizar

La cafeína es relativamente fácil de usar y la API es compatible con Guava Cache:

public

static

void
 main
(
String
[]
 args
)

{

Cache
<
String
,

String
>
 cache 
=

Caffeine
.
newBuilder
()

.
expireAfterWrite
(
1
,

TimeUnit
.
SECONDS
)

.
expireAfterAccess
(
1
,
TimeUnit
.
SECONDS
)

.
maximumSize
(
10
)

.
build
();
        cache
.
put
(
"hello"
,
"hello"
);

}

2. Introducción al principio de la cafeína

2.1W-TinyLFU

El LFU tradicional se ve muy afectado por el período de tiempo. Entonces, han aparecido varias variantes de LFU, basadas en el período de tiempo para decaer, o la frecuencia en un cierto período de tiempo. El mismo LFU también usa espacio adicional para registrar la frecuencia de cada acceso a los datos, incluso si los datos no están en la caché, deben registrarse, por lo que el espacio adicional que debe mantenerse es grande.

Imagine que creamos un hashMap para este espacio de mantenimiento, y cada elemento de datos se almacenará en este hashMap. Cuando la cantidad de datos es particularmente grande, el hashMap será particularmente grande.

Volviendo a LRU, nuestro LRU no es tan inútil, LRU puede lidiar muy bien con situaciones de tráfico repentino porque no necesita acumular frecuencia de datos.

Entonces, W-TinyLFU combina LRU y LFU, así como algunas características de otros algoritmos.

2.1.1 Registro de frecuencia

Lo primero que hay que hablar es el problema de la grabación de frecuencias, el objetivo que queremos conseguir es utilizar un espacio limitado para registrar la frecuencia de acceso que cambia con el tiempo. Usamos Count-Min Sketch para registrar nuestra frecuencia de visitas en W-TinyLFU, y esto también es una variante del filtro Bloom. Como se muestra abajo:
Análisis en profundidad del caché del futuro: cafeína

Si necesitamos registrar un valor, necesitamos hash a través de múltiples algoritmos hash y luego agregar +1 al registro del algoritmo hash correspondiente. ¿Por qué necesitamos múltiples algoritmos hash? Dado que se trata de un algoritmo de compresión, habrá conflictos. Por ejemplo, creamos una matriz Long y calculamos la posición hash de cada dato. Por ejemplo, Zhang San y Li Si, ambos pueden tener el mismo valor hash. Por ejemplo, si ambos son 1, la posición de Long [1] aumentará la frecuencia correspondiente. Zhang San visita 10,000 veces y Li Si visita 1 vez, y Long [ 1] Esta ubicación es 10: 1. Si toma la tasa de entrevista de Li Si, se sacará como 101, pero Li Si solo visitó una vez. Para resolver este problema, usamos múltiples El algoritmo hash se puede entender como un concepto de una matriz bidimensional larga [] []. Por ejemplo, en el primer algoritmo, Zhang San y Li Si entran en conflicto, pero en el segundo y tercer algoritmos, hay una alta probabilidad de que no entren en conflicto, como una El algoritmo tiene una probabilidad de colisión de alrededor del 1% y la probabilidad de que los cuatro algoritmos choquen entre sí es del 1% elevado a la cuarta potencia. A través de este modelo, cuando tomamos la tasa de acceso de Li Si, tomamos el número de veces que Li Si tiene la frecuencia más baja entre todos los algoritmos. Entonces su nombre es Count-Min Sketch.
Análisis en profundidad del caché del futuro: cafeína
Análisis en profundidad del caché del futuro: cafeína

Aquí hay una comparación con el anterior. Un ejemplo simple: si un hashMap registra esta frecuencia, si tengo 100 datos, entonces este HashMap debe almacenar 100 frecuencias de acceso de estos datos. Incluso si la capacidad de mi caché es 1, debido a las reglas de Lfu, debo registrar la frecuencia de acceso de los 100 datos. Si hay más datos, grabaré más.

En Count-Min Sketch, permítanme hablar directamente sobre la implementación en cafeína (en la clase FrequencySketch). Si su tamaño de caché es 100, generará una matriz larga cuyo tamaño es la potencia más cercana de 2 a 100. , Que es 128. Y esta matriz registrará nuestra frecuencia de acceso. En la cafeína, la frecuencia máxima es 15 y el bit binario de 15 es 1111, que son 4 bits en total, mientras que el tipo Long es de 64 bits. Entonces, cada tipo Long puede poner 16 algoritmos, pero la cafeína no hace esto. Solo usa cuatro algoritmos hash. Cada tipo Long se divide en cuatro secciones, y cada sección almacena las frecuencias de los cuatro algoritmos. La ventaja de esto es que puede reducir aún más los conflictos de Hash. El hash original de tamaño 128 se convierte en 128X4.

La estructura de un Long es la siguiente:
Análisis en profundidad del caché del futuro: cafeína
Nuestros 4 segmentos se dividen en A, B, C y D, que los llamaré más adelante. Llamo a los cuatro algoritmos en cada segmento s1, s2, s3 y s4. Aquí tiene un ejemplo: ¿Qué debo hacer si quiero agregar una frecuencia digital para acceder a 50? Usamos size = 100 como ejemplo aquí.

  1. Primero, determine en qué segmento está el hash de 50. A través del hash & 3 (el valor binario de 3 es 11), se deben obtener números menores que 4. Si hash & 3 = 0, entonces está en el segmento A.
  2. Utilice otros algoritmos hash para hacer otro hash en el hash de 50 para obtener la posición de la matriz larga, que es la posición en la matriz de longitud 128. Suponga que el algoritmo s1 se usa para obtener 1, el algoritmo s2 es 3, el algoritmo s3 es 4 y el algoritmo s4 es 0.
  3. Debido a que el algoritmo S1 obtiene 1, agregue +1 en la posición s1 en la sección A de long [1], que se conoce como 1As1 más 1, luego agregue 1 a 3As2, agregue 1 a 4As3 y agregue 1 a 0As4.
    Análisis en profundidad del caché del futuro: cafeína

En este momento, algunas personas se preguntarán si la frecuencia máxima de 15 es demasiado pequeña. No importa en este algoritmo, por ejemplo, si el tamaño es igual a 100, si aumenta el tamaño * 10 globalmente, es decir 1000 veces, se dividirá globalmente por la atenuación 2. Después de la atenuación, puede seguir aumentando. Puede adaptarse mejor a la frecuencia de acceso del período de tiempo.

2.2 Rendimiento de lectura y escritura

En guava cache dijimos que sus operaciones de lectura y escritura se mezclan con el procesamiento de tiempo de vencimiento, es decir, también se pueden realizar operaciones de eliminación en una operación Put, por lo que su rendimiento de lectura y escritura se verá afectado en cierta medida, puedes ver la figura de arriba. La cafeína hizo explotar el caché de guayaba en operaciones de lectura y escritura. Principalmente porque en la cafeína, el funcionamiento de estos eventos es a través de operaciones asincrónicas. Él envía los eventos a la cola. La estructura de datos de la cola aquí es RingBuffer. Si no está seguro, puede leer este artículo. Debe conocer el alto rendimiento sin bloqueo Disruptor de cola. Luego usará el ForkJoinPool.commonPool () predeterminado, o configurará el grupo de subprocesos por sí mismo, realizará la operación de búsqueda de la cola y luego realizará las siguientes operaciones de eliminación y expiración.

Por supuesto, existen diferentes colas de lectura y escritura, en cafeína se considera que hay muchas más lecturas de caché que escrituras, por lo que para las operaciones de escritura todos los hilos comparten un Ringbuffer.

Análisis en profundidad del caché del futuro: cafeína

Para operaciones de lectura con más frecuencia que operaciones de escritura, para reducir aún más la competencia, está equipado con un RingBuffer para cada hilo:
Análisis en profundidad del caché del futuro: cafeína

2.3 Estrategia de eliminación de datos

  • Todos los datos de la cafeína están en ConcurrentHashMap, que es diferente del caché de guayaba, que implementa una estructura similar a ConcurrentHashMap por sí mismo. Hay tres colas LRU a las que hacen referencia los registros de cafeína:

  • Cola de Eden: se especifica en cafeína que solo puede ser% 1 de la capacidad de la caché. Si tamaño = 100, el tamaño efectivo de esta cola es igual a 1. Lo que se registra en esta cola son los datos recién llegados, lo que evita que se elimine el tráfico en ráfagas debido a que antes no había frecuencia de acceso. Por ejemplo, si se lanza un nuevo drama, no tiene frecuencia de acceso al principio, para evitar que otras cachés lo eliminen después de su lanzamiento y se una a esta área. La zona de Edén, la zona más cómoda y confortable, es difícil de eliminar por otros datos aquí.

  • Cola de prueba: se llama cola de prueba. En esta cola, sus datos están relativamente fríos y se eliminarán pronto. El tamaño efectivo es el tamaño menos edén menos protegido.

Cola protegida: En esta cola, puede estar seguro de que no será eliminado por el momento, pero no se preocupe, si no hay datos en la cola de Libertad Condicional o los datos protegidos están llenos, también se enfrentará a la vergonzosa situación de eliminación. Por supuesto, si desea convertirse en esta cola, debe visitar el período de prueba una vez y se promoverá a la cola protegida. El tamaño efectivo es (tamaño menos eden) X 80%. Si tamaño = 100, será 79.

Las tres colas están relacionadas de la siguiente manera:
Análisis en profundidad del caché del futuro: cafeína

  1. Todos los datos nuevos irán a Eden.
    • El Edén está lleno, eliminado al período de prueba.
    • Si se accede a uno de los datos en período de prueba, estos datos se actualizan a Protegido.
    • Si Protegido está completo, continuará siendo degradado a Probatoria.

Cuando se produzca la eliminación de datos, se eliminarán del período de prueba. El líder del equipo de datos en esta cola se llamará la víctima. Este líder del equipo debe ser el primero en ingresar. Según el algoritmo de la cola LRU, en realidad debería ser eliminado, pero aquí solo se puede llamar la víctima. Esta cola Era una cola de libertad condicional y el representante estaba a punto de ejecutarlo. Aquí, el final del equipo será eliminado y llamado candidatos, también llamados *** ers. Aquí, las víctimas y la *** ciudad imperial PK deciden que debemos ser eliminados.
Análisis en profundidad del caché del futuro: cafeína
Hay varios juicios basados ​​en los datos de frecuencia registrados en nuestro Count-Min Sketch:

  • Si el asesino es mayor que la víctima, la víctima es directamente eliminada.
  • Si la persona *** <= 5, entonces la persona *** se elimina directamente. Esta lógica se explica en sus notas:
    Análisis en profundidad del caché del futuro: cafeína
    cree que establecer un umbral de calentamiento aumentará la tasa de aciertos general.
  • En otros casos, se eliminará de forma aleatoria.

    3. Análisis de la función de la cafeína

Hay muchas funciones en Caffeine. Analicemos cómo funcionan estas API.

3.1 Fábrica de caché de flores de cien flores

Hay una clase LocalCacheFactory en Caffeine, que creará una caché específica de acuerdo con su configuración.
Análisis en profundidad del caché del futuro: cafeína
Se puede ver que ensamblará la cadena de acuerdo con si ha configurado el tiempo de vencimiento, eliminará el oyente y otros parámetros, y finalmente generará una caché específica basada en la cadena. Hay demasiadas cachés aquí, y el código fuente del autor no está escrito directamente. Esta parte del código se genera a través de Java Poet:
Análisis en profundidad del caché del futuro: cafeína

3.2 estrategia de expiración fugaz

Hay dos tipos de cachés en Caffeine, uno es caché limitado, el otro es *** caché, *** caché no necesita caducar y no tiene límites. Se proporcionan tres API de vencimiento en la caché limitada:

  • expireAfterWrite: representa cuánto tiempo caduca después de la escritura.
  • expireAfterAccess: representa cuánto tiempo caducará después de la última visita.
  • expireAfter: En expireAfter, debe implementar la interfaz de Caducidad usted mismo. Esta interfaz admite la creación, actualización y cuánto tiempo caducará después del acceso. Tenga en cuenta que esta API y las dos API anteriores son mutuamente excluyentes. La diferencia entre esta y las dos API anteriores es que debe indicarle al marco de almacenamiento en caché que debe caducar en un momento específico, es decir, obtener el tiempo de vencimiento específico reescribiendo los métodos de creación, actualización y acceso anteriores.

Hay un método scheduleDrainBuffers en Caffeine, que se utiliza para programar nuestras tareas caducadas, que se llamarán después de que leamos y escribamos:

Análisis en profundidad del caché del futuro: cafeína

Primero, bloqueará, si el bloqueo falla, significa que alguien ya está realizando la programación. Él usará el grupo de subprocesos predeterminado ForkJoinPool o un grupo de subprocesos personalizado. El DrainBuffersTask aquí es en realidad el PerformCleanupTask en Caffeine.
Análisis en profundidad del caché del futuro: cafeína
Análisis en profundidad del caché del futuro: cafeína

Vuelva a bloquear el método performCleanUp para evitar que otros hilos realicen operaciones de limpieza. Luego ingresamos al método de mantenimiento:
Análisis en profundidad del caché del futuro: cafeína

Puede ver que hay muchos métodos en él, y otros métodos se discutirán más adelante. Aquí nos enfocamos en expireEntries (), que es el método utilizado para expirar:
Análisis en profundidad del caché del futuro: cafeína

  • Primero obtenga la hora actual.
  • El segundo paso es expirar expireAfterAccess:
    Análisis en profundidad del caché del futuro: cafeína
    Análisis en profundidad del caché del futuro: cafeína
    aquí, de acuerdo con nuestra configuración, el método evicts () es verdadero, por lo que las tres colas se eliminarán del vencimiento. Como se mencionó anteriormente, estas tres colas son todas colas LRU, por lo que nuestro expireAfterAccessEntries Método, solo necesita juzgar el nodo principal de cada cola si el acceso caduca y luego eliminarlo.

  • El tercer paso es expireAfterWrite:
    Análisis en profundidad del caché del futuro: cafeína
    puede ver que aquí se confía en una cola writeQrderDeque. ¿Cuándo se llenan los datos de esta cola? Por supuesto, también es asincrónico. El método específico está en nuestro draninWriteBuffer arriba. Ejecutará la Tarea que pusimos en el RingBuffer antes, incluyendo la adición de writeQrderDeque. La estrategia de vencimiento es muy simple, simplemente repita el primero para determinar si vence.

  • El cuarto paso es expirar las expireVariableEntries:
    Análisis en profundidad del caché del futuro: cafeína
    En el método anterior, podemos ver que la rueda de tiempo se usa para el procesamiento de expiración ¿Qué es la rueda de tiempo? Es de suponer que están familiarizados con algunos sistemas de tareas de temporización, ya que es una estructura eficiente para procesar tareas de temporización, que puede considerarse simplemente como una matriz multidimensional. En Caffeine, es una rueda de tiempo de dos capas, que es una matriz bidimensional. Sus datos unidimensionales representan una dimensión de tiempo mayor, como segundos, minutos, horas, días, etc., y sus datos bidimensionales representan la dimensión de tiempo más pequeña. Dimensión de tiempo, como un cierto intervalo en segundos. Cuando se encuentra un TimeWhile [i] [j], su estructura de datos es en realidad una lista enlazada, que registra nuestro Node. En Caffeine, usamos la rueda del tiempo para registrar nuestros datos caducados en un momento determinado y luego procesarlos.
    Análisis en profundidad del caché del futuro: cafeína

La rueda del tiempo en la cafeína se muestra arriba. Cuando insertamos los datos, calculamos el tiempo de caducidad de acuerdo con nuestro método reescrito. Por ejemplo, debería caducar en 1536046571142. El último tiempo de caducidad de procesamiento fue 1536046571100. Restarlo para obtener 42ms y luego ponerlo en La rueda de tiempo, debido a que es menor que 1.07s, se coloca directamente en la posición de 1.07s, y una cierta posición de la segunda capa (necesita ser calculada por un cierto algoritmo), y se inserta en la lista vinculada usando la interpolación de cola.

Al procesar el tiempo de vencimiento, se calculará la diferencia entre el último tiempo de procesamiento y el tiempo de procesamiento actual, y todas las rondas de tiempo dentro de este rango de tiempo deben procesarse. Si un Nodo no vence realmente, entonces debe Vuelva a insertarlo en la rueda del tiempo.

3.3. Además de la estrategia de actualización antigua y nueva

La cafeína proporciona el método refreshAfterWrite () para permitirnos actualizar la estrategia después de escribir:
Análisis en profundidad del caché del futuro: cafeína

En el código anterior, necesitamos crear un CacheLodaer para actualizar, aquí se hace de forma sincrónica y se puede construir de forma asincrónica a través del método buildAsync. En el negocio real, podemos pasar el asignador en nuestro código para actualizar la fuente de datos.

Tenga en cuenta que la actualización aquí no se actualiza cuando caduca, sino solo después de que se vuelve a acceder a los datos. Por ejemplo: hay un dato con la clave: 'café', valor: 'latte', lo configuramos para que se actualice durante 1 segundo, después de agregar los datos, esperamos 1 minuto y es lógico que se actualice y obtenga el nuevo valor la próxima vez que visitemos Lamentablemente no, todavía regresé a 'latte' cuando visité. Pero si continúa visitando, encontrará que ya se ha renovado.

Echemos un vistazo a cómo actualizarlo automáticamente. La actualización automática solo existe después de la operación de lectura, es decir, nuestro método afterRead (), uno de los cuales se llama refreshIfNeeded, se actualizará según sea síncrono o asíncrono.

3.4 Referencia blanda de realidad virtual y referencia débil

Hay cuatro tipos de referencias en Java: referencias fuertes (StrongReference), referencias suaves (SoftReference), referencias débiles (WeakReference) y referencias fantasmas (PhantomReference).

  • Referencia sólida: declarar un objeto directamente en nuestro código es una referencia sólida.
  • Referencias blandas: si un objeto solo tiene referencias blandas, si el espacio de memoria es suficiente, el recolector de basura no lo reclamará; si el espacio de memoria es insuficiente, se reclamará la memoria de estos objetos. Mientras el recolector de basura no lo recolecte, el programa puede usar el objeto. Una referencia suave se puede utilizar junto con una cola de referencia (ReferenceQueue). Si el objeto al que hace referencia la referencia suave es reciclado por el recolector de basura, la máquina virtual Java agregará la referencia suave a la cola de referencia asociada a ella.
  • Referencia débil: cuando el hilo del recolector de basura escanea el área de memoria bajo su jurisdicción, una vez que se encuentra un objeto con solo referencias débiles, su memoria se recuperará independientemente de si el espacio de memoria actual es suficiente. Una referencia débil se puede usar junto con una cola de referencia (ReferenceQueue). Si el objeto al que hace referencia la referencia débil es recolectado como basura, la máquina virtual Java agregará la referencia débil a la cola de referencia asociada con ella.
  • Referencia fantasma: si un objeto solo contiene una referencia fantasma, entonces es lo mismo que sin ninguna referencia, y el recolector de basura puede recolectarlo en cualquier momento. Las referencias fantasmas deben usarse junto con las colas de referencia (ReferenceQueue). Cuando el recolector de basura está a punto de reclamar un objeto, si encuentra que tiene una referencia fantasma, agregará la referencia fantasma a la cola de referencia asociada con él antes de reclamar la memoria del objeto.

    3.4.1 Estrategia de eliminación de referencias débiles

La cafeína apoya la eliminación de las referencias débiles. Hay dos API: débilesKeys () y débilesValues ​​(), que se utilizan para establecer si la clave es una referencia débil o el valor es una referencia débil. Específicamente principio se pone en la clave y el valor cuando se utiliza referencia phantom a los envases y la cola de referencia límite:
Análisis en profundidad del caché del futuro: cafeína
.

Al reciclar, hay dos métodos en el método de mantenimiento que presentamos anteriormente:

//处理key引用的
drainKeyReferences
();
//处理value引用
drainValueReferences
();

El código de procesamiento específico es:
Análisis en profundidad del caché del futuro: cafeína
debido a que nuestra clave ha sido reciclada, ingresará a la cola de referencia y, a través de esta cola de referencia, aparecerá hasta que esté vacía. Podemos obtener Node según la aplicación en esta cola y luego expulsarlo.

Nota: Muchos estudiantes piensan que la forma de Valor-clave almacenado en la caché en realidad se almacena en forma de Nodo de referencia clave (El nodo contiene Valor).

3.4.2 La estrategia de eliminación de referencias blandas

La cafeína también admite la estrategia de eliminación de referencias suaves. Su api es softValues ​​(). Las referencias suaves solo admiten Value pero no Key. Podemos ver eso en la estrategia de reciclaje de Value: es
Análisis en profundidad del caché del futuro: cafeína
similar al reciclaje de referencias clave, pero debe tenerse en cuenta que la cola de referencia aquí puede ser una cola de referencia suave o una cola de referencia débil.

3.5 Conócete a ti mismo y al enemigo: haz un seguimiento

Algunas estrategias de supervisión de la gestión se proporcionan en Caffeine, que puede activar con recordStats () Api. El valor predeterminado es usar la cafeína incorporada, o puede implementarla usted mismo. En la interfaz de StatsCounter, se definen los métodos que deben ser puntuados. Actualmente, existen los siguientes:

  • recordHits: grabar hits de caché
  • recordMisses: error de caché de registros
  • recordLoadSuccess: Registro cargado correctamente (se refiere a CacheLoader cargado correctamente)
  • recordLoadFailure: Error al cargar el registro
  • recordEviction: registro de datos de eliminación

A través del monitoreo anterior, podemos monitorear el estado actual del caché en tiempo real para evaluar el estado del caché y la tasa de aciertos del caché, etc., para facilitar el ajuste posterior de los parámetros.

3.6 Monitoreo de inicio y finalización-eliminación

Hay muchas ocasiones en las que necesitamos saber por qué se elimina el caché de cafeína, para poder realizar algunas optimizaciones. En este momento necesitamos un oyente, el código es el siguiente:

Cache
<
String
,

String
>
 cache 
=

Caffeine
.
newBuilder
()

.
removalListener
(((
key
,
 value
,
 cause
)

->

{

System
.
out
.
println
(
cause
);

}))

.
build
();

Hay muchas razones para ser eliminado en cafeína:

  • EXPLÍCITO: El motivo es que el usuario hizo que se eliminara llamando al método remove.
  • REEMPLAZADO: Al actualizar, equivale a borrar el valor anterior.
  • RECOPILADO: Para nuestro recolector de basura, es decir, las referencias suaves y las referencias débiles que redujimos anteriormente.
  • EXPIRED: Vencido y eliminado.
  • TAMAÑO: Se elimina el tamaño, cuando supere el máximo se eliminará.

Cuando nos eliminen, volveremos a llamar. Podemos imprimir el registro y monitorear la eliminación de datos en tiempo real.

4. Finalmente

Este artículo presenta todos los principios funcionales de la cafeína, entre los que se encuentran los puntos de conocimiento: LFU, LRU, rueda de tiempo, cuatro referencias de Java, etc. No importa si no está interesado en la cafeína, creo que ha ganado mucho con la introducción de este conocimiento. Finalmente, la serie de almacenamiento en caché básicamente ha llegado a su fin. Si quieres saber más, puedes seguir mi cuenta oficial, agregar a mis amigos o unirte al grupo de intercambio técnico de WeChat para discutir.

Finalmente, haga un anuncio. Si cree que este artículo tiene un artículo para usted, puede seguir mi cuenta pública técnica. Recientemente, el autor ha recopilado muchos de los últimos videos de materiales de aprendizaje y materiales de entrevistas, y puede recibirlos después de prestar atención. Su atención y reenvío son El mayor apoyo para mí, O (∩_∩) O

Análisis en profundidad del caché del futuro: cafeína

Supongo que te gusta

Origin blog.51cto.com/14980978/2544817
Recomendado
Clasificación