Redis7——进阶篇(三)

 前言:此篇文章系本人学习过程中记录下来的笔记,里面难免会有不少欠缺的地方,诚心期待大家多多给予指教。


基础篇:

  1. Redis(一)
  2. Redis(二)
  3. Redis(三)
  4. Redis(四)
  5. Redis(五)
  6. Redis(六)
  7. Redis(七)
  8. Redis(八)

进阶篇:

  1. Redis(九)
  2. Redis(十)

接上期内容:上期完成了RedisBigKey和MoreKey方面的学习。下面开始学习Redis缓存双写的知识,话不多说,直接发车。


一、引言

在当今高并发、大数据量的应用场景中,缓存技术已经成为提升系统性能和响应速度的关键手段。然而,在使用缓存的过程中,如何保证数据库和缓存之间的数据一致性是一个极具挑战性的问题。缓存双写作为其中一种处理方式,对于理解数据一致性至关重要。本文将探讨缓存双写的相关概念、策略以及如何保证数据库和缓存的一致性。


二、什么是缓存双写?

缓存双写是指在对数据库进行写操作(如插入、更新、删除)时,同时也对缓存进行相应的写操作。其目的是确保数据库和缓存中的数据尽可能保持一致。例如,当应用程序更新了数据库中某条用户记录的信息后,为了让后续的读操作能够从缓存中获取到最新的数据,需要同时更新缓存中对应的用户记录。

缓存双写看似简单直接,但在实际应用中,由于数据库和缓存的读写性能差异、网络延迟、并发操作等因素的影响,要实现完全的一致性并不容易。如果处理不当,可能会导致数据库和缓存中的数据不一致,进而影响到应用程序的正确性。


三、缓存策略

缓存按照操作划分为只读缓存和读写缓存,读写缓存又分为同步直写和异步缓写策略。目的:使redis缓存和数据库数据一致。

(一)、同步直写策略

1、原理

同步直写(Synchronous Write - Through)策略,也叫读写穿透策略。在写操作时,应用程序向数据库发送写请求,数据库接收到请求后,会先将数据写入。当数据库写入成功后,会同步将相同的数据写入缓存中。只有当缓存和数据源都成功写入数据后,才会向应用程序返回写入成功的响应。在读操作时,应用程序先从缓存中读取数据,如果缓存命中,直接返回数据;若缓存未命中,则从数据源中读取数据,然后将数据写入缓存并返回给应用程序。

简单来说:写数据库后也同步写redis缓存,保证缓存和数据库中的数据⼀致;读数据先从缓存读,有返回;无,则从数据库读,在回写到缓存。


2、优劣势

优:

  • 数据一致性高由于每次写操作都同时更新缓存和数据源,能最大程度保证两者的数据一致性,很大程度上避免了数据不一致的情况发生。
  • 实现相对简单该策略的逻辑较为清晰,不需要复杂的异步处理和额外的一致性检查机制,开发和维护相对容易。

劣:

  • 性能瓶颈写操作需要等待缓存和数据源都完成写入后才能返回,这会增加写操作的响应时间。在高并发写操作场景下,可能会导致系统性能下降,因为每次写操作都需要等待,可能会出现大量的请求阻塞。

(二)、异步缓写策略

1、原理

异步缓写(Asynchronous Write-Behind)策略,也叫异步写入缓存策略。在写操作时,应用程序向数据库发送写请求,数据库接收到请求后,会立即将数据写入,并迅速向应用程序返回写入成功的响应,而不会等待缓存的写入完成。然后,程序会以异步(消息队列)的方式将数据写入缓存。在读操作时,与同步直写策略类似,先从缓存中读取数据,如果缓存命中,直接返回数据;若缓存未命中,则从数据源中读取数据,然后将数据写入缓存并返回给应用程序。

简单来说:先将数据写入,在异步将数据写入缓存读数据先从缓存读,有返回;无,则从数据库读,在回写到缓存。


2、优劣势

优:

  • 高写性能:写操作只需要将数据写入数据源,不需要等待缓存写入操作,响应时间短

劣:

  • 数据一致性风险由于数据写入缓存是异步的,可能会出现缓存和底层存储数据不一致的情况。
  • 数据库承受风险大如果在数据还未写入缓存时,缓存为空,过多的请求直接绕过缓存,请求数据库,数据库可能因此宕机。

四、什么是双检加锁?

(一)、概念

双检加锁(Double-Checked Locking)策略常用于多线程环境下,确保在高并发场景下对缓存的操作是线程安全的。

双检加锁策略常用于实现分布式锁,用来保证缓存数据的一致性和高效性,比如在缓存击穿场景中,大量并发请求同时查询一个刚好过期的缓存项,此时多个请求可能会同时去查询数据库并更新缓存,使用双检加锁策略可以避免这种情况。


(二)、原理

1、首先进行第一次检查,判断缓存对象是否已经存在。如果已经存在,直接返回缓存对象,避免不必要的加锁操作,提高性能。

2、如果第一次检查发现缓存对象不存在,则进入同步代码块(加锁)。

3、在同步代码块内,进行第二次检查,再次确认缓存对象是否存在。这是因为在多线程环境下,可能有多个线程同时通过了第一次检查,进入同步代码块之前,其他线程可能已经回写了缓存对象。

4、如果第二次检查发现缓存对象仍然不存在,则从数据库获取数据并回写缓存对象。


(三)、实现

基于双检加锁策略的缓存查询方法的实现。

public String get(String key) {
        String value = redis.get(key);// 查询缓存
        if (value != null) {
            //缓存存在直接返回
            return value;
        } else {
            //缓存不存在则对方法加锁
            //假设请求量很大,缓存过期
            synchronized (this) {
                value = redis.get(key); // 在查一遍redis
                if (value == null) {
                    // 从数据库获取数据
                    value = dao.get(key);
                    // 设置过期时间并回写到缓存
                    redis.setex(key, time, value);
                }
                return value;
            }
        }
    }

五、数据库和缓存一致性更新策略

(一)、目的

目的:最终实现缓存和数据库的数据一致。


(二)、常用策略

1、先更新数据库,在更新缓存

1.1、问题①

场景模拟一:

  1. A update myslq 100   --success
  2. A update redis 100 -- error

最终结果,数据库里面和缓存redis里面数据不一致,下一次读到redis脏数据。


1.2、问题②

场景模拟二:

【正常逻辑】:先更新数据库,再更新缓存A、B两个线程发起调用

  1. A update mysql 100
  2. A update redis 100
  3. B update mysql 80
  4. B update redis 80

最终结果,mysql和redis数据一致,皆大欢喜。


【异常逻辑】:多线程环境下,A、B两个线程有快有慢,有前有后并行

  1. A update mysql 100
  2. B update mysql 80
  3. B update redis 80
  4. A update redis 100

最终结果,mysql和redis数据不一致,o(╥﹏╥)o。


2、先更新缓存,在更新数据库

出现的异常问题跟第一种策略类似。此外,业内一般把数据库作为底单数据库,保证最后解释,一切都以数据库为主。


3、先更新数据库,在删除缓存

3.1、异常问题

先更新数据库,再删除缓存场景模拟

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啥也不会的小神龙·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值