Implementación de caché de Redis y soluciones a problemas comunes

Con el desarrollo de la tecnología de Internet, la velocidad y la eficiencia del procesamiento de datos se han convertido en indicadores importantes para medir el desempeño de un sistema. Entre muchas tecnologías de procesamiento de datos, la tecnología de almacenamiento en caché se ha convertido en una parte indispensable debido a su excelente efecto de optimización del rendimiento. Entre las muchas tecnologías de almacenamiento en caché, Redis se ha ganado el favor de la mayoría de los desarrolladores por su excelente rendimiento y sus ricas funciones.

Redis es un sistema de almacenamiento de estructura de datos en memoria de código abierto que se puede utilizar como base de datos, caché y middleware de mensajería. Redis admite múltiples tipos de estructuras de datos, como cadenas, hashes, listas, conjuntos, conjuntos ordenados, etc. Además, Redis también proporciona una serie de funciones, como persistencia de datos, transacciones, publicación y suscripción, etc.

Sin embargo, ¿cómo utilizar Redis para implementar un mecanismo de almacenamiento en caché eficiente? Eso es lo que vamos a explorar hoy. En este artículo, presentaremos en detalle la implementación de caché de Redis, incluidas sus estrategias de lectura y escritura, estrategias de caducidad y estrategias de eliminación. Esperamos que a través de este artículo podamos ayudar a los lectores a comprender y utilizar mejor Redis, mejorando así el rendimiento de su sistema.



1. Implementación de caché de Redis y estrategia de caché

1.1 Aplicación de caché de Redis

La caché de Redis es un escenario de aplicación importante de Redis. Al almacenar los datos del punto de acceso en la memoria, la velocidad de lectura de la aplicación se puede mejorar enormemente, mejorando así el rendimiento de la aplicación.

imagen-20230913200928903

Cuando se usa Redis como caché, generalmente se establece un tiempo de vencimiento. Cuando los datos caducan, Redis los eliminará automáticamente para liberar espacio en la memoria. Al mismo tiempo, para evitar avalanchas de caché, el tiempo de caducidad suele ser aleatorio.

Además, Redis también proporciona estructuras de datos enriquecidas, como cadenas, listas, conjuntos, tablas hash, etc., para satisfacer diversos requisitos complejos de almacenamiento en caché. Por ejemplo, puede utilizar tablas hash para almacenar objetos, listas para implementar el algoritmo utilizado menos recientemente (LRU), etc.

1.2 Clasificación de la estrategia de caché de Redis

La estrategia de caché se refiere a una serie de reglas y métodos sobre cómo seleccionar y administrar datos en el caché cuando se usa el caché. El objetivo de la estrategia de almacenamiento en caché es aumentar la velocidad de acceso a los datos tanto como sea posible y reducir el acceso a la fuente de datos original (como una base de datos), mejorando así el rendimiento del sistema.

Las estrategias de almacenamiento en caché incluyen principalmente los siguientes aspectos: estrategia de lectura, estrategia de escritura, estrategia de carga, estrategia de vencimiento y estrategia de eliminación.

1.3 Estrategias de lectura comunes para Redis

Estrategias comunes de lectura de Redis:

  1. Lectura completa (estrategia de lectura-carga bajo demanda): al leer datos, si se descubre que no están en el caché, se leerán de la base de datos y los datos se colocarán en el caché después de la lectura. Esta estrategia puede garantizar que los datos en la memoria caché sean datos activos, pero puede provocar una mayor latencia al leer datos por primera vez;
  2. Carga diferida (estrategia de lectura - carga bajo demanda): los datos se cargan en la caché solo cuando se solicitan. Si los datos faltan en el caché, se leen de la base de datos y se agregan al caché;
  3. Precarga (estrategia de lectura - precarga): el sistema precargará los datos que puedan ser necesarios en el caché al inicio o en un momento específico. De esta forma, cuando se solicitan datos, se pueden obtener directamente del caché sin consultar la base de datos, mejorando así la velocidad de acceso a los datos. La estrategia de precarga es particularmente adecuada para escenarios donde el patrón de acceso a los datos es relativamente fijo y la frecuencia de actualización de los datos no es alta. Por ejemplo, cierta información de configuración, contenido estático, etc. son muy adecuados para utilizar la estrategia de precarga.

En el uso real, puede elegir una estrategia de lectura adecuada según los escenarios y necesidades de aplicación específicos. Por ejemplo, si la frecuencia de actualización de datos es baja y los requisitos de rendimiento de la operación de lectura son altos, puede optar por utilizar la estrategia de lectura directa; si la frecuencia de actualización de datos es alta o desea ahorrar espacio en caché, puede optar por Utilice la estrategia de carga diferida.

1.4 Estrategias de escritura comunes para Redis

