Redis 有几种缓存读写策略?

程序员鱼皮

共 7013字,需浏览 15分钟

 ·

2024-04-12 01:18

引言:在某一天面试的时候,小 x  被问到 Redis 三种缓存读写的策略,他懵了,原因是简历上明明是写着熟悉 Redis,因此面试官可以随意向任何一个方向进行开火,大家要注意从小点切入,除非自己是完全能够掌握这门技术的,本文将带你去了解三种常用的缓存读写策略的优缺点和使用场景。

题目

Redis 三种高效缓存读写策略你了解吗?

推荐解析

旁路缓存模式(Cache Aside Pattern)

旁路缓存是最常见的缓存读写模式,适用于读多写少的使用经常。服务端以数据库比如 MySQL 为主,Redis 为辅,进行存储。

写操作流程

1)先更新数据库

2)删除 Redis 中的缓存

读操作流程

1)尝试从缓存中读取数据,读取到数据就直接返回

2)缓存中读取不到,从数据库中读取数据

3)读取完毕后,将数据放入缓存

缓存不一致可能的场景(先删后更)

假如先删除缓存,再更新数据库,大概率会造成缓存不一致。线程 1 把 Redis 中 x 数据删除,此时线程 2 发现缓存中没有数据,从数据库读取,而线程以此时又把数据库中的 x 数据更新,因此线程 2 读取到的就是旧数据,造成了缓存不一致的情况。

缓存不一致发生概率小

被推荐的作法,就是上文讲过的,先更新数据库,再删除缓存。

可能不一致的场景如下

1)缓存中 X(数据) 不存在(数据库 X = 1) 2)线程 1 读取数据库,得到旧值(X = 1) 3)线程 2 更新数据库(X = 2) 4)线程 2 删除缓存 5)线程 1 将旧值写入缓存(X = 1) 6)最终 X 的值在缓存中是 1(旧值),在数据库中是 2(新值),也发生不一致。

此场景需要满足:1)缓存失效 2) 读写请求同步对一个数据进行并发操作 3)更新数据库+删除缓存的时间大于读取和写入缓存的时间,也就是说写操作时间大于度操作时间,因为缓存这块可以不计入,理论发生概率是很小的。

旁路缓存优缺点

优点

1)提高数据访问速度

2)减少主存访问

3)提高并发性

缺点

1)存在缓存数据库不一致情况

2)首次请求数据一定不在缓存(可以缓存预热结合定时任务)

3)写操作频繁会导致缓存被频繁删除,影响缓存命中率。(可以加分布式锁,保证更新数据库同时更新缓存。或者直接设置一个较短的过期时间)

旁路缓存示例代码

import java.util.HashMap;
import java.util.Map;

public class CacheAsideExample {
    // 模拟缓存
    private static Map<String, String> cache = new HashMap<>();

    // 模拟数据库或数据源
    private static Map<String, String> dataSource = new HashMap<>();

    // 从缓存中获取数据
    public static String getDataFromCache(String key) {
        return cache.get(key);
    }

    // 从数据源中获取数据
    public static String getDataFromDataSource(String key) {
        return dataSource.get(key);
    }

    // 将数据存入缓存
    public static void putDataIntoCache(String key, String value) {
        cache.put(key, value);
    }

    // 删除缓存中的数据
    public static void deleteDataFromCache(String key) {
        cache.remove(key);
    }

    // 从数据源中加载数据,并存入缓存
    public static String loadData(String key) {
        String data = getDataFromDataSource(key);
        if (data != null) {
            putDataIntoCache(key, data);
        }
        return data;
    }

