Redis в сочетании с локальным кешем

задняя часть
Redis в сочетании с локальным кешем

предисловие

  • Мы часто используем Redis в качестве кэша в нашей разработке. Размещение высокочастотных данных в Redis может повысить производительность бизнеса и снизить нагрузку на реляционные базы данных, такие как MySQL. Некоторые системы даже используют Redis для сохранения данных. Неплотная структура документов Redis очень подходит для бизнес-систем.Развитие, точный запрос, бизнес статистики данных имеет большие преимущества. Однако в системе обработки высокочастотных потоков данных нагрузка на Redis также будет велика, а накладные расходы ввода-вывода являются основной причиной затрат времени.В настоящее время, чтобы уменьшить нагрузку на чтение и писать на Redis, мы можем использовать локальный кеш.Guava предоставляет нам отличный API локального кеша, включая политики истечения срока действия и т. д., низкую сложность кодирования, лично настоятельно рекомендуется.

Пример дизайна

Кэш ленивой загрузки Redis

Данные не кэшируются, когда они вновь добавляются в MySQL, но кэшируются при точном поиске, так что запрос кэшируется, а ни один запрос не кэшируется.

блок-схема

Redis懒加载缓存.png

пример кода

// 伪代码示例 Xx代表你的的业务对象 如User Goods等等
public class XxLazyCache {

    @Autowired
    private RedisTemplate<String, Xx> redisTemplate;
    
    @Autowired
    private XxService xxService;// 你的业务service
    
    /**
     * 查询 通过查询缓存是否存在驱动缓存加载 建议在前置业务保证id对应数据是绝对存在于数据库中的
     */
    public Xx getXx(int id) {
        // 1.查询缓存里面有没有数据
        Xx xxCache = getXxFromCache(id);
        if(xxCache != null) {
            return xxCache;// 卫语句使代码更有利于阅读
        }
        // 2.查询数据库获取数据 我们假定到业务这一步,传过来的id都在数据库中有对应数据
        Xx xx = xxService.getXxById(id);
        // 3.设置缓存、这一步相当于Redis缓存懒加载,下次再查询此id,则会走缓存
        setXxFromCache(xx);
        return xx;
        }
    }
    
    /**
     * 对xx数据进行修改或者删除操作 操作数据库成功后 删除缓存
     * 删除请求 - 删除数据库数据 删除缓存
     * 修改请求 - 更新数据库数据 删除缓存 下次在查询时候就会从数据库拉取新的数据到缓存中
     */
    public void deleteXxFromCache(long id) {
        String key = "Xx:" + xx.getId();
        redisTemplate.delete(key);
    }
    
    private void setXxFromCache(Xx xx) {
        String key = "Xx:" + xx.getId();
        redisTemplate.opsForValue().set(key, xx);
    }
    
    private Xx getXxFromCache(int id) {
        // 通过缓存前缀拼装唯一主键作为缓存Key 如Xxx信息 就是Xxx:id
        String key = "Xx:" + id;
        return redisTemplate.opsForValue().get(key);
    }
    
}
// 业务类
public class XxServie {
    @Autowired
    private XxLazyCache xxLazyCache;
    // 查询数据库
    public Xx getXxById(long id) {
        // 省略实现
        return xx;
    }
    
    public void updateXx(Xx xx) {
        // 更新MySQL数据 省略
        // 删除缓存
        xxLazyCache.deleteXxFromCache(xx.getId());
    }
    
    public void deleteXx(long id) {
        // 删除MySQL数据 省略
        // 删除缓存
        xxLazyCache.deleteXxFromCache(xx.getId());
    }
}
// 实体类
@Data
public class Xx {
    // 业务主键
    private Long id;
    // ...省略
}

преимущество

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

недостаток

  • Объем данных должен быть контролируемым, что неприменимо в неограниченных бизнес-сценариях.
  • В сценарии микросервиса это не способствует приложению глобального кеша.

Суммировать

  • минимизация пространства
  • Встречайте точные сценарии запросов
  • Общий объем данных является контролируемым и рекомендуется
  • Неприменимо для сценариев микрослужб

Redis в сочетании с локальным кешем

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

блок-схема

本地缓存.png

Деловая сцена