Estrategias de escritura comunes para Redis:

  1. Escritura directa (estrategia de escritura-actualización sincrónica): cada vez que se actualizan los datos, la base de datos y el caché se actualizarán al mismo tiempo. La ventaja de esta estrategia es que puede mantener la coherencia de los datos, pero la desventaja es que afecta el rendimiento porque cada actualización requiere la operación simultánea de la base de datos y el caché.
  2. Escritura regresiva (estrategia de escritura-actualización asincrónica): cada vez que se actualizan los datos, primero se actualiza el caché y luego la base de datos se actualiza de forma asincrónica. La ventaja de esta estrategia es que no afecta el alto rendimiento del caché y puede responder rápidamente al cliente, pero la desventaja es que los datos del caché y la base de datos son temporalmente inconsistentes antes de que los datos se vuelvan a escribir de forma asincrónica en la base de datos.
  3. Write Around (estrategia de escritura-actualización directa): al actualizar datos, actualice directamente la base de datos y no actualice el caché. La próxima vez que se lean los datos, si no están en el caché, se leerán desde la base de datos. Esta estrategia es adecuada para datos que rara vez se leen después de escribirse.
1.5 Estrategias de vencimiento comunes de Redis

La política de vencimiento de Redis se implementa principalmente configurando TTL (Time To Live). Para cada clave con un tiempo de vencimiento establecido, Redis elimina automáticamente la clave cuando llega a su tiempo de vencimiento. Redis utiliza dos estrategias: eliminación diferida y eliminación periódica para lidiar con claves caducadas:

  1. Eliminación diferida: solo cuando se accede a una clave, Redis comprobará si la clave ha caducado y la eliminará si caduca. La ventaja de esta estrategia es que puede reducir el uso de la CPU y evitar una gran cantidad de operaciones de eliminación en el momento en que caducan las claves, lo que afectará el rendimiento de Redis;

imagen-20230913200158436

  1. Eliminación periódica: es decir, Redis comprobará aleatoriamente algunas claves a intervalos regulares y, si se descubre que una clave ha caducado, se eliminará. Esta estrategia puede limpiar eficazmente las claves caducadas y liberar espacio en la memoria.

    Sin embargo, dado que Redis no puede sondear todas las claves, es posible que algunas claves caduquen y no se eliminen inmediatamente. Es por eso que Redis también necesita usar una estrategia de eliminación diferida, es decir, solo cuando se accede a una clave, Redis verificará si la clave ha caducado y la eliminará si caduca.

La combinación de estas dos estrategias puede administrar eficazmente las claves caducadas al tiempo que garantiza el rendimiento de Redis y evita que las claves caducadas ocupen memoria durante mucho tiempo.

1.6 Estrategia de eliminación de Redis

Entonces, ¿qué debo hacer si la clave caducada no se elimina de forma regular o lenta? Aquí es cuando entra en juego la estrategia de eliminación de memoria de Redis.

Cuando el uso de memoria de Redis alcanza el límite superior establecido, si es necesario almacenar nuevos datos, se debe adoptar una estrategia de eliminación para eliminar algunos datos antiguos y liberar espacio en la memoria. Este es el llamado mecanismo de eliminación de memoria.

Redis proporciona una variedad de estrategias de eliminación, que se pueden maxmemory-policyconfigurar mediante elementos de configuración, que incluyen:

  1. no desalojo: cuando la memoria no es suficiente para acomodar los datos recién escritos, la nueva operación de escritura informará un error;
  2. allkeys-lru: selecciona los datos utilizados menos recientemente del conjunto de datos y elimínalos;
  3. volatile-lru: seleccione los datos utilizados menos recientemente del conjunto de datos con un tiempo de vencimiento y elimínelos;

imagen-20230913194418121

  1. allkeys-random: elimina una clave aleatoriamente;
  2. volátil-aleatorio: selecciona arbitrariamente los datos para eliminarlos del conjunto de datos con el tiempo de vencimiento establecido;
  3. volatile-ttl: selecciona y elimina los datos que están a punto de caducar del conjunto de datos con un tiempo de caducidad establecido.

Las estrategias anteriores se pueden seleccionar en función de las necesidades y escenarios de la aplicación real.


2. Problemas comunes y soluciones para la caché de Redis

2.1 Problema con las teclas de acceso rápido de Redis

El llamado problema de las teclas de acceso rápido es que una o varias claves son accedidas por una gran cantidad de solicitudes simultáneas, lo que puede hacer que el tráfico esté demasiado concentrado y alcance el límite superior de la tarjeta de red física, lo que provocará que el servidor Redis falle y desencadenar una avalancha.