    public static void main(String[] args) {
        // 设置数据源
        dataSource.put("key1""value1");
        dataSource.put("key2""value2");

        // 从缓存中获取数据,如果不存在则从数据源中加载
        String data1 = getDataFromCache("key1");
        if (data1 == null) {
            data1 = loadData("key1");
        }
        System.out.println("Data1: " + data1);

        // 从缓存中获取数据,如果不存在则从数据源中加载
        String data2 = getDataFromCache("key2");
        if (data2 == null) {
            data2 = loadData("key2");
        }
        System.out.println("Data2: " + data2);

        // 删除缓存中的数据
        deleteDataFromCache("key1");

        // 从缓存中获取数据,如果不存在则从数据源中加载
        String data3 = getDataFromCache("key1");
        if (data3 == null) {
            data3 = loadData("key1");
        }
        System.out.println("Data3: " + data3);
    }
}

读写穿透(Read/Write Through Pattern)

读写穿透策略将 Redis/Memcached 视为数据存储的主要地方,也就是说将缓存充当原本的数据库,利用 Cache 服务负责将数据读取并写入数据库(MySQL、Oracle等)。

写操作流程

1)先查询缓存,缓存不存在,更新数据库

2)缓存存在,先更新缓存,利用 Cache 服务同步更新数据库。

读操作流程

1)从缓存读取数据,读取到返回

2)缓存读取不到,从数据库加载后写入缓存并返回。

和旁路缓存相反,读写穿透,主缓存从数据库。

异步缓存写入(Write Behind Pattern)

只更新缓存,不利用 Cache 服务更新数据库,可以利用消息队列,先存放要消费的信息,然后可以异步批量的更新数据库,一般不使用,但数据库的缓冲池机制是这种策略的一个实现,

适用场景:数据经常变化,一致性要求不高(可以延时同步),比如 PV、UV、点赞量。

其他补充

鱼聪明 AI 的回答:

鱼聪明 AI 地址:https://www.yucongming.com/

1)旁路缓存(Cache Aside)

1.1)优点

  • 简单易实现,适用于小规模系统。
  • 缓存数据不会过期,不会出现缓存雪崩。

1.2)缺点

  • 数据一致性问题,需要应用程序主动更新缓存。
  • 缓存数据可能过期,导致缓存击穿。

1.3)使用场景

  • 适用于读多写少的场景。
  • 数据更新频率不高,对数据实时性要求不高的场景。

2)读写穿透(Cache Through)

2.1)优点

  • 数据一致性较好,不会出现数据不一致的情况。
  • 缓存数据不会过期,不会出现缓存雪崩。

2.2)缺点

  • 需要保证数据源的可靠性和性能。
  • 对于大规模系统,可能增加数据源的压力。

2.3)使用场景

  • 适用于数据源更新频率高,对数据实时性要求高的场景。
  • 数据源具有较好的性能和可靠性。

3)异步缓存写入(Write Behind)

3.1)优点

  • 减少对数据源的频繁写入,提高性能。
  • 可以缓解瞬时写入压力,提高系统稳定性。

3.2)缺点

  • 数据一致性可能受影响,存在一定的数据丢失风险。
  • 需要额外的机制来处理数据更新失败的情况。

3.3)使用场景

  • 适用于写入频率高,对数据实时性要求不高的场景。
  • 对数据丢失一定容忍度的场景。

在实际应用中,根据系统的特点和需求,可以选择合适的缓存读写策略来提高系统性能和稳定性。

欢迎交流

在阅读完本篇文章后,你应该对 Redis 的三种缓存读写策略有了一定了解,一般采用第一个策略进行读写,其他两种策略了解即可,在文末有三个问题将会检验本章的学习,欢迎在评论区发表意见。

1)旁路缓存策略中,如何解决缓存数据过期和缓存击穿的问题?

2)读写穿透策略中,如何确保数据源的可靠性和性能,以及如何处理数据源故障的情况?

3)在实际应用中,如何选择合适的缓存读写策略,考虑到系统的特点和需求?

点燃求职热情!每周持续更新,海量面试题等你挑战!赶紧关注面试鸭公众号,轻松备战春招和暑期实习!

浏览 12
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报