В процессе обработки потоковых данных микросервис обрабатывает данные, загруженные несколькими устройствами, у каждого устройства есть код, и частота потоковых данных высока, в процессе отправки очереди сообщений нам необходимо сгенерировать соответствующий код для устройства. , Число самоувеличения kafka используется для деления по модулю количества разделов темы в kafka, так что, если есть 10 000 устройств, число самоувеличения составляет 0 ~ 9999. После взятия по модулю раздел отправляется, чтобы каждый раздел мог быть равномерно распределен.Распределение, мы используем самоинкрементное число redis для генерации этого самоинкрементного числа и помещаем его в хеш-структуру redis для кэширования.Один, а затем помещаем его в хеш-кеш Redis. В настоящее время самоинкрементный номер каждого устройства не изменится после его создания. Мы хотим использовать локальный кеш для оптимизации, избегать частых вызовов Redis для получения, уменьшать давление Redis, следующая ссылка является статью я писал о потреблении партиций kafka, можете зайти и посмотреть

Kafka раздел отправки и потребления бой

пример кода

/**
 * 此缓存演示如何结合redis自增数 hash 本地缓存使用进行设备自增数的生成、缓存、本地缓存
 * 本地缓存使用Guava Cache
 */
public class DeviceIncCache {

    /**
     * 本地缓存
     */
    private Cache<String, Integer> localCache = CacheBuilder.newBuilder()
        .concurrencyLevel(16) // 并发级别
        .initialCapacity(1000) // 初始容量
        .maximumSize(10000) // 缓存最大长度
        .expireAfterAccess(1, TimeUnit.HOURS) // 缓存1小时没被使用就过期
        .build();

    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;
    
    /**
     * redis自增数缓存的key
     */
    private static final String DEVICE_INC_COUNT = "device_inc_count";
    
    /**
     * redis设备编码对应自增数的hash缓存key
     */
    private static final String DEVICE_INC_VALUE = "device_inc_value";
    
    /**
     * 获取设备自增数
     */
    public int getInc(String deviceCode){
        // 1.从本地缓存获取
        Integer inc = localCache.get(deviceCode);
        if(inc != null) {
            return inc;
        }
        // 2.本地缓存未命中,从redis的hash缓存获取
        inc = (Integer)redisTemplate.opsForHash().get(DEVICE_INC_VALUE, deviceCode);
        // 3. redis的hash缓存中没有,说明是新设备,先为设备生成一个自增号
        if(inc == null) {
            inc = redisTemplate.opsForValue().increment(DEVICE_INC_COUNT).intValue;
            // 添加到redis hash缓存
            redisTemplate.opsForHash().put(DEVICE_INC_VALUE, deviceCode, inc);
        }
        // 4.添加到本地缓存
        localCache.put(deviceCode, inc);
        // 4.返回自增数
        return inc;
    }
    
}

преимущество

  • Redis обеспечивает надежность данных, а локальный кеш обеспечивает сверхвысокую производительность чтения.Сценарий, в котором микросервисы совместно используют большой кеш Redis, может эффективно снизить нагрузку на Redis.
  • В качестве локального кеша guava предоставляет богатые API-интерфейсы, политики истечения срока действия и максимальную емкость, чтобы гарантировать, что служебная память управляема, а холодные данные не будут занимать место в памяти в течение длительного времени.
  • Очистка локального кеша, вызванная перезапуском службы, не повлияет на бизнес.
  • Используются микросервисы и распределенные сценарии.В распределенных случаях каждый экземпляр службы будет кэшировать только самоувеличивающийся номер той части устройства, к которой он обращается, а объем локальной памяти оптимален.
  • В сервисе-примере самовозрастающее число удовлетворяет требованию равномерного распределения в зоне распространения, а также может соответствовать бизнес-сценарию подсчета количества обращений к устройству, убивая двух зайцев одним выстрелом.

недостаток

  • Увеличьте сложность кодирования, но не напрямую
  • Применимо только к сценариям, в которых кэшированное содержимое только увеличивается, но не изменяется.

Суммировать

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

постскриптум

Redis предоставляет множество типов данных и API-интерфейсов, которые очень подходят для разработки бизнес-систем, статистического подсчета (увеличение, уменьшение), маркировки битов (битовая карта), незакрепленных данных (хэш), первого поступления, чтения очереди ( list); Кэш гуавы Как локальный кеш, способный эффективно читать, он предоставляет нам большое количество API-интерфейсов для управления объемом данных в локальном кеше и устранения холодных данных; наше полное изучение этих функций может помогите нам быть более раскованными и гибкими в развитии бизнеса, в пространстве и времени найти баланс.