Soluciones para problemas de teclas de acceso rápido:

  1. Distribuya las teclas de acceso rápido a diferentes servidores con anticipación: este método, también conocido como fragmentación, puede dispersar los datos de las teclas de acceso rápido a múltiples servidores Redis, lo que reduce la presión de acceso en un solo servidor;
  2. Caché de segundo nivel: mantiene un caché local dentro del servidor de aplicaciones. Cuando Redis está inactivo, los datos se pueden obtener del caché local. Este método puede mejorar la disponibilidad del sistema, pero cabe señalar que puede haber problemas de coherencia de datos entre la caché local y Redis.

Además, también puede considerar el uso de algunos métodos de control de tráfico, como limitación de corriente, disyuntor, etc., para evitar que una gran cantidad de solicitudes accedan a las teclas de acceso rápido al mismo tiempo, evitando así el tiempo de inactividad del servidor.

2.2 Penetración de caché de Redis

La penetración de la caché se refiere a la consulta de datos que no existen en la caché o la base de datos. Cada solicitud llegará a la base de datos, lo que provocará una presión excesiva sobre la base de datos.

Una solución válida es:

  1. Verificación de la interfaz: verifique los parámetros solicitados. Las solicitudes ilegales devuelven errores directamente y evitan que lleguen a la base de datos.
  2. Valores nulos en caché: incluso si no se consultan datos en la base de datos, los valores nulos se escribirán en el caché, de modo que la próxima vez que se consulten los mismos datos, los valores nulos se obtendrán directamente del caché sin accediendo nuevamente a la base de datos.
  3. Filtro Bloom: el filtro Bloom es una estructura de datos probabilística que se puede utilizar para determinar si un elemento está en un conjunto. Podemos almacenar las claves de todos los datos posibles en el filtro Bloom. Al consultar los datos, primero determine si la clave está en el filtro Bloom. Si no, devuelva directamente que no existe. Si es así, vaya al caché y base de datos Consultar.

Las características principales de Bloom Filter son las siguientes:

  1. Determinar que un elemento no existe: Si el filtro Bloom determina que un elemento no existe, entonces este elemento no debe existir.
  2. Determinación de la existencia: si el filtro Bloom determina que un elemento existe, este elemento puede existir o no, y existe una cierta tasa de error de juicio. Esta tasa de falsos positivos se puede controlar ajustando los parámetros del filtro Bloom.

El filtro Bloom consta de una matriz de bits (BitSet) y un conjunto de funciones hash. Es un algoritmo probabilístico y una estructura de datos altamente eficientes en el espacio. Se utiliza principalmente para determinar si un elemento existe en un conjunto.

En comparación con HashMap, el filtro Bloom tiene ventajas obvias al procesar grandes cantidades de datos. Cuando la cantidad de datos es pequeña, HashMap puede manejar bien el problema y no hay una tasa de falsos positivos. Sin embargo, cuando la cantidad de datos aumenta, especialmente cuando la clave a almacenar ocupa más espacio, la ventaja de espacio del filtro Bloom comenzará a desaparecer.

Estos métodos pueden prevenir eficazmente problemas de penetración de caché y proteger la base de datos para que no se vea abrumada por una gran cantidad de solicitudes no válidas.

2.3 Desglose de la caché de Redis

El desglose de la caché se refiere al momento en que los datos activos caducan en la caché y una gran cantidad de solicitudes llegan directamente a la base de datos, lo que puede causar un aumento repentino en la presión de la base de datos o incluso colapsar.

Una solución válida es:

  1. Agregue un bloqueo mutex: mientras la primera solicitud consulta la base de datos y actualiza el caché, otras solicitudes esperan. Esto garantiza que solo una solicitud accederá a la base de datos y evita una presión excesiva sobre la base de datos.
  2. Los datos del punto de acceso no caducan: configure los datos del punto de acceso para que nunca caduquen y luego actualice los datos de forma asíncrona mediante tareas programadas. Este método puede evitar problemas de avería de la caché causados ​​por la caducidad repentina de los datos del punto de acceso, pero debe tenerse en cuenta que esto puede provocar que los datos sean inconsistentes durante un período de tiempo y debe decidir si es aceptable según las necesidades comerciales.

Estos métodos pueden prevenir eficazmente problemas de penetración de caché y proteger la base de datos para que no se vea abrumada por una gran cantidad de solicitudes.

2.4 Avalancha de caché de Redis

La avalancha de caché significa que una gran cantidad de datos activos caducan en el mismo momento, lo que provoca que una gran cantidad de solicitudes lleguen directamente a la base de datos, lo que puede provocar un aumento repentino en la presión de la base de datos o incluso un colapso.

Una solución válida es:

  1. Distribuya el tiempo de vencimiento: agregue un valor aleatorio al tiempo de vencimiento de cada clave para distribuir el tiempo de vencimiento de cada clave y evitar que una gran cantidad de claves caduquen al mismo tiempo.
  2. Agregue un bloqueo mutex: para la misma clave, solo se permite una solicitud para consultar la base de datos y actualizar el caché, mientras que otras solicitudes esperan. Esto garantiza que solo una solicitud accederá a la base de datos y evita una presión excesiva sobre la base de datos.
  3. Los datos del punto de acceso no caducan: configure los datos del punto de acceso para que nunca caduquen y luego actualice los datos de forma asíncrona mediante tareas programadas. Este método puede evitar el problema de la avalancha de caché causado por la caducidad repentina de los datos del punto de acceso, pero debe tenerse en cuenta que esto puede hacer que los datos sean inconsistentes durante un período de tiempo y debe decidir si es aceptable según las necesidades comerciales.

Estos métodos pueden prevenir eficazmente problemas de avalancha de caché y proteger la base de datos para que no se vea abrumada por una gran cantidad de solicitudes.


3. Implementación de caché de Redis en Java

3.1 Implementación Jedi

El siguiente es un ejemplo sencillo de implementación de las estrategias de lectura y escritura mediante Java:

import redis.clients.jedis.Jedis;

public class Cache {
    
    
    private Jedis jedis;
    private Database db;

    public Cache() {
    
    
        this.jedis = new Jedis("localhost", 6379);
        this.db = new Database();
    }

    // Read Through策略
    public String readThrough(String key) {
    
    
        // 先从缓存中读取数据
        String value = jedis.get(key);
        if (value == null) {
    
    
            // 如果缓存中没有,那么从数据库中读取
            value = db.getFromDatabase(key);
            // 将从数据库中读取的数据放入缓存
            jedis.set(key, value);
        }
        return value;
    }

    // Write Through策略
    public void writeThrough(String key, String value) {
    
    
        // 先将数据写入数据库
        db.writeToDatabase(key, value);
        // 然后将数据写入缓存
        jedis.set(key, value);
    }
}

class Database {
    
    
    // 这里假设我们有一个数据库,具体实现省略
    public String getFromDatabase(String key) {
    
    
        // 从数据库中获取数据的代码
        return "data";
    }

    public void writeToDatabase(String key, String value) {
    
    
        // 将数据写入数据库的代码
    }
}

En este ejemplo, primero creamos una Cacheclase que se conecta al servidor Redis en el constructor e inicializa un objeto de base de datos.

Luego, definimos dos métodos: readThroughy writeThrough, que implementan las estrategias de lectura y escritura respectivamente.

  • readThroughEl método primero intenta leer los datos del caché, si no están en el caché, luego lee de la base de datos y coloca los datos leídos de la base de datos en el caché.
  • writeThroughEl método primero escribe los datos en la base de datos y luego los escribe en el caché.

DatabaseLa clase es una clase de base de datos hipotética; se omite la implementación específica.

3.2 Implementación de SpringBoot

En Spring Boot, también podemos utilizarlo springframework.data.redispara implementar las estrategias de lectura y escritura. Aquí hay un ejemplo simple:

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class DataService {
    
    

    private final StringRedisTemplate redisTemplate;

    public DataService(StringRedisTemplate redisTemplate) {
    
    
        this.redisTemplate = redisTemplate;
    }

    public String readThrough(String key) {
    
    
        // 先从缓存中读取数据
        String value = redisTemplate.opsForValue().get(key);
        if (value == null) {
    
    
            // 如果缓存中没有,那么从数据库中读取
            value = getFromDatabase(key);
            // 将从数据库中读取的数据放入缓存
            redisTemplate.opsForValue().set(key, value);
        }
        return value;
    }

    public void writeThrough(String key, String value) {
    
    
        // 先将数据写入数据库
        writeToDatabase(key, value);
        // 然后将数据写入缓存
        redisTemplate.opsForValue().set(key, value);
    }

    private String getFromDatabase(String key) {
    
    
        // 从数据库中获取数据的代码
        return "data";
    }

    private void writeToDatabase(String key, String value) {
    
    
        // 将数据写入数据库的代码
    }
}

En este ejemplo, primero creamos una DataServiceclase administrada por Spring.

Luego, definimos dos métodos: readThrough y writeThrough, que implementan las estrategias de lectura y escritura respectivamente.

  • readThroughEl método primero intenta leer los datos del caché, si no están en el caché, luego lee de la base de datos y coloca los datos leídos de la base de datos en el caché.
  • writeThroughEl método primero escribe los datos en la base de datos y luego los escribe en el caché.

getFromDatabasey writeToDatabaselos métodos son códigos para obtener datos de la base de datos y escribir datos en la base de datos, y se omite la implementación específica.

Nota: En el uso real, debe configurar la información de conexión de Redis en el archivo de configuración de Spring Boot.

Supongo que te gusta

Origin blog.csdn.net/weixin_45187434/article/details/132916312
Recomendado
Clasificación